diff options
Diffstat (limited to '')
-rw-r--r-- | netwerk/protocol/http/HttpBackgroundChannelChild.cpp | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/netwerk/protocol/http/HttpBackgroundChannelChild.cpp b/netwerk/protocol/http/HttpBackgroundChannelChild.cpp new file mode 100644 index 0000000000..4ef2cd11e7 --- /dev/null +++ b/netwerk/protocol/http/HttpBackgroundChannelChild.cpp @@ -0,0 +1,499 @@ +/* -*- 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/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() { + MOZ_ASSERT(OnSocketThread()); + + if (!mChannelChild) { + return; + } + + PBackgroundChild* actorChild = + BackgroundChild::GetOrCreateSocketActorForCurrentThread(); + if (NS_WARN_IF(!actorChild)) { + return; + } + + RefPtr<BackgroundDataBridgeChild> dataBridgeChild = + new BackgroundDataBridgeChild(this); + Unused << actorChild->SendPBackgroundDataBridgeConstructor( + dataBridgeChild, mChannelChild->ChannelId()); +} + +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) { + 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); + // 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) { + RefPtr<HttpBackgroundChannelChild> self = this; + std::function<void()> callProcessOnTransportAndData = + [self, aChannelStatus, aTransportStatus, aOffset, aCount, + data = nsCString(aData), aDataFromSocketProcess]() { + 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); + }; + + // 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) { + 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]() mutable { + self->RecvOnStopRequest(aChannelStatus, aTiming, aLastActiveTabOptHit, + aResponseTrailers, std::move(consoleReports), + aFromSocketProcess); + }); + + 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); + } + 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); + 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 |