forked from mirrors/gecko-dev
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
380 lines
12 KiB
C++
380 lines
12 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 "FetchChild.h"
|
|
#include "FetchLog.h"
|
|
#include "FetchObserver.h"
|
|
#include "InternalResponse.h"
|
|
#include "Request.h"
|
|
#include "Response.h"
|
|
#include "mozilla/ConsoleReportCollector.h"
|
|
#include "mozilla/SchedulerGroup.h"
|
|
#include "mozilla/dom/PerformanceTiming.h"
|
|
#include "mozilla/dom/PerformanceStorage.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/dom/RemoteWorkerChild.h"
|
|
#include "mozilla/dom/SecurityPolicyViolationEventBinding.h"
|
|
#include "mozilla/dom/WorkerChannelInfo.h"
|
|
#include "mozilla/dom/WorkerPrivate.h"
|
|
#include "mozilla/dom/WorkerRef.h"
|
|
#include "mozilla/dom/WorkerScope.h"
|
|
#include "nsIAsyncInputStream.h"
|
|
#include "nsIGlobalObject.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsIRunnable.h"
|
|
#include "nsIURI.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsThreadUtils.h"
|
|
|
|
namespace mozilla::dom {
|
|
|
|
NS_IMPL_ISUPPORTS0(FetchChild)
|
|
|
|
mozilla::ipc::IPCResult FetchChild::Recv__delete__(const nsresult&& aResult) {
|
|
FETCH_LOG(("FetchChild::Recv__delete__ [%p]", this));
|
|
if (mIsShutdown) {
|
|
return IPC_OK();
|
|
}
|
|
// Shutdown has not been called, so mWorkerRef->Private() should be still
|
|
// alive.
|
|
MOZ_ASSERT(mWorkerRef->Private());
|
|
mWorkerRef->Private()->AssertIsOnWorkerThread();
|
|
|
|
if (mPromise->State() == Promise::PromiseState::Pending) {
|
|
if (NS_FAILED(aResult)) {
|
|
mPromise->MaybeReject(aResult);
|
|
if (mFetchObserver) {
|
|
mFetchObserver->SetState(FetchState::Errored);
|
|
}
|
|
} else {
|
|
mPromise->MaybeResolve(aResult);
|
|
if (mFetchObserver) {
|
|
mFetchObserver->SetState(FetchState::Complete);
|
|
}
|
|
}
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult FetchChild::RecvOnResponseAvailableInternal(
|
|
ParentToChildInternalResponse&& aResponse) {
|
|
FETCH_LOG(("FetchChild::RecvOnResponseAvailableInternal [%p]", this));
|
|
if (mIsShutdown) {
|
|
return IPC_OK();
|
|
}
|
|
// Shutdown has not been called, so mWorkerRef->Private() should be still
|
|
// alive.
|
|
MOZ_ASSERT(mWorkerRef->Private());
|
|
mWorkerRef->Private()->AssertIsOnWorkerThread();
|
|
SafeRefPtr<InternalResponse> internalResponse =
|
|
InternalResponse::FromIPC(aResponse);
|
|
IgnoredErrorResult result;
|
|
internalResponse->Headers()->SetGuard(HeadersGuardEnum::Immutable, result);
|
|
MOZ_ASSERT(internalResponse);
|
|
|
|
if (internalResponse->Type() != ResponseType::Error) {
|
|
if (internalResponse->Type() == ResponseType::Opaque) {
|
|
internalResponse->GeneratePaddingInfo();
|
|
}
|
|
|
|
if (mFetchObserver) {
|
|
mFetchObserver->SetState(FetchState::Complete);
|
|
}
|
|
nsCOMPtr<nsIGlobalObject> global;
|
|
global = mWorkerRef->Private()->GlobalScope();
|
|
RefPtr<Response> response =
|
|
new Response(global, internalResponse.clonePtr(), mSignalImpl);
|
|
mPromise->MaybeResolve(response);
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
FETCH_LOG(
|
|
("FetchChild::RecvOnResponseAvailableInternal [%p] response type is "
|
|
"Error(0x%x)",
|
|
this, static_cast<int32_t>(internalResponse->GetErrorCode())));
|
|
if (mFetchObserver) {
|
|
mFetchObserver->SetState(FetchState::Errored);
|
|
}
|
|
mPromise->MaybeRejectWithTypeError<MSG_FETCH_FAILED>();
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult FetchChild::RecvOnResponseEnd(ResponseEndArgs&& aArgs) {
|
|
FETCH_LOG(("FetchChild::RecvOnResponseEnd [%p]", this));
|
|
if (mIsShutdown) {
|
|
return IPC_OK();
|
|
}
|
|
// Shutdown has not been called, so mWorkerRef->Private() should be still
|
|
// alive.
|
|
MOZ_ASSERT(mWorkerRef->Private());
|
|
mWorkerRef->Private()->AssertIsOnWorkerThread();
|
|
|
|
if (aArgs.endReason() == FetchDriverObserver::eAborted) {
|
|
FETCH_LOG(
|
|
("FetchChild::RecvOnResponseEnd [%p] endReason is eAborted", this));
|
|
if (mFetchObserver) {
|
|
mFetchObserver->SetState(FetchState::Errored);
|
|
}
|
|
mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
|
}
|
|
|
|
Unfollow();
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult FetchChild::RecvOnDataAvailable() {
|
|
FETCH_LOG(("FetchChild::RecvOnDataAvailable [%p]", this));
|
|
if (mIsShutdown) {
|
|
return IPC_OK();
|
|
}
|
|
// Shutdown has not been called, so mWorkerRef->Private() should be still
|
|
// alive.
|
|
MOZ_ASSERT(mWorkerRef->Private());
|
|
mWorkerRef->Private()->AssertIsOnWorkerThread();
|
|
|
|
if (mFetchObserver && mFetchObserver->State() == FetchState::Requesting) {
|
|
mFetchObserver->SetState(FetchState::Responding);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult FetchChild::RecvOnFlushConsoleReport(
|
|
nsTArray<net::ConsoleReportCollected>&& aReports) {
|
|
FETCH_LOG(("FetchChild::RecvOnFlushConsoleReport [%p]", this));
|
|
if (mIsShutdown) {
|
|
return IPC_OK();
|
|
}
|
|
// Shutdown has not been called, so mWorkerRef->Private() should be still
|
|
// alive.
|
|
MOZ_ASSERT(mWorkerRef->Private());
|
|
mWorkerRef->Private()->AssertIsOnWorkerThread();
|
|
MOZ_ASSERT(mReporter);
|
|
|
|
RefPtr<ThreadSafeWorkerRef> workerRef = mWorkerRef;
|
|
nsCOMPtr<nsIConsoleReportCollector> reporter = mReporter;
|
|
|
|
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
|
__func__, [reports = std::move(aReports), reporter = std::move(reporter),
|
|
workerRef = std::move(workerRef)]() mutable {
|
|
for (const auto& report : reports) {
|
|
reporter->AddConsoleReport(
|
|
report.errorFlags(), report.category(),
|
|
static_cast<nsContentUtils::PropertiesFile>(
|
|
report.propertiesFile()),
|
|
report.sourceFileURI(), report.lineNumber(),
|
|
report.columnNumber(), report.messageName(),
|
|
report.stringParams());
|
|
}
|
|
|
|
if (workerRef->Private()->IsServiceWorker()) {
|
|
reporter->FlushReportsToConsoleForServiceWorkerScope(
|
|
workerRef->Private()->ServiceWorkerScope());
|
|
}
|
|
|
|
if (workerRef->Private()->IsSharedWorker()) {
|
|
workerRef->Private()
|
|
->GetRemoteWorkerController()
|
|
->FlushReportsOnMainThread(reporter);
|
|
}
|
|
|
|
reporter->FlushConsoleReports(workerRef->Private()->GetLoadGroup());
|
|
});
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
RefPtr<FetchChild> FetchChild::Create(WorkerPrivate* aWorkerPrivate,
|
|
RefPtr<Promise> aPromise,
|
|
RefPtr<AbortSignalImpl> aSignalImpl,
|
|
RefPtr<FetchObserver> aObserver) {
|
|
MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
RefPtr<FetchChild> actor = MakeRefPtr<FetchChild>(
|
|
std::move(aPromise), std::move(aSignalImpl), std::move(aObserver));
|
|
|
|
RefPtr<StrongWorkerRef> workerRef =
|
|
StrongWorkerRef::Create(aWorkerPrivate, "FetchChild", [actor]() {
|
|
FETCH_LOG(("StrongWorkerRef callback"));
|
|
actor->Shutdown();
|
|
});
|
|
if (NS_WARN_IF(!workerRef)) {
|
|
return nullptr;
|
|
}
|
|
|
|
actor->mWorkerRef = new ThreadSafeWorkerRef(workerRef);
|
|
if (NS_WARN_IF(!actor->mWorkerRef)) {
|
|
return nullptr;
|
|
}
|
|
return actor;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult FetchChild::RecvOnCSPViolationEvent(
|
|
const nsAString& aJSON) {
|
|
FETCH_LOG(("FetchChild::RecvOnCSPViolationEvent [%p] aJSON: %s\n", this,
|
|
NS_ConvertUTF16toUTF8(aJSON).BeginReading()));
|
|
|
|
nsString JSON(aJSON);
|
|
|
|
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [JSON]() mutable {
|
|
SecurityPolicyViolationEventInit violationEventInit;
|
|
if (NS_WARN_IF(!violationEventInit.Init(JSON))) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv =
|
|
NS_NewURI(getter_AddRefs(uri), violationEventInit.mBlockedURI);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIObserverService> observerService =
|
|
mozilla::services::GetObserverService();
|
|
if (!observerService) {
|
|
return;
|
|
}
|
|
|
|
rv = observerService->NotifyObservers(
|
|
uri, CSP_VIOLATION_TOPIC, violationEventInit.mViolatedDirective.get());
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return;
|
|
}
|
|
});
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
|
|
|
|
if (mCSPEventListener) {
|
|
Unused << NS_WARN_IF(
|
|
NS_FAILED(mCSPEventListener->OnCSPViolationEvent(aJSON)));
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult FetchChild::RecvOnReportPerformanceTiming(
|
|
ResponseTiming&& aTiming) {
|
|
FETCH_LOG(("FetchChild::RecvOnReportPerformanceTiming [%p]", this));
|
|
if (mIsShutdown) {
|
|
return IPC_OK();
|
|
}
|
|
// Shutdown has not been called, so mWorkerRef->Private() should be still
|
|
// alive.
|
|
MOZ_ASSERT(mWorkerRef->Private());
|
|
mWorkerRef->Private()->AssertIsOnWorkerThread();
|
|
|
|
RefPtr<PerformanceStorage> performanceStorage =
|
|
mWorkerRef->Private()->GetPerformanceStorage();
|
|
if (performanceStorage) {
|
|
performanceStorage->AddEntry(
|
|
aTiming.entryName(), aTiming.initiatorType(),
|
|
MakeUnique<PerformanceTimingData>(aTiming.timingData()));
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult FetchChild::RecvOnNotifyNetworkMonitorAlternateStack(
|
|
uint64_t aChannelID) {
|
|
FETCH_LOG(
|
|
("FetchChild::RecvOnNotifyNetworkMonitorAlternateStack [%p]", this));
|
|
if (mIsShutdown) {
|
|
return IPC_OK();
|
|
}
|
|
// Shutdown has not been called, so mWorkerRef->Private() should be still
|
|
// alive.
|
|
MOZ_ASSERT(mWorkerRef->Private());
|
|
mWorkerRef->Private()->AssertIsOnWorkerThread();
|
|
|
|
if (!mOriginStack) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
if (!mWorkerChannelInfo) {
|
|
mWorkerChannelInfo = MakeRefPtr<WorkerChannelInfo>(
|
|
aChannelID, mWorkerRef->Private()->AssociatedBrowsingContextID());
|
|
}
|
|
|
|
// Unfortunately, SerializedStackHolder can only be read on the main thread.
|
|
// However, it doesn't block the fetch execution.
|
|
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
|
__func__, [channel = mWorkerChannelInfo,
|
|
stack = std::move(mOriginStack)]() mutable {
|
|
NotifyNetworkMonitorAlternateStack(channel, std::move(stack));
|
|
});
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
void FetchChild::SetCSPEventListener(nsICSPEventListener* aListener) {
|
|
MOZ_ASSERT(aListener && !mCSPEventListener);
|
|
mCSPEventListener = aListener;
|
|
}
|
|
|
|
FetchChild::FetchChild(RefPtr<Promise>&& aPromise,
|
|
RefPtr<AbortSignalImpl>&& aSignalImpl,
|
|
RefPtr<FetchObserver>&& aObserver)
|
|
: mPromise(std::move(aPromise)),
|
|
mSignalImpl(std::move(aSignalImpl)),
|
|
mFetchObserver(std::move(aObserver)),
|
|
mReporter(new ConsoleReportCollector()) {
|
|
FETCH_LOG(("FetchChild::FetchChild [%p]", this));
|
|
}
|
|
|
|
void FetchChild::RunAbortAlgorithm() {
|
|
FETCH_LOG(("FetchChild::RunAbortAlgorithm [%p]", this));
|
|
if (mIsShutdown) {
|
|
return;
|
|
}
|
|
if (mWorkerRef) {
|
|
Unused << SendAbortFetchOp();
|
|
}
|
|
}
|
|
|
|
void FetchChild::DoFetchOp(const FetchOpArgs& aArgs) {
|
|
FETCH_LOG(("FetchChild::DoFetchOp [%p]", this));
|
|
if (mSignalImpl) {
|
|
if (mSignalImpl->Aborted()) {
|
|
Unused << SendAbortFetchOp();
|
|
return;
|
|
}
|
|
Follow(mSignalImpl);
|
|
}
|
|
Unused << SendFetchOp(aArgs);
|
|
}
|
|
|
|
void FetchChild::Shutdown() {
|
|
FETCH_LOG(("FetchChild::Shutdown [%p]", this));
|
|
if (mIsShutdown) {
|
|
return;
|
|
}
|
|
mIsShutdown.Flip();
|
|
|
|
// If mWorkerRef is nullptr here, that means Recv__delete__() must be called
|
|
if (!mWorkerRef) {
|
|
return;
|
|
}
|
|
mPromise = nullptr;
|
|
mFetchObserver = nullptr;
|
|
Unfollow();
|
|
mSignalImpl = nullptr;
|
|
mCSPEventListener = nullptr;
|
|
Unused << SendAbortFetchOp();
|
|
mWorkerRef = nullptr;
|
|
}
|
|
|
|
void FetchChild::ActorDestroy(ActorDestroyReason aReason) {
|
|
FETCH_LOG(("FetchChild::ActorDestroy [%p]", this));
|
|
mPromise = nullptr;
|
|
mFetchObserver = nullptr;
|
|
mSignalImpl = nullptr;
|
|
mCSPEventListener = nullptr;
|
|
mWorkerRef = nullptr;
|
|
}
|
|
|
|
} // namespace mozilla::dom
|