/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// HttpLog.h should generally be included first
#include "HttpLog.h"
#include "HttpBackgroundChannelParent.h"
#include "HttpChannelParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Unused.h"
#include "mozilla/net/BackgroundChannelRegistrar.h"
#include "mozilla/net/ChannelEventQueue.h"
#include "nsNetCID.h"
#include "nsQueryObject.h"
#include "nsThreadUtils.h"
using mozilla::dom::ContentParent;
using mozilla::ipc::AssertIsInMainProcess;
using mozilla::ipc::AssertIsOnBackgroundThread;
using mozilla::ipc::BackgroundParent;
using mozilla::ipc::IPCResult;
using mozilla::ipc::IsOnBackgroundThread;
namespace mozilla {
namespace net {
/*
* Helper class for continuing the AsyncOpen procedure on main thread.
*/
class ContinueAsyncOpenRunnable final :
public Runnable {
public:
ContinueAsyncOpenRunnable(HttpBackgroundChannelParent* aActor,
const uint64_t& aChannelId)
: Runnable(
"net::ContinueAsyncOpenRunnable"),
mActor(aActor),
mChannelId(aChannelId) {
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
MOZ_ASSERT(mActor);
}
NS_IMETHOD Run() override {
LOG(
(
"HttpBackgroundChannelParent::ContinueAsyncOpen [this=%p "
"channelId=%" PRIu64
"]\n",
mActor.get(), mChannelId));
AssertIsInMainProcess();
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
BackgroundChannelRegistrar::GetOrCreate();
MOZ_ASSERT(registrar);
registrar->LinkBackgroundChannel(mChannelId, mActor);
return NS_OK;
}
private:
RefPtr<HttpBackgroundChannelParent> mActor;
const uint64_t mChannelId;
};
HttpBackgroundChannelParent::HttpBackgroundChannelParent()
: mIPCOpened(
true),
mBgThreadMutex(
"HttpBackgroundChannelParent::BgThreadMutex") {
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
{
MutexAutoLock lock(mBgThreadMutex);
mBackgroundThread = NS_GetCurrentThread();
}
}
HttpBackgroundChannelParent::~HttpBackgroundChannelParent() {
MOZ_ASSERT(NS_IsMainThread() || IsOnBackgroundThread());
MOZ_ASSERT(!mIPCOpened);
}
nsresult HttpBackgroundChannelParent::Init(
const uint64_t& aChannelId) {
LOG((
"HttpBackgroundChannelParent::Init [this=%p channelId=%" PRIu64
"]\n",
this, aChannelId));
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
RefPtr<ContinueAsyncOpenRunnable> runnable =
new ContinueAsyncOpenRunnable(
this, aChannelId);
return NS_DispatchToMainThread(runnable);
}
void HttpBackgroundChannelParent::LinkToChannel(
HttpChannelParent* aChannelParent) {
LOG((
"HttpBackgroundChannelParent::LinkToChannel [this=%p channel=%p]\n",
this, aChannelParent));
AssertIsInMainProcess();
MOZ_ASSERT(NS_IsMainThread());
if (!mIPCOpened) {
return;
}
mChannelParent = aChannelParent;
}
void HttpBackgroundChannelParent::OnChannelClosed() {
LOG((
"HttpBackgroundChannelParent::OnChannelClosed [this=%p]\n",
this));
AssertIsInMainProcess();
MOZ_ASSERT(NS_IsMainThread());
if (!mIPCOpened) {
return;
}
nsresult rv;
{
MutexAutoLock lock(mBgThreadMutex);
RefPtr<HttpBackgroundChannelParent> self =
this;
rv = mBackgroundThread->Dispatch(
NS_NewRunnableFunction(
"net::HttpBackgroundChannelParent::OnChannelClosed",
[self]() {
LOG((
"HttpBackgroundChannelParent::DeleteRunnable [this=%p]\n",
self.get()));
AssertIsOnBackgroundThread();
if (!self->mIPCOpened.compareExchange(
true,
false)) {
return;
}
Unused << self->Send__delete__(self);
}),
NS_DISPATCH_NORMAL);
}
Unused << NS_WARN_IF(NS_FAILED(rv));
}
bool HttpBackgroundChannelParent::OnStartRequest(
nsHttpResponseHead&& aResponseHead,
const bool& aUseResponseHead,
const nsHttpHeaderArray& aRequestHeaders,
const HttpChannelOnStartRequestArgs& aArgs,
const nsCOMPtr<nsICacheEntry>& aAltDataSource,
TimeStamp aOnStartRequestStart) {
LOG((
"HttpBackgroundChannelParent::OnStartRequest [this=%p]\n",
this));
AssertIsInMainProcess();
if (NS_WARN_IF(!mIPCOpened)) {
return false;
}
if (!IsOnBackgroundThread()) {
MutexAutoLock lock(mBgThreadMutex);
nsresult rv = mBackgroundThread->Dispatch(
NewRunnableMethod<nsHttpResponseHead&&,
const bool,
const nsHttpHeaderArray,
const HttpChannelOnStartRequestArgs,
const nsCOMPtr<nsICacheEntry>, TimeStamp>(
"net::HttpBackgroundChannelParent::OnStartRequest",
this,
&HttpBackgroundChannelParent::OnStartRequest,
std::move(aResponseHead), aUseResponseHead, aRequestHeaders, aArgs,
aAltDataSource, aOnStartRequestStart),
NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
return NS_SUCCEEDED(rv);
}
HttpChannelAltDataStream altData;
if (aAltDataSource) {
nsAutoCString altDataType;
Unused << aAltDataSource->GetAltDataType(altDataType);
if (!altDataType.IsEmpty()) {
nsCOMPtr<nsIInputStream> inputStream;
nsresult rv = aAltDataSource->OpenAlternativeInputStream(
altDataType, getter_AddRefs(inputStream));
if (NS_SUCCEEDED(rv)) {
Unused << mozilla::ipc::SerializeIPCStream(inputStream.forget(),
altData.altDataInputStream(),
/* aAllowLazy */ true);
}
}
}
return SendOnStartRequest(std::move(aResponseHead), aUseResponseHead,
aRequestHeaders, aArgs, altData,
aOnStartRequestStart);
}
bool HttpBackgroundChannelParent::OnTransportAndData(
const nsresult& aChannelStatus,
const nsresult& aTransportStatus,
const uint64_t& aOffset,
const uint32_t& aCount,
const nsCString& aData,
TimeStamp aOnDataAvailableStart) {
LOG((
"HttpBackgroundChannelParent::OnTransportAndData [this=%p]\n",
this));
AssertIsInMainProcess();
if (NS_WARN_IF(!mIPCOpened)) {
return false;
}
if (!IsOnBackgroundThread()) {
MutexAutoLock lock(mBgThreadMutex);
nsresult rv = mBackgroundThread->Dispatch(
NewRunnableMethod<
const nsresult,
const nsresult,
const uint64_t,
const uint32_t,
const nsCString, TimeStamp>(
"net::HttpBackgroundChannelParent::OnTransportAndData",
this,
&HttpBackgroundChannelParent::OnTransportAndData, aChannelStatus,
aTransportStatus, aOffset, aCount, aData, aOnDataAvailableStart),
NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
return NS_SUCCEEDED(rv);
}
nsHttp::SendFunc<nsDependentCSubstring> sendFunc =
[self = UnsafePtr<HttpBackgroundChannelParent>(
this), aChannelStatus,
aTransportStatus,
aOnDataAvailableStart](
const nsDependentCSubstring& aData,
uint64_t aOffset, uint32_t aCount) {
return self->SendOnTransportAndData(aChannelStatus, aTransportStatus,
aOffset, aCount, aData,
false,
aOnDataAvailableStart);
};
return nsHttp::SendDataInChunks(aData, aOffset, aCount, sendFunc);
}
bool HttpBackgroundChannelParent::OnStopRequest(
const nsresult& aChannelStatus,
const ResourceTimingStructArgs& aTiming,
const nsHttpHeaderArray& aResponseTrailers,
const nsTArray<ConsoleReportCollected>& aConsoleReports,
TimeStamp aOnStopRequestStart) {
LOG(
(
"HttpBackgroundChannelParent::OnStopRequest [this=%p "
"status=%" PRIx32
"]\n",
this,
static_cast<uint32_t>(aChannelStatus)));
AssertIsInMainProcess();
if (NS_WARN_IF(!mIPCOpened)) {
return false;
}
if (!IsOnBackgroundThread()) {
MutexAutoLock lock(mBgThreadMutex);
nsresult rv = mBackgroundThread->Dispatch(
NewRunnableMethod<
const nsresult,
const ResourceTimingStructArgs,
const nsHttpHeaderArray,
const CopyableTArray<ConsoleReportCollected>,
TimeStamp>(
"net::HttpBackgroundChannelParent::OnStopRequest",
this,
&HttpBackgroundChannelParent::OnStopRequest, aChannelStatus,
aTiming, aResponseTrailers, aConsoleReports, aOnStopRequestStart),
NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
return NS_SUCCEEDED(rv);
}
// See the child code for why we do this.
TimeStamp lastActTabOpt = nsHttp::GetLastActiveTabLoadOptimizationHit();
return SendOnStopRequest(aChannelStatus, aTiming, lastActTabOpt,
aResponseTrailers, aConsoleReports,
false,
aOnStopRequestStart);
}
bool HttpBackgroundChannelParent::OnConsoleReport(
const nsTArray<ConsoleReportCollected>& aConsoleReports) {
LOG((
"HttpBackgroundChannelParent::OnConsoleReport [this=%p]",
this));
AssertIsInMainProcess();
if (NS_WARN_IF(!mIPCOpened)) {
return false;
}
if (!IsOnBackgroundThread()) {
MutexAutoLock lock(mBgThreadMutex);
nsresult rv = mBackgroundThread->Dispatch(
NewRunnableMethod<
const CopyableTArray<ConsoleReportCollected>>(
"net::HttpBackgroundChannelParent::OnConsoleReport",
this,
&HttpBackgroundChannelParent::OnConsoleReport, aConsoleReports),
NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
return NS_SUCCEEDED(rv);
}
return SendOnConsoleReport(aConsoleReports);
}
bool HttpBackgroundChannelParent::OnAfterLastPart(
const nsresult aStatus) {
LOG((
"HttpBackgroundChannelParent::OnAfterLastPart [this=%p]\n",
this));
AssertIsInMainProcess();
if (NS_WARN_IF(!mIPCOpened)) {
return false;
}
if (!IsOnBackgroundThread()) {
MutexAutoLock lock(mBgThreadMutex);
nsresult rv = mBackgroundThread->Dispatch(
NewRunnableMethod<
const nsresult>(
"net::HttpBackgroundChannelParent::OnAfterLastPart",
this,
&HttpBackgroundChannelParent::OnAfterLastPart, aStatus),
NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
return NS_SUCCEEDED(rv);
}
return SendOnAfterLastPart(aStatus);
}
bool HttpBackgroundChannelParent::OnProgress(
const int64_t aProgress,
const int64_t aProgressMax) {
LOG((
"HttpBackgroundChannelParent::OnProgress [this=%p]\n",
this));
AssertIsInMainProcess();
if (NS_WARN_IF(!mIPCOpened)) {
return false;
}
if (!IsOnBackgroundThread()) {
MutexAutoLock lock(mBgThreadMutex);
nsresult rv = mBackgroundThread->Dispatch(
NewRunnableMethod<
const int64_t,
const int64_t>(
"net::HttpBackgroundChannelParent::OnProgress",
this,
&HttpBackgroundChannelParent::OnProgress, aProgress, aProgressMax),
NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
return NS_SUCCEEDED(rv);
}
return SendOnProgress(aProgress, aProgressMax);
}
bool HttpBackgroundChannelParent::OnStatus(
const nsresult aStatus) {
LOG((
"HttpBackgroundChannelParent::OnStatus [this=%p]\n",
this));
AssertIsInMainProcess();
if (NS_WARN_IF(!mIPCOpened)) {
return false;
}
if (!IsOnBackgroundThread()) {
MutexAutoLock lock(mBgThreadMutex);
nsresult rv = mBackgroundThread->Dispatch(
NewRunnableMethod<
const nsresult>(
"net::HttpBackgroundChannelParent::OnStatus",
this,
&HttpBackgroundChannelParent::OnStatus, aStatus),
NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
return NS_SUCCEEDED(rv);
}
return SendOnStatus(aStatus);
}
bool HttpBackgroundChannelParent::OnNotifyClassificationFlags(
uint32_t aClassificationFlags,
bool aIsThirdParty) {
LOG(
(
"HttpBackgroundChannelParent::OnNotifyClassificationFlags "
"classificationFlags=%" PRIu32
", thirdparty=%d [this=%p]\n",
aClassificationFlags,
static_cast<
int>(aIsThirdParty),
this));
AssertIsInMainProcess();
if (NS_WARN_IF(!mIPCOpened)) {
return false;
}
if (!IsOnBackgroundThread()) {
MutexAutoLock lock(mBgThreadMutex);
nsresult rv = mBackgroundThread->Dispatch(
NewRunnableMethod<uint32_t,
bool>(
"net::HttpBackgroundChannelParent::OnNotifyClassificationFlags",
this, &HttpBackgroundChannelParent::OnNotifyClassificationFlags,
aClassificationFlags, aIsThirdParty),
NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
return NS_SUCCEEDED(rv);
}
return SendNotifyClassificationFlags(aClassificationFlags, aIsThirdParty);
}
bool HttpBackgroundChannelParent::OnSetClassifierMatchedInfo(
const nsACString& aList,
const nsACString& aProvider,
const nsACString& aFullHash) {
LOG((
"HttpBackgroundChannelParent::OnSetClassifierMatchedInfo [this=%p]\n",
this));
AssertIsInMainProcess();
if (NS_WARN_IF(!mIPCOpened)) {
return false;
}
if (!IsOnBackgroundThread()) {
MutexAutoLock lock(mBgThreadMutex);
nsresult rv = mBackgroundThread->Dispatch(
NewRunnableMethod<
const nsCString,
const nsCString,
const nsCString>(
"net::HttpBackgroundChannelParent::OnSetClassifierMatchedInfo",
this, &HttpBackgroundChannelParent::OnSetClassifierMatchedInfo,
aList, aProvider, aFullHash),
NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
return NS_SUCCEEDED(rv);
}
ClassifierInfo info;
info.list() = aList;
info.fullhash() = aFullHash;
info.provider() = aProvider;
return SendSetClassifierMatchedInfo(info);
}
bool HttpBackgroundChannelParent::OnSetClassifierMatchedTrackingInfo(
const nsACString& aLists,
const nsACString& aFullHashes) {
LOG(
(
"HttpBackgroundChannelParent::OnSetClassifierMatchedTrackingInfo "
"[this=%p]\n",
this));
AssertIsInMainProcess();
if (NS_WARN_IF(!mIPCOpened)) {
return false;
}
if (!IsOnBackgroundThread()) {
MutexAutoLock lock(mBgThreadMutex);
nsresult rv = mBackgroundThread->Dispatch(
NewRunnableMethod<
const nsCString,
const nsCString>(
"net::HttpBackgroundChannelParent::"
"OnSetClassifierMatchedTrackingInfo",
this,
&HttpBackgroundChannelParent::OnSetClassifierMatchedTrackingInfo,
aLists, aFullHashes),
NS_DISPATCH_NORMAL);
MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
return NS_SUCCEEDED(rv);
}
ClassifierInfo info;
info.list() = aLists;
info.fullhash() = aFullHashes;
return SendSetClassifierMatchedTrackingInfo(info);
}
nsISerialEventTarget* HttpBackgroundChannelParent::GetBackgroundTarget() {
MOZ_ASSERT(mBackgroundThread);
return mBackgroundThread.get();
}
auto HttpBackgroundChannelParent::AttachStreamFilter(
Endpoint<extensions::PStreamFilterParent>&& aParentEndpoint,
Endpoint<extensions::PStreamFilterChild>&& aChildEndpoint)
-> RefPtr<ChildEndpointPromise> {
LOG((
"HttpBackgroundChannelParent::AttachStreamFilter [this=%p]\n",
this));
MOZ_ASSERT(IsOnBackgroundThread());
if (NS_WARN_IF(!mIPCOpened) ||
!SendAttachStreamFilter(std::move(aParentEndpoint))) {
return ChildEndpointPromise::CreateAndReject(
false, __func__);
}
return ChildEndpointPromise::CreateAndResolve(std::move(aChildEndpoint),
__func__);
}
auto HttpBackgroundChannelParent::DetachStreamFilters()
-> RefPtr<GenericPromise> {
LOG((
"HttpBackgroundChannelParent::DetachStreamFilters [this=%p]\n",
this));
if (NS_WARN_IF(!mIPCOpened) || !SendDetachStreamFilters()) {
return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
}
return GenericPromise::CreateAndResolve(
true, __func__);
}
void HttpBackgroundChannelParent::ActorDestroy(ActorDestroyReason aWhy) {
LOG((
"HttpBackgroundChannelParent::ActorDestroy [this=%p]\n",
this));
AssertIsInMainProcess();
AssertIsOnBackgroundThread();
mIPCOpened =
false;
RefPtr<HttpBackgroundChannelParent> self =
this;
DebugOnly<nsresult> rv = NS_DispatchToMainThread(NS_NewRunnableFunction(
"net::HttpBackgroundChannelParent::ActorDestroy", [self]() {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<HttpChannelParent> channelParent =
std::move(self->mChannelParent);
if (channelParent) {
channelParent->OnBackgroundParentDestroyed();
}
}));
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
// namespace net
}
// namespace mozilla