/* -*- 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/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())) { mChannelChild = nullptr; return NS_ERROR_FAILURE; } return NS_OK; } void HttpBackgroundChannelChild::OnChannelClosed() { LOG(("HttpBackgroundChannelChild::OnChannelClosed [this=%p]\n", this)); MOZ_ASSERT(OnSocketThread()); // HttpChannelChild is not going to handle any incoming message. mChannelChild = nullptr; // Remove pending IPC messages as well. mQueuedRunnables.Clear(); } void HttpBackgroundChannelChild::OnStartRequestReceived() { LOG(("HttpBackgroundChannelChild::OnStartRequestReceived [this=%p]\n", this)); MOZ_ASSERT(OnSocketThread()); MOZ_ASSERT(mChannelChild); MOZ_ASSERT(!mStartReceived); // Should only be called once. mStartReceived = true; nsTArray> runnables; runnables.SwapElements(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; } // hold extra reference for IPDL RefPtr self = this; Unused << self.forget().take(); mChannelChild->OnBackgroundChildReady(this); return true; } 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 (mStartSent && !mStartReceived); } // PHttpBackgroundChannelChild IPCResult HttpBackgroundChannelChild::RecvOnStartRequestSent() { LOG(("HttpBackgroundChannelChild::RecvOnStartRequestSent [this=%p]\n", this)); MOZ_ASSERT(OnSocketThread()); MOZ_ASSERT(!mStartSent); // Should only receive this message once. mStartSent = true; return IPC_OK(); } IPCResult HttpBackgroundChannelChild::RecvOnTransportAndData( const nsresult& aChannelStatus, const nsresult& aTransportStatus, const uint64_t& aOffset, const uint32_t& aCount, const nsCString& aData) { LOG(("HttpBackgroundChannelChild::RecvOnTransportAndData [this=%p]\n", this)); MOZ_ASSERT(OnSocketThread()); if (NS_WARN_IF(!mChannelChild)) { return IPC_OK(); } if (IsWaitingOnStartRequest()) { LOG((" > pending until OnStartRequest [offset=%" PRIu64 " count=%" PRIu32 "]\n", aOffset, aCount)); mQueuedRunnables.AppendElement( NewRunnableMethod( "HttpBackgroundChannelChild::RecvOnTransportAndData", this, &HttpBackgroundChannelChild::RecvOnTransportAndData, aChannelStatus, aTransportStatus, aOffset, aCount, aData)); return IPC_OK(); } mChannelChild->ProcessOnTransportAndData(aChannelStatus, aTransportStatus, aOffset, aCount, aData); return IPC_OK(); } IPCResult HttpBackgroundChannelChild::RecvOnStopRequest( const nsresult& aChannelStatus, const ResourceTimingStruct& aTiming, const TimeStamp& aLastActiveTabOptHit, const nsHttpHeaderArray& aResponseTrailers) { LOG(("HttpBackgroundChannelChild::RecvOnStopRequest [this=%p]\n", this)); 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))); mQueuedRunnables.AppendElement( NewRunnableMethod( "HttpBackgroundChannelChild::RecvOnStopRequest", this, &HttpBackgroundChannelChild::RecvOnStopRequest, aChannelStatus, aTiming, aLastActiveTabOptHit, aResponseTrailers)); return IPC_OK(); } mChannelChild->ProcessOnStopRequest(aChannelStatus, aTiming, aResponseTrailers); return IPC_OK(); } IPCResult HttpBackgroundChannelChild::RecvOnProgress( const int64_t& aProgress, const int64_t& aProgressMax) { LOG(("HttpBackgroundChannelChild::RecvOnProgress [this=%p progress=%" PRId64 " max=%" PRId64 "]\n", this, aProgress, aProgressMax)); MOZ_ASSERT(OnSocketThread()); if (NS_WARN_IF(!mChannelChild)) { return IPC_OK(); } if (IsWaitingOnStartRequest()) { LOG((" > pending until OnStartRequest [progress=%" PRId64 " max=%" PRId64 "]\n", aProgress, aProgressMax)); mQueuedRunnables.AppendElement( NewRunnableMethod( "HttpBackgroundChannelChild::RecvOnProgress", this, &HttpBackgroundChannelChild::RecvOnProgress, aProgress, aProgressMax)); return IPC_OK(); } mChannelChild->ProcessOnProgress(aProgress, aProgressMax); return IPC_OK(); } IPCResult HttpBackgroundChannelChild::RecvOnStatus(const nsresult& aStatus) { LOG(("HttpBackgroundChannelChild::RecvOnStatus [this=%p status=%" PRIx32 "]\n", this, static_cast(aStatus))); MOZ_ASSERT(OnSocketThread()); if (NS_WARN_IF(!mChannelChild)) { return IPC_OK(); } if (IsWaitingOnStartRequest()) { LOG((" > pending until OnStartRequest [status=%" PRIx32 "]\n", static_cast(aStatus))); mQueuedRunnables.AppendElement(NewRunnableMethod( "HttpBackgroundChannelChild::RecvOnStatus", this, &HttpBackgroundChannelChild::RecvOnStatus, aStatus)); return IPC_OK(); } mChannelChild->ProcessOnStatus(aStatus); return IPC_OK(); } IPCResult HttpBackgroundChannelChild::RecvFlushedForDiversion() { LOG(("HttpBackgroundChannelChild::RecvFlushedForDiversion [this=%p]\n", this)); MOZ_ASSERT(OnSocketThread()); if (NS_WARN_IF(!mChannelChild)) { return IPC_OK(); } if (IsWaitingOnStartRequest()) { LOG((" > pending until OnStartRequest\n")); mQueuedRunnables.AppendElement(NewRunnableMethod( "HttpBackgroundChannelChild::RecvFlushedForDiversion", this, &HttpBackgroundChannelChild::RecvFlushedForDiversion)); return IPC_OK(); } mChannelChild->ProcessFlushedForDiversion(); return IPC_OK(); } IPCResult HttpBackgroundChannelChild::RecvDivertMessages() { LOG(("HttpBackgroundChannelChild::RecvDivertMessages [this=%p]\n", this)); MOZ_ASSERT(OnSocketThread()); if (NS_WARN_IF(!mChannelChild)) { return IPC_OK(); } if (IsWaitingOnStartRequest()) { LOG((" > pending until OnStartRequest\n")); mQueuedRunnables.AppendElement(NewRunnableMethod( "HttpBackgroundChannelChild::RecvDivertMessages", this, &HttpBackgroundChannelChild::RecvDivertMessages)); return IPC_OK(); } mChannelChild->ProcessDivertMessages(); return IPC_OK(); } IPCResult HttpBackgroundChannelChild::RecvNotifyChannelClassifierProtectionDisabled( const uint32_t& aAcceptedReason) { LOG( ("HttpBackgroundChannelChild::" "RecvNotifyChannelClassifierProtectionDisabled [this=%p " "aAcceptedReason=%" PRIu32 "]\n", this, aAcceptedReason)); MOZ_ASSERT(OnSocketThread()); if (NS_WARN_IF(!mChannelChild)) { return IPC_OK(); } // NotifyChannelClassifierProtectionDisabled has no order dependency to // OnStartRequest. It this be handled as soon as possible mChannelChild->ProcessNotifyChannelClassifierProtectionDisabled( aAcceptedReason); return IPC_OK(); } IPCResult HttpBackgroundChannelChild::RecvNotifyCookieAllowed() { LOG(("HttpBackgroundChannelChild::RecvNotifyCookieAllowed [this=%p]\n", this)); MOZ_ASSERT(OnSocketThread()); if (NS_WARN_IF(!mChannelChild)) { return IPC_OK(); } mChannelChild->ProcessNotifyCookieAllowed(); return IPC_OK(); } IPCResult HttpBackgroundChannelChild::RecvNotifyCookieBlocked( const uint32_t& aRejectedReason) { LOG( ("HttpBackgroundChannelChild::RecvNotifyCookieBlocked [this=%p " "aRejectedReason=%" PRIu32 "]\n", this, aRejectedReason)); MOZ_ASSERT(OnSocketThread()); if (NS_WARN_IF(!mChannelChild)) { return IPC_OK(); } mChannelChild->ProcessNotifyCookieBlocked(aRejectedReason); 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(); } 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 = self->mChannelChild.forget(); if (channelChild) { channelChild->OnBackgroundChildDestroyed(self); } })); return; } if (mChannelChild) { RefPtr channelChild = mChannelChild.forget(); channelChild->OnBackgroundChildDestroyed(this); } } } // namespace net } // namespace mozilla