summaryrefslogtreecommitdiffstats
path: root/dom/serviceworkers/ServiceWorkerPrivateImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/serviceworkers/ServiceWorkerPrivateImpl.cpp')
-rw-r--r--dom/serviceworkers/ServiceWorkerPrivateImpl.cpp1085
1 files changed, 1085 insertions, 0 deletions
diff --git a/dom/serviceworkers/ServiceWorkerPrivateImpl.cpp b/dom/serviceworkers/ServiceWorkerPrivateImpl.cpp
new file mode 100644
index 0000000000..54bef4bd1b
--- /dev/null
+++ b/dom/serviceworkers/ServiceWorkerPrivateImpl.cpp
@@ -0,0 +1,1085 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "ServiceWorkerPrivateImpl.h"
+
+#include <utility>
+
+#include "MainThreadUtils.h"
+#include "js/ErrorReport.h"
+#include "nsContentUtils.h"
+#include "nsDebug.h"
+#include "nsError.h"
+#include "nsIChannel.h"
+#include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIHttpHeaderVisitor.h"
+#include "nsINetworkInterceptController.h"
+#include "nsIObserverService.h"
+#include "nsIScriptError.h"
+#include "nsIURI.h"
+#include "nsIUploadChannel2.h"
+#include "nsThreadUtils.h"
+#include "nsICacheInfoChannel.h"
+#include "nsNetUtil.h"
+
+#include "ServiceWorkerCloneData.h"
+#include "ServiceWorkerManager.h"
+#include "ServiceWorkerRegistrationInfo.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Result.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StoragePrincipalHelper.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/ClientIPCTypes.h"
+#include "mozilla/dom/DOMTypes.h"
+#include "mozilla/dom/FetchEventOpChild.h"
+#include "mozilla/dom/InternalHeaders.h"
+#include "mozilla/dom/InternalRequest.h"
+#include "mozilla/dom/ReferrerInfo.h"
+#include "mozilla/dom/RemoteWorkerControllerChild.h"
+#include "mozilla/dom/RemoteWorkerManager.h" // RemoteWorkerManager::GetRemoteType
+#include "mozilla/dom/ServiceWorkerBinding.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/net/CookieJarSettings.h"
+#include "mozilla/RemoteLazyInputStreamStorage.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+
+ServiceWorkerPrivateImpl::RAIIActorPtrHolder::RAIIActorPtrHolder(
+ already_AddRefed<RemoteWorkerControllerChild> aActor)
+ : mActor(aActor) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(mActor->Manager());
+}
+
+ServiceWorkerPrivateImpl::RAIIActorPtrHolder::~RAIIActorPtrHolder() {
+ AssertIsOnMainThread();
+
+ mDestructorPromiseHolder.ResolveIfExists(true, __func__);
+
+ mActor->MaybeSendDelete();
+}
+
+RemoteWorkerControllerChild*
+ServiceWorkerPrivateImpl::RAIIActorPtrHolder::operator->() const {
+ AssertIsOnMainThread();
+
+ return get();
+}
+
+RemoteWorkerControllerChild* ServiceWorkerPrivateImpl::RAIIActorPtrHolder::get()
+ const {
+ AssertIsOnMainThread();
+
+ return mActor.get();
+}
+
+RefPtr<GenericPromise>
+ServiceWorkerPrivateImpl::RAIIActorPtrHolder::OnDestructor() {
+ AssertIsOnMainThread();
+
+ return mDestructorPromiseHolder.Ensure(__func__);
+}
+
+ServiceWorkerPrivateImpl::ServiceWorkerPrivateImpl(
+ RefPtr<ServiceWorkerPrivate> aOuter)
+ : mOuter(std::move(aOuter)) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(WorkerIsDead());
+}
+
+nsresult ServiceWorkerPrivateImpl::Initialize() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(mOuter->mInfo);
+
+ nsCOMPtr<nsIPrincipal> principal = mOuter->mInfo->Principal();
+
+ nsCOMPtr<nsIURI> uri;
+ auto* basePrin = BasePrincipal::Cast(principal);
+ nsresult rv = basePrin->GetURI(getter_AddRefs(uri));
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (NS_WARN_IF(!uri)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ URIParams baseScriptURL;
+ SerializeURI(uri, baseScriptURL);
+
+ nsString id;
+ rv = mOuter->mInfo->GetId(id);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ PrincipalInfo principalInfo;
+ rv = PrincipalToPrincipalInfo(principal, &principalInfo);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+
+ if (NS_WARN_IF(!swm)) {
+ return NS_ERROR_DOM_ABORT_ERR;
+ }
+
+ RefPtr<ServiceWorkerRegistrationInfo> regInfo =
+ swm->GetRegistration(principal, mOuter->mInfo->Scope());
+
+ if (NS_WARN_IF(!regInfo)) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
+ net::CookieJarSettings::Create();
+ MOZ_ASSERT(cookieJarSettings);
+
+ net::CookieJarSettings::Cast(cookieJarSettings)->SetPartitionKey(uri);
+
+ net::CookieJarSettingsArgs cjsData;
+ net::CookieJarSettings::Cast(cookieJarSettings)->Serialize(cjsData);
+
+ nsCOMPtr<nsIPrincipal> partitionedPrincipal;
+ rv = StoragePrincipalHelper::CreatePartitionedPrincipalForServiceWorker(
+ principal, cookieJarSettings, getter_AddRefs(partitionedPrincipal));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ PrincipalInfo partitionedPrincipalInfo;
+ rv =
+ PrincipalToPrincipalInfo(partitionedPrincipal, &partitionedPrincipalInfo);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ StorageAccess storageAccess =
+ StorageAllowedForServiceWorker(principal, cookieJarSettings);
+
+ ServiceWorkerData serviceWorkerData;
+ serviceWorkerData.cacheName() = mOuter->mInfo->CacheName();
+ serviceWorkerData.loadFlags() =
+ static_cast<uint32_t>(mOuter->mInfo->GetImportsLoadFlags() |
+ nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
+ serviceWorkerData.id() = std::move(id);
+
+ nsAutoCString domain;
+ rv = uri->GetHost(domain);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ auto remoteType = RemoteWorkerManager::GetRemoteType(
+ principal, WorkerType::WorkerTypeService);
+ if (NS_WARN_IF(remoteType.isErr())) {
+ return remoteType.unwrapErr();
+ }
+
+ mRemoteWorkerData = RemoteWorkerData(
+ NS_ConvertUTF8toUTF16(mOuter->mInfo->ScriptSpec()), baseScriptURL,
+ baseScriptURL, /* name */ VoidString(),
+ /* loading principal */ principalInfo, principalInfo,
+ partitionedPrincipalInfo,
+ /* useRegularPrincipal */ true,
+
+ // ServiceWorkers run as first-party, no storage-access permission needed.
+ /* hasStorageAccessPermissionGranted */ false,
+
+ cjsData, domain,
+ /* isSecureContext */ true,
+ /* clientInfo*/ Nothing(),
+
+ // The RemoteWorkerData CTOR doesn't allow to set the referrerInfo via
+ // already_AddRefed<>. Let's set it to null.
+ /* referrerInfo */ nullptr,
+
+ storageAccess, std::move(serviceWorkerData), regInfo->AgentClusterId(),
+ remoteType.unwrap());
+
+ mRemoteWorkerData.referrerInfo() = MakeAndAddRef<ReferrerInfo>();
+
+ // This fills in the rest of mRemoteWorkerData.serviceWorkerData().
+ RefreshRemoteWorkerData(regInfo);
+
+ return NS_OK;
+}
+
+RefPtr<GenericPromise> ServiceWorkerPrivateImpl::SetSkipWaitingFlag() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(mOuter->mInfo);
+
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+
+ if (!swm) {
+ return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ }
+
+ RefPtr<ServiceWorkerRegistrationInfo> regInfo =
+ swm->GetRegistration(mOuter->mInfo->Principal(), mOuter->mInfo->Scope());
+
+ if (!regInfo) {
+ return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ }
+
+ mOuter->mInfo->SetSkipWaitingFlag();
+
+ RefPtr<GenericPromise::Private> promise =
+ new GenericPromise::Private(__func__);
+
+ regInfo->TryToActivateAsync([promise] { promise->Resolve(true, __func__); });
+
+ return promise;
+}
+
+void ServiceWorkerPrivateImpl::RefreshRemoteWorkerData(
+ const RefPtr<ServiceWorkerRegistrationInfo>& aRegistration) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(mOuter->mInfo);
+
+ ServiceWorkerData& serviceWorkerData =
+ mRemoteWorkerData.serviceWorkerData().get_ServiceWorkerData();
+ serviceWorkerData.descriptor() = mOuter->mInfo->Descriptor().ToIPC();
+ serviceWorkerData.registrationDescriptor() =
+ aRegistration->Descriptor().ToIPC();
+}
+
+nsresult ServiceWorkerPrivateImpl::SpawnWorkerIfNeeded() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(mOuter->mInfo);
+
+ if (mControllerChild) {
+ mOuter->RenewKeepAliveToken(ServiceWorkerPrivate::WakeUpReason::Unknown);
+ return NS_OK;
+ }
+
+ PBackgroundChild* bgChild = BackgroundChild::GetForCurrentThread();
+
+ if (NS_WARN_IF(!bgChild)) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+
+ if (NS_WARN_IF(!swm)) {
+ return NS_ERROR_DOM_ABORT_ERR;
+ }
+
+ RefPtr<ServiceWorkerRegistrationInfo> regInfo =
+ swm->GetRegistration(mOuter->mInfo->Principal(), mOuter->mInfo->Scope());
+
+ if (NS_WARN_IF(!regInfo)) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ RefreshRemoteWorkerData(regInfo);
+
+ RefPtr<RemoteWorkerControllerChild> controllerChild =
+ new RemoteWorkerControllerChild(this);
+
+ if (NS_WARN_IF(!bgChild->SendPRemoteWorkerControllerConstructor(
+ controllerChild, mRemoteWorkerData))) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ /**
+ * Manutally `AddRef()` because `DeallocPRemoteWorkerControllerChild()`
+ * calls `Release()` and the `AllocPRemoteWorkerControllerChild()` function
+ * is not called.
+ */
+ // NOLINTNEXTLINE(readability-redundant-smartptr-get)
+ controllerChild.get()->AddRef();
+
+ mControllerChild = new RAIIActorPtrHolder(controllerChild.forget());
+
+ return NS_OK;
+}
+
+ServiceWorkerPrivateImpl::~ServiceWorkerPrivateImpl() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(!mOuter);
+ MOZ_ASSERT(WorkerIsDead());
+}
+
+nsresult ServiceWorkerPrivateImpl::SendMessageEvent(
+ RefPtr<ServiceWorkerCloneData>&& aData,
+ const ClientInfoAndState& aClientInfoAndState) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(aData);
+
+ auto scopeExit = MakeScopeExit([&] { Shutdown(); });
+
+ PBackgroundChild* bgChild = BackgroundChild::GetForCurrentThread();
+
+ if (NS_WARN_IF(!bgChild)) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ ServiceWorkerMessageEventOpArgs args;
+ args.clientInfoAndState() = aClientInfoAndState;
+ if (!aData->BuildClonedMessageDataForBackgroundChild(bgChild,
+ args.clonedData())) {
+ return NS_ERROR_DOM_DATA_CLONE_ERR;
+ }
+
+ scopeExit.release();
+
+ return ExecServiceWorkerOp(
+ std::move(args), [](ServiceWorkerOpResult&& aResult) {
+ MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
+ });
+}
+
+nsresult ServiceWorkerPrivateImpl::CheckScriptEvaluation(
+ RefPtr<LifeCycleEventCallback> aCallback) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(aCallback);
+
+ RefPtr<ServiceWorkerPrivateImpl> self = this;
+
+ /**
+ * We need to capture the actor associated with the current Service Worker so
+ * we can terminate it if script evaluation failed.
+ */
+ nsresult rv = SpawnWorkerIfNeeded();
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aCallback->SetResult(false);
+ aCallback->Run();
+
+ return rv;
+ }
+
+ MOZ_ASSERT(mControllerChild);
+
+ RefPtr<RAIIActorPtrHolder> holder = mControllerChild;
+
+ return ExecServiceWorkerOp(
+ ServiceWorkerCheckScriptEvaluationOpArgs(),
+ [self = std::move(self), holder = std::move(holder),
+ callback = aCallback](ServiceWorkerOpResult&& aResult) mutable {
+ if (aResult.type() == ServiceWorkerOpResult::
+ TServiceWorkerCheckScriptEvaluationOpResult) {
+ auto& result =
+ aResult.get_ServiceWorkerCheckScriptEvaluationOpResult();
+
+ if (result.workerScriptExecutedSuccessfully()) {
+ if (self->mOuter) {
+ self->mOuter->SetHandlesFetch(result.fetchHandlerWasAdded());
+ }
+
+ Unused << NS_WARN_IF(!self->mOuter);
+
+ callback->SetResult(result.workerScriptExecutedSuccessfully());
+ callback->Run();
+
+ return;
+ }
+ }
+
+ /**
+ * If script evaluation failed, first terminate the Service Worker
+ * before invoking the callback.
+ */
+ MOZ_ASSERT_IF(aResult.type() == ServiceWorkerOpResult::Tnsresult,
+ NS_FAILED(aResult.get_nsresult()));
+
+ // If a termination operation was already issued using `holder`...
+ if (self->mControllerChild != holder) {
+ holder->OnDestructor()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [callback = std::move(callback)](
+ const GenericPromise::ResolveOrRejectValue&) {
+ callback->SetResult(false);
+ callback->Run();
+ });
+
+ return;
+ }
+
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ MOZ_ASSERT(swm);
+
+ auto shutdownStateId = swm->MaybeInitServiceWorkerShutdownProgress();
+
+ RefPtr<GenericNonExclusivePromise> promise =
+ self->ShutdownInternal(shutdownStateId);
+
+ swm->BlockShutdownOn(promise, shutdownStateId);
+
+ promise->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [callback = std::move(callback)](
+ const GenericNonExclusivePromise::ResolveOrRejectValue&) {
+ callback->SetResult(false);
+ callback->Run();
+ });
+ },
+ [callback = aCallback] {
+ callback->SetResult(false);
+ callback->Run();
+ });
+}
+
+nsresult ServiceWorkerPrivateImpl::SendLifeCycleEvent(
+ const nsAString& aEventName, RefPtr<LifeCycleEventCallback> aCallback) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(aCallback);
+
+ return ExecServiceWorkerOp(
+ ServiceWorkerLifeCycleEventOpArgs(nsString(aEventName)),
+ [callback = aCallback](ServiceWorkerOpResult&& aResult) {
+ MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
+
+ callback->SetResult(NS_SUCCEEDED(aResult.get_nsresult()));
+ callback->Run();
+ },
+ [callback = aCallback] {
+ callback->SetResult(false);
+ callback->Run();
+ });
+}
+
+nsresult ServiceWorkerPrivateImpl::SendPushEvent(
+ RefPtr<ServiceWorkerRegistrationInfo> aRegistration,
+ const nsAString& aMessageId, const Maybe<nsTArray<uint8_t>>& aData) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(aRegistration);
+
+ ServiceWorkerPushEventOpArgs args;
+ args.messageId() = nsString(aMessageId);
+
+ if (aData) {
+ args.data() = aData.ref();
+ } else {
+ args.data() = void_t();
+ }
+
+ if (mOuter->mInfo->State() == ServiceWorkerState::Activating) {
+ UniquePtr<PendingFunctionalEvent> pendingEvent =
+ MakeUnique<PendingPushEvent>(this, std::move(aRegistration),
+ std::move(args));
+
+ mPendingFunctionalEvents.AppendElement(std::move(pendingEvent));
+
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(mOuter->mInfo->State() == ServiceWorkerState::Activated);
+
+ return SendPushEventInternal(std::move(aRegistration), std::move(args));
+}
+
+nsresult ServiceWorkerPrivateImpl::SendPushEventInternal(
+ RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
+ ServiceWorkerPushEventOpArgs&& aArgs) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(aRegistration);
+
+ return ExecServiceWorkerOp(
+ std::move(aArgs),
+ [registration = aRegistration](ServiceWorkerOpResult&& aResult) {
+ MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
+
+ registration->MaybeScheduleTimeCheckAndUpdate();
+ },
+ [registration = aRegistration]() {
+ registration->MaybeScheduleTimeCheckAndUpdate();
+ });
+}
+
+nsresult ServiceWorkerPrivateImpl::SendPushSubscriptionChangeEvent() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+
+ return ExecServiceWorkerOp(
+ ServiceWorkerPushSubscriptionChangeEventOpArgs(),
+ [](ServiceWorkerOpResult&& aResult) {
+ MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
+ });
+}
+
+nsresult ServiceWorkerPrivateImpl::SendNotificationEvent(
+ const nsAString& aEventName, const nsAString& aID, const nsAString& aTitle,
+ const nsAString& aDir, const nsAString& aLang, const nsAString& aBody,
+ const nsAString& aTag, const nsAString& aIcon, const nsAString& aData,
+ const nsAString& aBehavior, const nsAString& aScope,
+ uint32_t aDisableOpenClickDelay) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+
+ ServiceWorkerNotificationEventOpArgs args;
+ args.eventName() = nsString(aEventName);
+ args.id() = nsString(aID);
+ args.title() = nsString(aTitle);
+ args.dir() = nsString(aDir);
+ args.lang() = nsString(aLang);
+ args.body() = nsString(aBody);
+ args.tag() = nsString(aTag);
+ args.icon() = nsString(aIcon);
+ args.data() = nsString(aData);
+ args.behavior() = nsString(aBehavior);
+ args.scope() = nsString(aScope);
+ args.disableOpenClickDelay() = aDisableOpenClickDelay;
+
+ return ExecServiceWorkerOp(
+ std::move(args), [](ServiceWorkerOpResult&& aResult) {
+ MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
+ });
+}
+
+ServiceWorkerPrivateImpl::PendingFunctionalEvent::PendingFunctionalEvent(
+ ServiceWorkerPrivateImpl* aOwner,
+ RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration)
+ : mOwner(aOwner), mRegistration(std::move(aRegistration)) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOwner);
+ MOZ_ASSERT(mOwner->mOuter);
+ MOZ_ASSERT(mOwner->mOuter->mInfo);
+ MOZ_ASSERT(mOwner->mOuter->mInfo->State() == ServiceWorkerState::Activating);
+ MOZ_ASSERT(mRegistration);
+}
+
+ServiceWorkerPrivateImpl::PendingFunctionalEvent::~PendingFunctionalEvent() {
+ AssertIsOnMainThread();
+}
+
+ServiceWorkerPrivateImpl::PendingPushEvent::PendingPushEvent(
+ ServiceWorkerPrivateImpl* aOwner,
+ RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
+ ServiceWorkerPushEventOpArgs&& aArgs)
+ : PendingFunctionalEvent(aOwner, std::move(aRegistration)),
+ mArgs(std::move(aArgs)) {
+ AssertIsOnMainThread();
+}
+
+nsresult ServiceWorkerPrivateImpl::PendingPushEvent::Send() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOwner->mOuter);
+ MOZ_ASSERT(mOwner->mOuter->mInfo);
+
+ return mOwner->SendPushEventInternal(std::move(mRegistration),
+ std::move(mArgs));
+}
+
+ServiceWorkerPrivateImpl::PendingFetchEvent::PendingFetchEvent(
+ ServiceWorkerPrivateImpl* aOwner,
+ RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
+ ServiceWorkerFetchEventOpArgs&& aArgs,
+ nsCOMPtr<nsIInterceptedChannel>&& aChannel)
+ : PendingFunctionalEvent(aOwner, std::move(aRegistration)),
+ mArgs(std::move(aArgs)),
+ mChannel(std::move(aChannel)) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mChannel);
+}
+
+nsresult ServiceWorkerPrivateImpl::PendingFetchEvent::Send() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOwner->mOuter);
+ MOZ_ASSERT(mOwner->mOuter->mInfo);
+
+ return mOwner->SendFetchEventInternal(std::move(mRegistration),
+ std::move(mArgs), std::move(mChannel));
+}
+
+ServiceWorkerPrivateImpl::PendingFetchEvent::~PendingFetchEvent() {
+ AssertIsOnMainThread();
+
+ if (NS_WARN_IF(mChannel)) {
+ mChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED);
+ }
+}
+
+namespace {
+
+class HeaderFiller final : public nsIHttpHeaderVisitor {
+ public:
+ NS_DECL_ISUPPORTS
+
+ explicit HeaderFiller(HeadersGuardEnum aGuard)
+ : mInternalHeaders(new InternalHeaders(aGuard)) {
+ MOZ_ASSERT(mInternalHeaders);
+ }
+
+ NS_IMETHOD
+ VisitHeader(const nsACString& aHeader, const nsACString& aValue) override {
+ ErrorResult result;
+ mInternalHeaders->Append(aHeader, aValue, result);
+
+ if (NS_WARN_IF(result.Failed())) {
+ return result.StealNSResult();
+ }
+
+ return NS_OK;
+ }
+
+ RefPtr<InternalHeaders> Extract() {
+ return RefPtr<InternalHeaders>(std::move(mInternalHeaders));
+ }
+
+ private:
+ ~HeaderFiller() = default;
+
+ RefPtr<InternalHeaders> mInternalHeaders;
+};
+
+NS_IMPL_ISUPPORTS(HeaderFiller, nsIHttpHeaderVisitor)
+
+Result<IPCInternalRequest, nsresult> GetIPCInternalRequest(
+ nsIInterceptedChannel* aChannel) {
+ AssertIsOnMainThread();
+
+ nsCOMPtr<nsIURI> uri;
+ MOZ_TRY(aChannel->GetSecureUpgradedChannelURI(getter_AddRefs(uri)));
+
+ nsCOMPtr<nsIURI> uriNoFragment;
+ MOZ_TRY(NS_GetURIWithoutRef(uri, getter_AddRefs(uriNoFragment)));
+
+ nsCOMPtr<nsIChannel> underlyingChannel;
+ MOZ_TRY(aChannel->GetChannel(getter_AddRefs(underlyingChannel)));
+
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(underlyingChannel);
+ MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
+
+ nsCOMPtr<nsIHttpChannelInternal> internalChannel =
+ do_QueryInterface(httpChannel);
+ NS_ENSURE_TRUE(internalChannel, Err(NS_ERROR_NOT_AVAILABLE));
+
+ nsCOMPtr<nsICacheInfoChannel> cacheInfoChannel =
+ do_QueryInterface(underlyingChannel);
+
+ nsAutoCString spec;
+ MOZ_TRY(uriNoFragment->GetSpec(spec));
+
+ nsAutoCString fragment;
+ MOZ_TRY(uri->GetRef(fragment));
+
+ nsAutoCString method;
+ MOZ_TRY(httpChannel->GetRequestMethod(method));
+
+ // This is safe due to static_asserts in ServiceWorkerManager.cpp
+ uint32_t cacheModeInt;
+ MOZ_ALWAYS_SUCCEEDS(internalChannel->GetFetchCacheMode(&cacheModeInt));
+ RequestCache cacheMode = static_cast<RequestCache>(cacheModeInt);
+
+ RequestMode requestMode =
+ InternalRequest::MapChannelToRequestMode(underlyingChannel);
+
+ // This is safe due to static_asserts in ServiceWorkerManager.cpp
+ uint32_t redirectMode;
+ MOZ_ALWAYS_SUCCEEDS(internalChannel->GetRedirectMode(&redirectMode));
+ RequestRedirect requestRedirect = static_cast<RequestRedirect>(redirectMode);
+
+ RequestCredentials requestCredentials =
+ InternalRequest::MapChannelToRequestCredentials(underlyingChannel);
+
+ nsAutoString referrer;
+ ReferrerPolicy referrerPolicy = ReferrerPolicy::_empty;
+
+ nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
+ if (referrerInfo) {
+ referrerPolicy = referrerInfo->ReferrerPolicy();
+ Unused << referrerInfo->GetComputedReferrerSpec(referrer);
+ }
+
+ uint32_t loadFlags;
+ MOZ_TRY(underlyingChannel->GetLoadFlags(&loadFlags));
+
+ nsCOMPtr<nsILoadInfo> loadInfo = underlyingChannel->LoadInfo();
+ nsContentPolicyType contentPolicyType = loadInfo->InternalContentPolicyType();
+
+ nsAutoString integrity;
+ MOZ_TRY(internalChannel->GetIntegrityMetadata(integrity));
+
+ RefPtr<HeaderFiller> headerFiller =
+ MakeRefPtr<HeaderFiller>(HeadersGuardEnum::Request);
+ MOZ_TRY(httpChannel->VisitNonDefaultRequestHeaders(headerFiller));
+
+ RefPtr<InternalHeaders> internalHeaders = headerFiller->Extract();
+
+ ErrorResult result;
+ internalHeaders->SetGuard(HeadersGuardEnum::Immutable, result);
+ if (NS_WARN_IF(result.Failed())) {
+ return Err(result.StealNSResult());
+ }
+
+ nsTArray<HeadersEntry> ipcHeaders;
+ HeadersGuardEnum ipcHeadersGuard;
+ internalHeaders->ToIPC(ipcHeaders, ipcHeadersGuard);
+
+ nsAutoCString alternativeDataType;
+ if (cacheInfoChannel &&
+ !cacheInfoChannel->PreferredAlternativeDataTypes().IsEmpty()) {
+ // TODO: the internal request probably needs all the preferred types.
+ alternativeDataType.Assign(
+ cacheInfoChannel->PreferredAlternativeDataTypes()[0].type());
+ }
+
+ Maybe<PrincipalInfo> principalInfo;
+
+ if (loadInfo->TriggeringPrincipal()) {
+ principalInfo.emplace();
+ MOZ_ALWAYS_SUCCEEDS(PrincipalToPrincipalInfo(
+ loadInfo->TriggeringPrincipal(), principalInfo.ptr()));
+ }
+
+ // Note: all the arguments are copied rather than moved, which would be more
+ // efficient, because there's no move-friendly constructor generated.
+ return IPCInternalRequest(
+ method, {spec}, ipcHeadersGuard, ipcHeaders, Nothing(), -1,
+ alternativeDataType, contentPolicyType, referrer, referrerPolicy,
+ requestMode, requestCredentials, cacheMode, requestRedirect, integrity,
+ fragment, principalInfo);
+}
+
+nsresult MaybeStoreStreamForBackgroundThread(nsIInterceptedChannel* aChannel,
+ IPCInternalRequest& aIPCRequest) {
+ nsCOMPtr<nsIChannel> channel;
+ MOZ_ALWAYS_SUCCEEDS(aChannel->GetChannel(getter_AddRefs(channel)));
+
+ Maybe<BodyStreamVariant> body;
+ int64_t bodySize = -1;
+ nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel);
+
+ if (uploadChannel) {
+ nsCOMPtr<nsIInputStream> uploadStream;
+ MOZ_TRY(uploadChannel->CloneUploadStream(&aIPCRequest.bodySize(),
+ getter_AddRefs(uploadStream)));
+
+ if (uploadStream) {
+ Maybe<BodyStreamVariant>& body = aIPCRequest.body();
+ body.emplace(ParentToParentStream());
+
+ MOZ_TRY(nsContentUtils::GenerateUUIDInPlace(
+ body->get_ParentToParentStream().uuid()));
+
+ auto storageOrErr = RemoteLazyInputStreamStorage::Get();
+ if (NS_WARN_IF(storageOrErr.isErr())) {
+ return storageOrErr.unwrapErr();
+ }
+
+ auto storage = storageOrErr.unwrap();
+ storage->AddStream(uploadStream, body->get_ParentToParentStream().uuid(),
+ bodySize, 0);
+ }
+ }
+
+ return NS_OK;
+}
+
+} // anonymous namespace
+
+nsresult ServiceWorkerPrivateImpl::SendFetchEvent(
+ RefPtr<ServiceWorkerRegistrationInfo> aRegistration,
+ nsCOMPtr<nsIInterceptedChannel> aChannel, const nsAString& aClientId,
+ const nsAString& aResultingClientId) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(mOuter->mInfo);
+ MOZ_ASSERT(aRegistration);
+ MOZ_ASSERT(aChannel);
+
+ auto scopeExit = MakeScopeExit([&] {
+ aChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED);
+ Shutdown();
+ });
+
+ nsCOMPtr<nsIChannel> channel;
+ MOZ_TRY(aChannel->GetChannel(getter_AddRefs(channel)));
+
+ IPCInternalRequest request;
+ MOZ_TRY_VAR(request, GetIPCInternalRequest(aChannel));
+
+ scopeExit.release();
+
+ ServiceWorkerFetchEventOpArgs args(
+ mOuter->mInfo->ScriptSpec(), std::move(request), nsString(aClientId),
+ nsString(aResultingClientId),
+ nsContentUtils::IsNonSubresourceRequest(channel));
+
+ if (mOuter->mInfo->State() == ServiceWorkerState::Activating) {
+ UniquePtr<PendingFunctionalEvent> pendingEvent =
+ MakeUnique<PendingFetchEvent>(this, std::move(aRegistration),
+ std::move(args), std::move(aChannel));
+
+ mPendingFunctionalEvents.AppendElement(std::move(pendingEvent));
+
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(mOuter->mInfo->State() == ServiceWorkerState::Activated);
+
+ return SendFetchEventInternal(std::move(aRegistration), std::move(args),
+ std::move(aChannel));
+}
+
+nsresult ServiceWorkerPrivateImpl::SendFetchEventInternal(
+ RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
+ ServiceWorkerFetchEventOpArgs&& aArgs,
+ nsCOMPtr<nsIInterceptedChannel>&& aChannel) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+
+ auto scopeExit = MakeScopeExit([&] { Shutdown(); });
+
+ if (NS_WARN_IF(!mOuter->mInfo)) {
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ }
+
+ MOZ_TRY(SpawnWorkerIfNeeded());
+ MOZ_TRY(
+ MaybeStoreStreamForBackgroundThread(aChannel, aArgs.internalRequest()));
+
+ scopeExit.release();
+
+ MOZ_ASSERT(mControllerChild);
+
+ RefPtr<RAIIActorPtrHolder> holder = mControllerChild;
+
+ FetchEventOpChild::SendFetchEvent(
+ mControllerChild->get(), std::move(aArgs), std::move(aChannel),
+ std::move(aRegistration), mOuter->CreateEventKeepAliveToken())
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [holder = std::move(holder)](
+ const GenericPromise::ResolveOrRejectValue& aResult) {
+ Unused << NS_WARN_IF(aResult.IsReject());
+ });
+
+ return NS_OK;
+}
+
+void ServiceWorkerPrivateImpl::TerminateWorker() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+
+ mOuter->mIdleWorkerTimer->Cancel();
+ mOuter->mIdleKeepAliveToken = nullptr;
+
+ Shutdown();
+}
+
+void ServiceWorkerPrivateImpl::Shutdown() {
+ AssertIsOnMainThread();
+
+ if (!WorkerIsDead()) {
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+
+ MOZ_ASSERT(swm,
+ "All Service Workers should start shutting down before the "
+ "ServiceWorkerManager does!");
+
+ auto shutdownStateId = swm->MaybeInitServiceWorkerShutdownProgress();
+
+ RefPtr<GenericNonExclusivePromise> promise =
+ ShutdownInternal(shutdownStateId);
+ swm->BlockShutdownOn(promise, shutdownStateId);
+ }
+
+ MOZ_ASSERT(WorkerIsDead());
+}
+
+RefPtr<GenericNonExclusivePromise> ServiceWorkerPrivateImpl::ShutdownInternal(
+ uint32_t aShutdownStateId) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mControllerChild);
+
+ mPendingFunctionalEvents.Clear();
+
+ mControllerChild->get()->RevokeObserver(this);
+
+ if (StaticPrefs::dom_serviceWorkers_testing_enabled()) {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(nullptr, "service-worker-shutdown", nullptr);
+ }
+ }
+
+ RefPtr<GenericNonExclusivePromise::Private> promise =
+ new GenericNonExclusivePromise::Private(__func__);
+
+ Unused << ExecServiceWorkerOp(
+ ServiceWorkerTerminateWorkerOpArgs(aShutdownStateId),
+ [promise](ServiceWorkerOpResult&& aResult) {
+ MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
+ promise->Resolve(true, __func__);
+ },
+ [promise]() { promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); });
+
+ /**
+ * After dispatching a termination operation, no new operations should
+ * be routed through this actor anymore.
+ */
+ mControllerChild = nullptr;
+
+ return promise;
+}
+
+void ServiceWorkerPrivateImpl::UpdateState(ServiceWorkerState aState) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+
+ if (WorkerIsDead()) {
+ return;
+ }
+
+ nsresult rv = ExecServiceWorkerOp(
+ ServiceWorkerUpdateStateOpArgs(aState),
+ [](ServiceWorkerOpResult&& aResult) {
+ MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
+ });
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ Shutdown();
+ return;
+ }
+
+ if (aState != ServiceWorkerState::Activated) {
+ return;
+ }
+
+ for (auto& event : mPendingFunctionalEvents) {
+ Unused << NS_WARN_IF(NS_FAILED(event->Send()));
+ }
+
+ mPendingFunctionalEvents.Clear();
+}
+
+void ServiceWorkerPrivateImpl::NoteDeadOuter() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+
+ Shutdown();
+ mOuter = nullptr;
+}
+
+void ServiceWorkerPrivateImpl::CreationFailed() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(mControllerChild);
+
+ Shutdown();
+}
+
+void ServiceWorkerPrivateImpl::CreationSucceeded() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(mControllerChild);
+
+ mOuter->RenewKeepAliveToken(ServiceWorkerPrivate::WakeUpReason::Unknown);
+}
+
+void ServiceWorkerPrivateImpl::ErrorReceived(const ErrorValue& aError) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(mOuter->mInfo);
+ MOZ_ASSERT(mControllerChild);
+
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ MOZ_ASSERT(swm);
+
+ ServiceWorkerInfo* info = mOuter->mInfo;
+
+ swm->HandleError(nullptr, info->Principal(), info->Scope(),
+ NS_ConvertUTF8toUTF16(info->ScriptSpec()), u""_ns, u""_ns,
+ u""_ns, 0, 0, nsIScriptError::errorFlag, JSEXN_ERR);
+}
+
+void ServiceWorkerPrivateImpl::Terminated() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(mControllerChild);
+
+ Shutdown();
+}
+
+bool ServiceWorkerPrivateImpl::WorkerIsDead() const {
+ AssertIsOnMainThread();
+
+ return !mControllerChild;
+}
+
+nsresult ServiceWorkerPrivateImpl::ExecServiceWorkerOp(
+ ServiceWorkerOpArgs&& aArgs,
+ std::function<void(ServiceWorkerOpResult&&)>&& aSuccessCallback,
+ std::function<void()>&& aFailureCallback) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mOuter);
+ MOZ_ASSERT(
+ aArgs.type() != ServiceWorkerOpArgs::TServiceWorkerFetchEventOpArgs,
+ "FetchEvent operations should be sent through FetchEventOp(Proxy) "
+ "actors!");
+ MOZ_ASSERT(aSuccessCallback);
+
+ nsresult rv = SpawnWorkerIfNeeded();
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aFailureCallback();
+ return rv;
+ }
+
+ MOZ_ASSERT(mControllerChild);
+
+ RefPtr<ServiceWorkerPrivateImpl> self = this;
+ RefPtr<RAIIActorPtrHolder> holder = mControllerChild;
+ RefPtr<KeepAliveToken> token =
+ aArgs.type() == ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs
+ ? nullptr
+ : mOuter->CreateEventKeepAliveToken();
+
+ /**
+ * NOTE: moving `aArgs` won't do anything until IPDL `SendMethod()` methods
+ * can accept rvalue references rather than just const references.
+ */
+ mControllerChild->get()->SendExecServiceWorkerOp(aArgs)->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self = std::move(self), holder = std::move(holder),
+ token = std::move(token), onSuccess = std::move(aSuccessCallback),
+ onFailure = std::move(aFailureCallback)](
+ PRemoteWorkerControllerChild::ExecServiceWorkerOpPromise::
+ ResolveOrRejectValue&& aResult) {
+ if (NS_WARN_IF(aResult.IsReject())) {
+ onFailure();
+ return;
+ }
+
+ onSuccess(std::move(aResult.ResolveValue()));
+ });
+
+ return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla