/* -*- 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->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 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 aMultiPartID) { LOG(("HttpBackgroundChannelChild::OnStartRequestReceived [this=%p]\n", this)); MOZ_ASSERT(OnSocketThread()); MOZ_ASSERT(mChannelChild); MOZ_ASSERT(!mStartReceived || *aMultiPartID > 0); mStartReceived = true; nsTArray> 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) { LOG(( "HttpBackgroundChannelChild::RecvOnStartRequest [this=%p, status=%" PRIx32 "]\n", this, static_cast(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); // 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 nsDependentCSubstring& aData, const bool& aDataFromSocketProcess) { RefPtr self = this; nsCString data(aData); std::function callProcessOnTransportAndData = [self, aChannelStatus, aTransportStatus, aOffset, aCount, data, 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&& 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(aChannelStatus))); RefPtr self = this; nsCOMPtr 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&& 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 self = this; nsCOMPtr 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 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(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::RecvNotifyFlashPluginStateChanged( const nsIHttpChannel::FlashPluginState& aState) { LOG( ("HttpBackgroundChannelChild::RecvNotifyFlashPluginStateChanged " "[this=%p]\n", this)); MOZ_ASSERT(OnSocketThread()); if (NS_WARN_IF(!mChannelChild)) { return IPC_OK(); } // NotifyFlashPluginStateChanged has no order dependency to OnStartRequest. // It this be handled as soon as possible mChannelChild->ProcessNotifyFlashPluginStateChanged(aState); 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&& 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(); } 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 self = this; mQueuedRunnables.AppendElement(NS_NewRunnableFunction( "HttpBackgroundChannelChild::ActorDestroy", [self]() { MOZ_ASSERT(OnSocketThread()); RefPtr channelChild = std::move(self->mChannelChild); if (channelChild) { channelChild->OnBackgroundChildDestroyed(self); } })); return; } if (mChannelChild) { RefPtr channelChild = std::move(mChannelChild); channelChild->OnBackgroundChildDestroyed(this); } } } // namespace net } // namespace mozilla