diff options
Diffstat (limited to 'dom/fetch/FetchParent.cpp')
-rw-r--r-- | dom/fetch/FetchParent.cpp | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/dom/fetch/FetchParent.cpp b/dom/fetch/FetchParent.cpp new file mode 100644 index 0000000000..93e9b9e43e --- /dev/null +++ b/dom/fetch/FetchParent.cpp @@ -0,0 +1,340 @@ +/* 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/. */ + +#include "FetchLog.h" +#include "FetchParent.h" +#include "FetchService.h" +#include "InternalRequest.h" +#include "InternalResponse.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/ClientInfo.h" +#include "mozilla/dom/FetchTypes.h" +#include "mozilla/dom/PerformanceTimingTypes.h" +#include "mozilla/dom/ServiceWorkerDescriptor.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "nsThreadUtils.h" + +using namespace mozilla::ipc; + +namespace mozilla::dom { + +NS_IMPL_ISUPPORTS(FetchParent::FetchParentCSPEventListener, nsICSPEventListener) + +FetchParent::FetchParentCSPEventListener::FetchParentCSPEventListener( + const nsID& aActorID, nsCOMPtr<nsISerialEventTarget> aEventTarget) + : mActorID(aActorID), mEventTarget(aEventTarget) { + MOZ_ASSERT(mEventTarget); + FETCH_LOG(("FetchParentCSPEventListener [%p] actor ID: %s", this, + mActorID.ToString().get())); +} + +NS_IMETHODIMP FetchParent::FetchParentCSPEventListener::OnCSPViolationEvent( + const nsAString& aJSON) { + AssertIsOnMainThread(); + FETCH_LOG(("FetchParentCSPEventListener::OnCSPViolationEvent [%p]", this)); + + nsAutoString json(aJSON); + nsCOMPtr<nsIRunnable> r = + NS_NewRunnableFunction(__func__, [actorID = mActorID, json]() mutable { + FETCH_LOG( + ("FetchParentCSPEventListener::OnCSPViolationEvent, Runnale")); + RefPtr<FetchParent> actor = FetchParent::GetActorByID(actorID); + if (actor) { + actor->OnCSPViolationEvent(json); + } + }); + + MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r, nsIThread::DISPATCH_NORMAL)); + return NS_OK; +} + +nsTHashMap<nsIDHashKey, RefPtr<FetchParent>> FetchParent::sActorTable; + +/*static*/ +RefPtr<FetchParent> FetchParent::GetActorByID(const nsID& aID) { + AssertIsOnBackgroundThread(); + auto entry = sActorTable.Lookup(aID); + if (entry) { + return entry.Data(); + } + return nullptr; +} + +FetchParent::FetchParent() : mID(nsID::GenerateUUID()) { + FETCH_LOG(("FetchParent::FetchParent [%p]", this)); + AssertIsOnBackgroundThread(); + mBackgroundEventTarget = GetCurrentSerialEventTarget(); + MOZ_ASSERT(mBackgroundEventTarget); + if (!sActorTable.WithEntryHandle(mID, [&](auto&& entry) { + if (entry.HasEntry()) { + return false; + } + entry.Insert(this); + return true; + })) { + FETCH_LOG(("FetchParent::FetchParent entry[%p] already exists", this)); + } +} + +FetchParent::~FetchParent() { + FETCH_LOG(("FetchParent::~FetchParent [%p]", this)); + // MOZ_ASSERT(!mBackgroundEventTarget); + MOZ_ASSERT(!mResponsePromises); + MOZ_ASSERT(mActorDestroyed && mIsDone); +} + +IPCResult FetchParent::RecvFetchOp(FetchOpArgs&& aArgs) { + FETCH_LOG(("FetchParent::RecvFetchOp [%p]", this)); + AssertIsOnBackgroundThread(); + + MOZ_ASSERT(!mIsDone); + if (mActorDestroyed) { + return IPC_OK(); + } + + mRequest = MakeSafeRefPtr<InternalRequest>(std::move(aArgs.request())); + mPrincipalInfo = std::move(aArgs.principalInfo()); + mWorkerScript = aArgs.workerScript(); + mClientInfo = Some(ClientInfo(aArgs.clientInfo())); + if (aArgs.controller().isSome()) { + mController = Some(ServiceWorkerDescriptor(aArgs.controller().ref())); + } + mCookieJarSettings = aArgs.cookieJarSettings(); + mNeedOnDataAvailable = aArgs.needOnDataAvailable(); + mHasCSPEventListener = aArgs.hasCSPEventListener(); + + if (mHasCSPEventListener) { + mCSPEventListener = + MakeRefPtr<FetchParentCSPEventListener>(mID, mBackgroundEventTarget); + } + mAssociatedBrowsingContextID = aArgs.associatedBrowsingContextID(); + + MOZ_ASSERT(!mPromise); + mPromise = new GenericPromise::Private(__func__); + + RefPtr<FetchParent> self = this; + mPromise->Then( + mBackgroundEventTarget, __func__, + [self](const bool&& result) mutable { + FETCH_LOG( + ("FetchParent::RecvFetchOp [%p] Success Callback", self.get())); + AssertIsOnBackgroundThread(); + self->mPromise = nullptr; + if (self->mIsDone) { + FETCH_LOG(("FetchParent::RecvFetchOp [%p] Fetch has already aborted", + self.get())); + if (!self->mActorDestroyed) { + Unused << NS_WARN_IF( + !self->Send__delete__(self, NS_ERROR_DOM_ABORT_ERR)); + } + return; + } + self->mIsDone = true; + if (!self->mActorDestroyed && !self->mExtendForCSPEventListener) { + FETCH_LOG(("FetchParent::RecvFetchOp [%p] Send__delete__(NS_OK)", + self.get())); + Unused << NS_WARN_IF(!self->Send__delete__(self, NS_OK)); + } + }, + [self](const nsresult&& aErr) mutable { + FETCH_LOG( + ("FetchParent::RecvFetchOp [%p] Failure Callback", self.get())); + AssertIsOnBackgroundThread(); + self->mIsDone = true; + self->mPromise = nullptr; + if (!self->mActorDestroyed) { + FETCH_LOG(("FetchParent::RecvFetchOp [%p] Send__delete__(aErr)", + self.get())); + Unused << NS_WARN_IF(!self->Send__delete__(self, aErr)); + } + }); + + RefPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self]() mutable { + FETCH_LOG( + ("FetchParent::RecvFetchOp [%p], Main Thread Runnable", self.get())); + AssertIsOnMainThread(); + if (self->mIsDone) { + MOZ_ASSERT(!self->mResponsePromises); + MOZ_ASSERT(self->mPromise); + FETCH_LOG( + ("FetchParent::RecvFetchOp [%p], Main Thread Runnable, " + "already aborted", + self.get())); + self->mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); + return; + } + RefPtr<FetchService> fetchService = FetchService::GetInstance(); + MOZ_ASSERT(fetchService); + MOZ_ASSERT(!self->mResponsePromises); + self->mResponsePromises = + fetchService->Fetch(AsVariant(FetchService::WorkerFetchArgs( + {self->mRequest.clonePtr(), self->mPrincipalInfo, + self->mWorkerScript, self->mClientInfo, self->mController, + self->mCookieJarSettings, self->mNeedOnDataAvailable, + self->mCSPEventListener, self->mAssociatedBrowsingContextID, + self->mBackgroundEventTarget, self->mID}))); + + self->mResponsePromises->GetResponseEndPromise()->Then( + GetMainThreadSerialEventTarget(), __func__, + [self](ResponseEndArgs&& aArgs) mutable { + AssertIsOnMainThread(); + MOZ_ASSERT(self->mPromise); + self->mPromise->Resolve(true, __func__); + self->mResponsePromises = nullptr; + }, + [self](CopyableErrorResult&& aErr) mutable { + AssertIsOnMainThread(); + MOZ_ASSERT(self->mPromise); + self->mPromise->Reject(aErr.StealNSResult(), __func__); + self->mResponsePromises = nullptr; + }); + }); + + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(r.forget(), nsIThread::DISPATCH_NORMAL)); + + return IPC_OK(); +} + +IPCResult FetchParent::RecvAbortFetchOp() { + FETCH_LOG(("FetchParent::RecvAbortFetchOp [%p]", this)); + AssertIsOnBackgroundThread(); + + if (mIsDone) { + FETCH_LOG(("FetchParent::RecvAbortFetchOp [%p], Already aborted", this)); + return IPC_OK(); + } + mIsDone = true; + + RefPtr<FetchParent> self = this; + nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self]() mutable { + FETCH_LOG(("FetchParent::RecvAbortFetchOp Runnable")); + AssertIsOnMainThread(); + if (self->mResponsePromises) { + RefPtr<FetchService> fetchService = FetchService::GetInstance(); + MOZ_ASSERT(fetchService); + fetchService->CancelFetch(std::move(self->mResponsePromises)); + } + }); + + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(r.forget(), nsIThread::DISPATCH_NORMAL)); + + return IPC_OK(); +} + +void FetchParent::OnResponseAvailableInternal( + SafeRefPtr<InternalResponse>&& aResponse) { + FETCH_LOG(("FetchParent::OnResponseAvailableInternal [%p]", this)); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aResponse); + MOZ_ASSERT(!mActorDestroyed); + + if (mIsDone && aResponse->Type() != ResponseType::Error) { + FETCH_LOG( + ("FetchParent::OnResponseAvailableInternal [%p] " + "Fetch has already aborted", + this)); + return; + } + + // To monitor the stream status between processes, response's body can not be + // serialized as RemoteLazyInputStream. Such that stream close can be + // propagated to FetchDriver in the parent process. + aResponse->SetSerializeAsLazy(false); + + // CSP violation notification is asynchronous. Extending the FetchParent's + // life cycle for the notificaiton. + if (aResponse->Type() == ResponseType::Error && + aResponse->GetErrorCode() == NS_ERROR_CONTENT_BLOCKED && + mCSPEventListener) { + FETCH_LOG( + ("FetchParent::OnResponseAvailableInternal [%p] " + "NS_ERROR_CONTENT_BLOCKED", + this)); + mExtendForCSPEventListener = true; + } + + Unused << SendOnResponseAvailableInternal( + aResponse->ToParentToChildInternalResponse(WrapNotNull(Manager()))); +} + +void FetchParent::OnResponseEnd(const ResponseEndArgs& aArgs) { + FETCH_LOG(("FetchParent::OnResponseEnd [%p]", this)); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + if (mIsDone && aArgs.endReason() != FetchDriverObserver::eAborted) { + FETCH_LOG( + ("FetchParent::OnResponseEnd [%p] " + "Fetch has already aborted", + this)); + return; + } + + Unused << SendOnResponseEnd(aArgs); +} + +void FetchParent::OnDataAvailable() { + FETCH_LOG(("FetchParent::OnDataAvailable [%p]", this)); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + Unused << SendOnDataAvailable(); +} + +void FetchParent::OnFlushConsoleReport( + const nsTArray<net::ConsoleReportCollected>& aReports) { + FETCH_LOG(("FetchParent::OnFlushConsoleReport [%p]", this)); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + Unused << SendOnFlushConsoleReport(aReports); +} + +void FetchParent::OnReportPerformanceTiming(const ResponseTiming&& aTiming) { + FETCH_LOG(("FetchParent::OnReportPerformanceTiming [%p]", this)); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + Unused << SendOnReportPerformanceTiming(aTiming); +} + +void FetchParent::OnNotifyNetworkMonitorAlternateStack(uint64_t aChannelID) { + FETCH_LOG(("FetchParent::OnNotifyNetworkMonitorAlternateStack [%p]", this)); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + Unused << SendOnNotifyNetworkMonitorAlternateStack(aChannelID); +} + +void FetchParent::ActorDestroy(ActorDestroyReason aReason) { + FETCH_LOG(("FetchParent::ActorDestroy [%p]", this)); + AssertIsOnBackgroundThread(); + mActorDestroyed = true; + auto entry = sActorTable.Lookup(mID); + if (entry) { + entry.Remove(); + FETCH_LOG(("FetchParent::ActorDestroy entry [%p] removed", this)); + } + // Force to abort the existing fetch. + // Actor can be destoried by shutdown when still fetching. + RecvAbortFetchOp(); + // mBackgroundEventTarget = nullptr; +} + +nsICSPEventListener* FetchParent::GetCSPEventListener() { + return mCSPEventListener; +} + +void FetchParent::OnCSPViolationEvent(const nsAString& aJSON) { + FETCH_LOG(("FetchParent::OnCSPViolationEvent [%p]", this)); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mHasCSPEventListener); + MOZ_ASSERT(!mActorDestroyed); + + Unused << SendOnCSPViolationEvent(aJSON); +} + +} // namespace mozilla::dom |