зеркало из https://github.com/mozilla/gecko-dev.git
381 строка
12 KiB
C++
381 строка
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
|