diff options
Diffstat (limited to 'dom/fetch/FetchChild.cpp')
-rw-r--r-- | dom/fetch/FetchChild.cpp | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/dom/fetch/FetchChild.cpp b/dom/fetch/FetchChild.cpp new file mode 100644 index 0000000000..774eb516e7 --- /dev/null +++ b/dom/fetch/FetchChild.cpp @@ -0,0 +1,380 @@ +/* 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 |