gecko-dev/dom/fetch/FetchParent.cpp
Eden Chuang bf3fa5100a Bug 1819570 - P4 Propagte stack trace notification back to the content process for PFetch. r=asuth
After PFetch is enabled, fetch() call in workers will not create a channel in the content process anymore.
Although netmonitor watches channels and NetEvents, stack traces are only caught in the content process.
That means PFetch should notify the netmonitor about the stack trace of the fetch at the proper moment.

In original fetch steps, FetchDriver would notify the netmonitor the fetch stack trace at
https://searchfox.org/mozilla-central/rev/cdddec7fd690700efa4d6b48532cf70155e0386b/dom/fetch/FetchDriver.cpp#834

When PFetch is enabled, PFetch needs also to propagate this notification back to the content process.

Depends on D174442

Differential Revision: https://phabricator.services.mozilla.com/D174443
2023-05-05 15:24:10 +00:00

341 lines
11 KiB
C++

/* 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