499 lines
16 KiB
C++
499 lines
16 KiB
C++
/* -*- 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 "HttpBackgroundChannelChild.h"
|
|
|
|
#include "HttpChannelChild.h"
|
|
#include "mozilla/ipc/BackgroundChild.h"
|
|
#include "mozilla/ipc/Endpoint.h"
|
|
#include "mozilla/ipc/PBackgroundChild.h"
|
|
#include "mozilla/IntegerPrintfMacros.h"
|
|
#include "mozilla/net/BackgroundDataBridgeChild.h"
|
|
#include "mozilla/Unused.h"
|
|
#include "nsSocketTransportService2.h"
|
|
|
|
using mozilla::ipc::BackgroundChild;
|
|
using mozilla::ipc::IPCResult;
|
|
|
|
namespace mozilla {
|
|
namespace net {
|
|
|
|
// HttpBackgroundChannelChild
|
|
HttpBackgroundChannelChild::HttpBackgroundChannelChild() = default;
|
|
|
|
HttpBackgroundChannelChild::~HttpBackgroundChannelChild() = default;
|
|
|
|
nsresult HttpBackgroundChannelChild::Init(HttpChannelChild* aChannelChild) {
|
|
LOG(
|
|
("HttpBackgroundChannelChild::Init [this=%p httpChannel=%p "
|
|
"channelId=%" PRIu64 "]\n",
|
|
this, aChannelChild, aChannelChild->ChannelId()));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
NS_ENSURE_ARG(aChannelChild);
|
|
|
|
mChannelChild = aChannelChild;
|
|
|
|
if (NS_WARN_IF(!CreateBackgroundChannel())) {
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
// mChannelChild may be nulled already. Use aChannelChild
|
|
aChannelChild->mCreateBackgroundChannelFailed = true;
|
|
#endif
|
|
mChannelChild = nullptr;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
mFirstODASource = ODA_PENDING;
|
|
mOnStopRequestCalled = false;
|
|
return NS_OK;
|
|
}
|
|
|
|
void HttpBackgroundChannelChild::CreateDataBridge(
|
|
Endpoint<PBackgroundDataBridgeChild>&& aEndpoint) {
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
if (!mChannelChild) {
|
|
return;
|
|
}
|
|
|
|
RefPtr<BackgroundDataBridgeChild> dataBridgeChild =
|
|
new BackgroundDataBridgeChild(this);
|
|
aEndpoint.Bind(dataBridgeChild);
|
|
}
|
|
|
|
void HttpBackgroundChannelChild::OnChannelClosed() {
|
|
LOG(("HttpBackgroundChannelChild::OnChannelClosed [this=%p]\n", this));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
if (mChannelChild) {
|
|
mChannelChild->mBackgroundChildQueueFinalState =
|
|
mQueuedRunnables.IsEmpty() ? HttpChannelChild::BCKCHILD_EMPTY
|
|
: HttpChannelChild::BCKCHILD_NON_EMPTY;
|
|
}
|
|
#endif
|
|
|
|
// HttpChannelChild is not going to handle any incoming message.
|
|
mChannelChild = nullptr;
|
|
|
|
// Remove pending IPC messages as well.
|
|
mQueuedRunnables.Clear();
|
|
mConsoleReportTask = nullptr;
|
|
}
|
|
|
|
bool HttpBackgroundChannelChild::ChannelClosed() {
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
return !mChannelChild;
|
|
}
|
|
|
|
void HttpBackgroundChannelChild::OnStartRequestReceived(
|
|
Maybe<uint32_t> aMultiPartID) {
|
|
LOG(("HttpBackgroundChannelChild::OnStartRequestReceived [this=%p]\n", this));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
MOZ_ASSERT(mChannelChild);
|
|
MOZ_ASSERT(!mStartReceived || *aMultiPartID > 0);
|
|
|
|
mStartReceived = true;
|
|
|
|
nsTArray<nsCOMPtr<nsIRunnable>> runnables = std::move(mQueuedRunnables);
|
|
|
|
for (const auto& event : runnables) {
|
|
// Note: these runnables call Recv* methods on HttpBackgroundChannelChild
|
|
// but not the Process* methods on HttpChannelChild.
|
|
event->Run();
|
|
}
|
|
|
|
// Ensure no new message is enqueued.
|
|
MOZ_ASSERT(mQueuedRunnables.IsEmpty());
|
|
}
|
|
|
|
bool HttpBackgroundChannelChild::CreateBackgroundChannel() {
|
|
LOG(("HttpBackgroundChannelChild::CreateBackgroundChannel [this=%p]\n",
|
|
this));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
MOZ_ASSERT(mChannelChild);
|
|
|
|
PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
|
|
if (NS_WARN_IF(!actorChild)) {
|
|
return false;
|
|
}
|
|
|
|
const uint64_t channelId = mChannelChild->ChannelId();
|
|
if (!actorChild->SendPHttpBackgroundChannelConstructor(this, channelId)) {
|
|
return false;
|
|
}
|
|
|
|
mChannelChild->OnBackgroundChildReady(this);
|
|
return true;
|
|
}
|
|
|
|
IPCResult HttpBackgroundChannelChild::RecvOnAfterLastPart(
|
|
const nsresult& aStatus) {
|
|
LOG(("HttpBackgroundChannelChild::RecvOnAfterLastPart [this=%p]\n", this));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
if (NS_WARN_IF(!mChannelChild)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
mChannelChild->ProcessOnAfterLastPart(aStatus);
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult HttpBackgroundChannelChild::RecvOnProgress(
|
|
const int64_t& aProgress, const int64_t& aProgressMax) {
|
|
LOG(("HttpBackgroundChannelChild::RecvOnProgress [this=%p]\n", this));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
if (NS_WARN_IF(!mChannelChild)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
mChannelChild->ProcessOnProgress(aProgress, aProgressMax);
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult HttpBackgroundChannelChild::RecvOnStatus(const nsresult& aStatus) {
|
|
LOG(("HttpBackgroundChannelChild::RecvOnStatus [this=%p]\n", this));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
if (NS_WARN_IF(!mChannelChild)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
mChannelChild->ProcessOnStatus(aStatus);
|
|
return IPC_OK();
|
|
}
|
|
|
|
bool HttpBackgroundChannelChild::IsWaitingOnStartRequest() {
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
// Need to wait for OnStartRequest if it is sent by
|
|
// parent process but not received by content process.
|
|
return !mStartReceived;
|
|
}
|
|
|
|
// PHttpBackgroundChannelChild
|
|
IPCResult HttpBackgroundChannelChild::RecvOnStartRequest(
|
|
const nsHttpResponseHead& aResponseHead, const bool& aUseResponseHead,
|
|
const nsHttpHeaderArray& aRequestHeaders,
|
|
const HttpChannelOnStartRequestArgs& aArgs,
|
|
const HttpChannelAltDataStream& aAltData,
|
|
const TimeStamp& aOnStartRequestStart) {
|
|
LOG((
|
|
"HttpBackgroundChannelChild::RecvOnStartRequest [this=%p, status=%" PRIx32
|
|
"]\n",
|
|
this, static_cast<uint32_t>(aArgs.channelStatus())));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
if (NS_WARN_IF(!mChannelChild)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
mFirstODASource =
|
|
aArgs.dataFromSocketProcess() ? ODA_FROM_SOCKET : ODA_FROM_PARENT;
|
|
|
|
mChannelChild->ProcessOnStartRequest(aResponseHead, aUseResponseHead,
|
|
aRequestHeaders, aArgs, aAltData,
|
|
aOnStartRequestStart);
|
|
// Allow to queue other runnable since OnStartRequest Event already hits the
|
|
// child's mEventQ.
|
|
OnStartRequestReceived(aArgs.multiPartID());
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult HttpBackgroundChannelChild::RecvOnTransportAndData(
|
|
const nsresult& aChannelStatus, const nsresult& aTransportStatus,
|
|
const uint64_t& aOffset, const uint32_t& aCount, const nsACString& aData,
|
|
const bool& aDataFromSocketProcess,
|
|
const TimeStamp& aOnDataAvailableStart) {
|
|
RefPtr<HttpBackgroundChannelChild> self = this;
|
|
std::function<void()> callProcessOnTransportAndData =
|
|
[self, aChannelStatus, aTransportStatus, aOffset, aCount,
|
|
data = nsCString(aData), aDataFromSocketProcess,
|
|
aOnDataAvailableStart]() {
|
|
LOG(
|
|
("HttpBackgroundChannelChild::RecvOnTransportAndData [this=%p, "
|
|
"aDataFromSocketProcess=%d, mFirstODASource=%d]\n",
|
|
self.get(), aDataFromSocketProcess, self->mFirstODASource));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
if (NS_WARN_IF(!self->mChannelChild)) {
|
|
return;
|
|
}
|
|
|
|
if (((self->mFirstODASource == ODA_FROM_SOCKET) &&
|
|
!aDataFromSocketProcess) ||
|
|
((self->mFirstODASource == ODA_FROM_PARENT) &&
|
|
aDataFromSocketProcess)) {
|
|
return;
|
|
}
|
|
|
|
// The HttpTransactionChild in socket process may not know that this
|
|
// request is cancelled or failed due to the IPC delay. In this case, we
|
|
// should not forward ODA to HttpChannelChild.
|
|
nsresult channelStatus;
|
|
self->mChannelChild->GetStatus(&channelStatus);
|
|
if (NS_FAILED(channelStatus)) {
|
|
return;
|
|
}
|
|
|
|
self->mChannelChild->ProcessOnTransportAndData(
|
|
aChannelStatus, aTransportStatus, aOffset, aCount, data,
|
|
aOnDataAvailableStart);
|
|
};
|
|
|
|
// Bug 1641336: Race only happens if the data is from socket process.
|
|
if (IsWaitingOnStartRequest()) {
|
|
LOG((" > pending until OnStartRequest [offset=%" PRIu64 " count=%" PRIu32
|
|
"]\n",
|
|
aOffset, aCount));
|
|
|
|
mQueuedRunnables.AppendElement(NS_NewRunnableFunction(
|
|
"HttpBackgroundChannelChild::RecvOnTransportAndData",
|
|
std::move(callProcessOnTransportAndData)));
|
|
return IPC_OK();
|
|
}
|
|
|
|
callProcessOnTransportAndData();
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult HttpBackgroundChannelChild::RecvOnStopRequest(
|
|
const nsresult& aChannelStatus, const ResourceTimingStructArgs& aTiming,
|
|
const TimeStamp& aLastActiveTabOptHit,
|
|
const nsHttpHeaderArray& aResponseTrailers,
|
|
nsTArray<ConsoleReportCollected>&& aConsoleReports,
|
|
const bool& aFromSocketProcess, const TimeStamp& aOnStopRequestStart) {
|
|
LOG(
|
|
("HttpBackgroundChannelChild::RecvOnStopRequest [this=%p, "
|
|
"aFromSocketProcess=%d, mFirstODASource=%d]\n",
|
|
this, aFromSocketProcess, mFirstODASource));
|
|
MOZ_ASSERT(gSocketTransportService);
|
|
MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
|
|
|
|
// It's enough to set this from (just before) OnStopRequest notification,
|
|
// since we don't need this value sooner than a channel was done loading -
|
|
// everything this timestamp affects takes place only after a channel's
|
|
// OnStopRequest.
|
|
nsHttp::SetLastActiveTabLoadOptimizationHit(aLastActiveTabOptHit);
|
|
|
|
if (NS_WARN_IF(!mChannelChild)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
if (IsWaitingOnStartRequest()) {
|
|
LOG((" > pending until OnStartRequest [status=%" PRIx32 "]\n",
|
|
static_cast<uint32_t>(aChannelStatus)));
|
|
|
|
RefPtr<HttpBackgroundChannelChild> self = this;
|
|
|
|
nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
|
|
"HttpBackgroundChannelChild::RecvOnStopRequest",
|
|
[self, aChannelStatus, aTiming, aLastActiveTabOptHit, aResponseTrailers,
|
|
consoleReports = CopyableTArray{std::move(aConsoleReports)},
|
|
aFromSocketProcess, aOnStopRequestStart]() mutable {
|
|
self->RecvOnStopRequest(aChannelStatus, aTiming, aLastActiveTabOptHit,
|
|
aResponseTrailers, std::move(consoleReports),
|
|
aFromSocketProcess, aOnStopRequestStart);
|
|
});
|
|
|
|
mQueuedRunnables.AppendElement(task.forget());
|
|
return IPC_OK();
|
|
}
|
|
|
|
if (mFirstODASource != ODA_FROM_SOCKET) {
|
|
if (!aFromSocketProcess) {
|
|
mOnStopRequestCalled = true;
|
|
mChannelChild->ProcessOnStopRequest(
|
|
aChannelStatus, aTiming, aResponseTrailers,
|
|
std::move(aConsoleReports), false, aOnStopRequestStart);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
MOZ_ASSERT(mFirstODASource == ODA_FROM_SOCKET);
|
|
|
|
if (aFromSocketProcess) {
|
|
MOZ_ASSERT(!mOnStopRequestCalled);
|
|
mOnStopRequestCalled = true;
|
|
mChannelChild->ProcessOnStopRequest(
|
|
aChannelStatus, aTiming, aResponseTrailers, std::move(aConsoleReports),
|
|
true, aOnStopRequestStart);
|
|
if (mConsoleReportTask) {
|
|
mConsoleReportTask();
|
|
mConsoleReportTask = nullptr;
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult HttpBackgroundChannelChild::RecvOnConsoleReport(
|
|
nsTArray<ConsoleReportCollected>&& aConsoleReports) {
|
|
LOG(("HttpBackgroundChannelChild::RecvOnConsoleReport [this=%p]\n", this));
|
|
MOZ_ASSERT(mFirstODASource == ODA_FROM_SOCKET);
|
|
MOZ_ASSERT(gSocketTransportService);
|
|
MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
|
|
|
|
if (IsWaitingOnStartRequest()) {
|
|
LOG((" > pending until OnStartRequest\n"));
|
|
|
|
RefPtr<HttpBackgroundChannelChild> self = this;
|
|
|
|
nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
|
|
"HttpBackgroundChannelChild::RecvOnConsoleReport",
|
|
[self, consoleReports =
|
|
CopyableTArray{std::move(aConsoleReports)}]() mutable {
|
|
self->RecvOnConsoleReport(std::move(consoleReports));
|
|
});
|
|
|
|
mQueuedRunnables.AppendElement(task.forget());
|
|
return IPC_OK();
|
|
}
|
|
|
|
if (mOnStopRequestCalled) {
|
|
mChannelChild->ProcessOnConsoleReport(std::move(aConsoleReports));
|
|
} else {
|
|
RefPtr<HttpBackgroundChannelChild> self = this;
|
|
mConsoleReportTask = [self, consoleReports = CopyableTArray{
|
|
std::move(aConsoleReports)}]() mutable {
|
|
self->mChannelChild->ProcessOnConsoleReport(std::move(consoleReports));
|
|
};
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult HttpBackgroundChannelChild::RecvNotifyClassificationFlags(
|
|
const uint32_t& aClassificationFlags, const bool& aIsThirdParty) {
|
|
LOG(
|
|
("HttpBackgroundChannelChild::RecvNotifyClassificationFlags "
|
|
"classificationFlags=%" PRIu32 ", thirdparty=%d [this=%p]\n",
|
|
aClassificationFlags, static_cast<int>(aIsThirdParty), this));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
if (NS_WARN_IF(!mChannelChild)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
// NotifyClassificationFlags has no order dependency to OnStartRequest.
|
|
// It this be handled as soon as possible
|
|
mChannelChild->ProcessNotifyClassificationFlags(aClassificationFlags,
|
|
aIsThirdParty);
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo(
|
|
const ClassifierInfo& info) {
|
|
LOG(("HttpBackgroundChannelChild::RecvSetClassifierMatchedInfo [this=%p]\n",
|
|
this));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
if (NS_WARN_IF(!mChannelChild)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
// SetClassifierMatchedInfo has no order dependency to OnStartRequest.
|
|
// It this be handled as soon as possible
|
|
mChannelChild->ProcessSetClassifierMatchedInfo(info.list(), info.provider(),
|
|
info.fullhash());
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult HttpBackgroundChannelChild::RecvSetClassifierMatchedTrackingInfo(
|
|
const ClassifierInfo& info) {
|
|
LOG(
|
|
("HttpBackgroundChannelChild::RecvSetClassifierMatchedTrackingInfo "
|
|
"[this=%p]\n",
|
|
this));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
if (NS_WARN_IF(!mChannelChild)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
// SetClassifierMatchedTrackingInfo has no order dependency to
|
|
// OnStartRequest. It this be handled as soon as possible
|
|
mChannelChild->ProcessSetClassifierMatchedTrackingInfo(info.list(),
|
|
info.fullhash());
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult HttpBackgroundChannelChild::RecvAttachStreamFilter(
|
|
Endpoint<extensions::PStreamFilterParent>&& aEndpoint) {
|
|
LOG(("HttpBackgroundChannelChild::RecvAttachStreamFilter [this=%p]\n", this));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
if (NS_WARN_IF(!mChannelChild)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
mChannelChild->ProcessAttachStreamFilter(std::move(aEndpoint));
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult HttpBackgroundChannelChild::RecvDetachStreamFilters() {
|
|
LOG(("HttpBackgroundChannelChild::RecvDetachStreamFilters [this=%p]\n",
|
|
this));
|
|
MOZ_ASSERT(OnSocketThread());
|
|
|
|
if (NS_WARN_IF(!mChannelChild)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
mChannelChild->ProcessDetachStreamFilters();
|
|
return IPC_OK();
|
|
}
|
|
|
|
void HttpBackgroundChannelChild::ActorDestroy(ActorDestroyReason aWhy) {
|
|
LOG(("HttpBackgroundChannelChild::ActorDestroy[this=%p]\n", this));
|
|
// This function might be called during shutdown phase, so OnSocketThread()
|
|
// might return false even on STS thread. Use IsOnCurrentThreadInfallible()
|
|
// to get correct information.
|
|
MOZ_ASSERT(gSocketTransportService);
|
|
MOZ_ASSERT(gSocketTransportService->IsOnCurrentThreadInfallible());
|
|
|
|
// Ensure all IPC messages received before ActorDestroy can be
|
|
// handled correctly. If there is any pending IPC message, destroyed
|
|
// mChannelChild until those messages are flushed.
|
|
// If background channel is not closed by normal IPDL actor deletion,
|
|
// remove the HttpChannelChild reference and notify background channel
|
|
// destroyed immediately.
|
|
if (aWhy == Deletion && !mQueuedRunnables.IsEmpty()) {
|
|
LOG((" > pending until queued messages are flushed\n"));
|
|
RefPtr<HttpBackgroundChannelChild> self = this;
|
|
mQueuedRunnables.AppendElement(NS_NewRunnableFunction(
|
|
"HttpBackgroundChannelChild::ActorDestroy", [self]() {
|
|
MOZ_ASSERT(OnSocketThread());
|
|
RefPtr<HttpChannelChild> channelChild =
|
|
std::move(self->mChannelChild);
|
|
|
|
if (channelChild) {
|
|
channelChild->OnBackgroundChildDestroyed(self);
|
|
}
|
|
}));
|
|
return;
|
|
}
|
|
|
|
if (mChannelChild) {
|
|
RefPtr<HttpChannelChild> channelChild = std::move(mChannelChild);
|
|
|
|
channelChild->OnBackgroundChildDestroyed(this);
|
|
}
|
|
}
|
|
|
|
} // namespace net
|
|
} // namespace mozilla
|