summaryrefslogtreecommitdiffstats
path: root/dom/fetch/FetchParent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/fetch/FetchParent.cpp')
-rw-r--r--dom/fetch/FetchParent.cpp340
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