/* -*- 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 "ServiceWorkerContainer.h" #include "nsContentPolicyUtils.h" #include "nsContentSecurityManager.h" #include "nsContentUtils.h" #include "mozilla/dom/Document.h" #include "nsIServiceWorkerManager.h" #include "nsIScriptError.h" #include "nsThreadUtils.h" #include "nsIURL.h" #include "nsNetUtil.h" #include "nsPIDOMWindow.h" #include "mozilla/Services.h" #include "mozilla/StaticPrefs.h" #include "nsCycleCollectionParticipant.h" #include "nsServiceManagerUtils.h" #include "mozilla/LoadInfo.h" #include "mozilla/StorageAccess.h" #include "mozilla/dom/ClientIPCTypes.h" #include "mozilla/dom/DOMMozPromiseRequestHolder.h" #include "mozilla/dom/MessageEvent.h" #include "mozilla/dom/MessageEventBinding.h" #include "mozilla/dom/Navigator.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/ServiceWorker.h" #include "mozilla/dom/ServiceWorkerContainerBinding.h" #include "mozilla/dom/ServiceWorkerManager.h" #include "mozilla/dom/ipc/StructuredCloneData.h" #include "RemoteServiceWorkerContainerImpl.h" #include "ServiceWorker.h" #include "ServiceWorkerContainerImpl.h" #include "ServiceWorkerRegistration.h" #include "ServiceWorkerUtils.h" // This is defined to something else on Windows #ifdef DispatchMessage # undef DispatchMessage #endif namespace mozilla { namespace dom { NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerContainer) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) NS_IMPL_ADDREF_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper, mControllerWorker, mReadyPromise) namespace { bool IsInPrivateBrowsing(JSContext* const aCx) { if (const nsCOMPtr global = xpc::CurrentNativeGlobal(aCx)) { if (const nsCOMPtr principal = global->PrincipalOrNull()) { return principal->GetPrivateBrowsingId() > 0; } } return false; } bool IsServiceWorkersTestingEnabledInWindow(JSObject* const aGlobal) { if (const nsCOMPtr innerWindow = Navigator::GetWindowFromGlobal(aGlobal)) { if (const nsCOMPtr outerWindow = innerWindow->GetOuterWindow()) { return outerWindow->GetServiceWorkersTestingEnabled(); } } return false; } } // namespace /* static */ bool ServiceWorkerContainer::IsEnabled(JSContext* aCx, JSObject* aGlobal) { MOZ_ASSERT(NS_IsMainThread()); JS::Rooted global(aCx, aGlobal); if (!StaticPrefs::dom_serviceWorkers_enabled()) { return false; } if (IsInPrivateBrowsing(aCx)) { return false; } if (IsSecureContextOrObjectIsFromSecureContext(aCx, global)) { return true; } const bool isTestingEnabledInWindow = IsServiceWorkersTestingEnabledInWindow(global); const bool isTestingEnabledByPref = StaticPrefs::dom_serviceWorkers_testing_enabled(); const bool isTestingEnabled = isTestingEnabledByPref || isTestingEnabledInWindow; return isTestingEnabled; } // static already_AddRefed ServiceWorkerContainer::Create( nsIGlobalObject* aGlobal) { RefPtr inner; if (ServiceWorkerParentInterceptEnabled()) { inner = new RemoteServiceWorkerContainerImpl(); } else { inner = new ServiceWorkerContainerImpl(); } NS_ENSURE_TRUE(inner, nullptr); RefPtr ref = new ServiceWorkerContainer(aGlobal, inner.forget()); return ref.forget(); } ServiceWorkerContainer::ServiceWorkerContainer( nsIGlobalObject* aGlobal, already_AddRefed aInner) : DOMEventTargetHelper(aGlobal), mInner(aInner) { mInner->AddContainer(this); Maybe controller = aGlobal->GetController(); if (controller.isSome()) { mControllerWorker = aGlobal->GetOrCreateServiceWorker(controller.ref()); } } ServiceWorkerContainer::~ServiceWorkerContainer() { mInner->RemoveContainer(this); } void ServiceWorkerContainer::DisconnectFromOwner() { mControllerWorker = nullptr; mReadyPromise = nullptr; DOMEventTargetHelper::DisconnectFromOwner(); } void ServiceWorkerContainer::ControllerChanged(ErrorResult& aRv) { nsCOMPtr go = GetParentObject(); if (!go) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } mControllerWorker = go->GetOrCreateServiceWorker(go->GetController().ref()); aRv = DispatchTrustedEvent(NS_LITERAL_STRING("controllerchange")); } using mozilla::dom::ipc::StructuredCloneData; // A ReceivedMessage represents a message sent via // Client.postMessage(). It is used as used both for queuing of // incoming messages and as an interface to DispatchMessage(). struct MOZ_HEAP_CLASS ServiceWorkerContainer::ReceivedMessage { explicit ReceivedMessage(const ClientPostMessageArgs& aArgs) : mServiceWorker(aArgs.serviceWorker()) { mClonedData.CopyFromClonedMessageDataForBackgroundChild(aArgs.clonedData()); } ServiceWorkerDescriptor mServiceWorker; StructuredCloneData mClonedData; NS_INLINE_DECL_REFCOUNTING(ReceivedMessage) private: ~ReceivedMessage() = default; }; void ServiceWorkerContainer::ReceiveMessage( const ClientPostMessageArgs& aArgs) { RefPtr message = new ReceivedMessage(aArgs); if (mMessagesStarted) { EnqueueReceivedMessageDispatch(message.forget()); } else { mPendingMessages.AppendElement(message.forget()); } } JSObject* ServiceWorkerContainer::WrapObject( JSContext* aCx, JS::Handle aGivenProto) { return ServiceWorkerContainer_Binding::Wrap(aCx, this, aGivenProto); } namespace { already_AddRefed GetBaseURIFromGlobal(nsIGlobalObject* aGlobal, ErrorResult& aRv) { // It would be nice not to require a window here, but right // now we don't have a great way to get the base URL just // from the nsIGlobalObject. nsCOMPtr window = do_QueryInterface(aGlobal); if (!window) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } Document* doc = window->GetExtantDoc(); if (!doc) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } nsCOMPtr baseURI = doc->GetDocBaseURI(); if (!baseURI) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } return baseURI.forget(); } } // anonymous namespace already_AddRefed ServiceWorkerContainer::Register( const nsAString& aScriptURL, const RegistrationOptions& aOptions, ErrorResult& aRv) { // Note, we can't use GetGlobalIfValid() from the start here. If we // hit a storage failure we want to log a message with the final // scope string we put together below. nsIGlobalObject* global = GetParentObject(); if (!global) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } Maybe clientInfo = global->GetClientInfo(); if (clientInfo.isNothing()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } nsCOMPtr baseURI = GetBaseURIFromGlobal(global, aRv); if (aRv.Failed()) { return nullptr; } nsresult rv; nsCOMPtr scriptURI; rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, baseURI); if (NS_WARN_IF(NS_FAILED(rv))) { aRv.ThrowTypeError(aScriptURL); return nullptr; } // In ServiceWorkerContainer.register() the scope argument is parsed against // different base URLs depending on whether it was passed or not. nsCOMPtr scopeURI; // Step 4. If none passed, parse against script's URL if (!aOptions.mScope.WasPassed()) { NS_NAMED_LITERAL_STRING(defaultScope, "./"); rv = NS_NewURI(getter_AddRefs(scopeURI), defaultScope, nullptr, scriptURI); if (NS_WARN_IF(NS_FAILED(rv))) { nsAutoCString spec; scriptURI->GetSpec(spec); NS_ConvertUTF8toUTF16 wSpec(spec); aRv.ThrowTypeError(defaultScope, wSpec); return nullptr; } } else { // Step 5. Parse against entry settings object's base URL. rv = NS_NewURI(getter_AddRefs(scopeURI), aOptions.mScope.Value(), nullptr, baseURI); if (NS_WARN_IF(NS_FAILED(rv))) { nsIURI* uri = baseURI ? baseURI : scriptURI; nsAutoCString spec; uri->GetSpec(spec); NS_ConvertUTF8toUTF16 wSpec(spec); aRv.ThrowTypeError(aOptions.mScope.Value(), wSpec); return nullptr; } } // Strip the any ref from both the script and scope URLs. nsCOMPtr cloneWithoutRef; aRv = NS_GetURIWithoutRef(scriptURI, getter_AddRefs(cloneWithoutRef)); if (aRv.Failed()) { return nullptr; } scriptURI = cloneWithoutRef.forget(); aRv = NS_GetURIWithoutRef(scopeURI, getter_AddRefs(cloneWithoutRef)); if (aRv.Failed()) { return nullptr; } scopeURI = cloneWithoutRef.forget(); aRv = ServiceWorkerScopeAndScriptAreValid(clientInfo.ref(), scopeURI, scriptURI); if (aRv.Failed()) { return nullptr; } nsCOMPtr window = do_QueryInterface(global); if (!window) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } Document* doc = window->GetExtantDoc(); if (!doc) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } // The next section of code executes an NS_CheckContentLoadPolicy() // check. This is necessary to enforce the CSP of the calling client. // Currently this requires an Document. Once bug 965637 lands we // should try to move this into ServiceWorkerScopeAndScriptAreValid() // using the ClientInfo instead of doing a window-specific check here. // See bug 1455077 for further investigation. nsCOMPtr secCheckLoadInfo = new mozilla::net::LoadInfo( doc->NodePrincipal(), // loading principal doc->NodePrincipal(), // triggering principal doc, // loading node nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER); // Check content policy. int16_t decision = nsIContentPolicy::ACCEPT; rv = NS_CheckContentLoadPolicy(scriptURI, secCheckLoadInfo, NS_LITERAL_CSTRING("application/javascript"), &decision); if (NS_FAILED(rv)) { aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); return nullptr; } if (NS_WARN_IF(decision != nsIContentPolicy::ACCEPT)) { aRv.Throw(NS_ERROR_CONTENT_BLOCKED); return nullptr; } // Get the string representation for both the script and scope since // we sanitized them above. nsCString cleanedScopeURL; aRv = scopeURI->GetSpec(cleanedScopeURL); if (aRv.Failed()) { return nullptr; } nsCString cleanedScriptURL; aRv = scriptURI->GetSpec(cleanedScriptURL); if (aRv.Failed()) { return nullptr; } // Verify that the global is valid and has permission to store // data. We perform this late so that we can report the final // scope URL in any error message. Unused << GetGlobalIfValid(aRv, [&](Document* aDoc) { AutoTArray param; CopyUTF8toUTF16(cleanedScopeURL, *param.AppendElement()); nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Service Workers"), aDoc, nsContentUtils::eDOM_PROPERTIES, "ServiceWorkerRegisterStorageError", param); }); window->NoteCalledRegisterForServiceWorkerScope(cleanedScopeURL); RefPtr outer = Promise::Create(global, aRv, Promise::ePropagateUserInteraction); if (aRv.Failed()) { return nullptr; } RefPtr self = this; mInner->Register( clientInfo.ref(), cleanedScopeURL, cleanedScriptURL, aOptions.mUpdateViaCache, [self, outer](const ServiceWorkerRegistrationDescriptor& aDesc) { ErrorResult rv; nsIGlobalObject* global = self->GetGlobalIfValid(rv); if (rv.Failed()) { outer->MaybeReject(rv); return; } RefPtr reg = global->GetOrCreateServiceWorkerRegistration(aDesc); outer->MaybeResolve(reg); }, [outer](ErrorResult& aRv) { outer->MaybeReject(aRv); }); return outer.forget(); } already_AddRefed ServiceWorkerContainer::GetController() { RefPtr ref = mControllerWorker; return ref.forget(); } already_AddRefed ServiceWorkerContainer::GetRegistrations( ErrorResult& aRv) { nsIGlobalObject* global = GetGlobalIfValid(aRv, [](Document* aDoc) { nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Service Workers"), aDoc, nsContentUtils::eDOM_PROPERTIES, "ServiceWorkerGetRegistrationStorageError"); }); if (aRv.Failed()) { return nullptr; } Maybe clientInfo = global->GetClientInfo(); if (clientInfo.isNothing()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } RefPtr outer = Promise::Create(global, aRv, Promise::ePropagateUserInteraction); if (aRv.Failed()) { return nullptr; } RefPtr self = this; mInner->GetRegistrations( clientInfo.ref(), [self, outer](const nsTArray& aDescList) { ErrorResult rv; nsIGlobalObject* global = self->GetGlobalIfValid(rv); if (rv.Failed()) { outer->MaybeReject(rv); return; } nsTArray> regList; for (auto& desc : aDescList) { RefPtr reg = global->GetOrCreateServiceWorkerRegistration(desc); if (reg) { regList.AppendElement(std::move(reg)); } } outer->MaybeResolve(regList); }, [self, outer](ErrorResult& aRv) { outer->MaybeReject(aRv); }); return outer.forget(); } void ServiceWorkerContainer::StartMessages() { while (!mPendingMessages.IsEmpty()) { EnqueueReceivedMessageDispatch(mPendingMessages.ElementAt(0)); mPendingMessages.RemoveElementAt(0); } mMessagesStarted = true; } already_AddRefed ServiceWorkerContainer::GetRegistration( const nsAString& aURL, ErrorResult& aRv) { nsIGlobalObject* global = GetGlobalIfValid(aRv, [](Document* aDoc) { nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Service Workers"), aDoc, nsContentUtils::eDOM_PROPERTIES, "ServiceWorkerGetRegistrationStorageError"); }); if (aRv.Failed()) { return nullptr; } Maybe clientInfo = global->GetClientInfo(); if (clientInfo.isNothing()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } nsCOMPtr baseURI = GetBaseURIFromGlobal(global, aRv); if (aRv.Failed()) { return nullptr; } nsCOMPtr uri; aRv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, baseURI); if (aRv.Failed()) { return nullptr; } nsCString spec; aRv = uri->GetSpec(spec); if (aRv.Failed()) { return nullptr; } RefPtr outer = Promise::Create(global, aRv, Promise::ePropagateUserInteraction); if (aRv.Failed()) { return nullptr; } RefPtr self = this; mInner->GetRegistration( clientInfo.ref(), spec, [self, outer](const ServiceWorkerRegistrationDescriptor& aDescriptor) { ErrorResult rv; nsIGlobalObject* global = self->GetGlobalIfValid(rv); if (rv.Failed()) { outer->MaybeReject(rv); return; } RefPtr reg = global->GetOrCreateServiceWorkerRegistration(aDescriptor); outer->MaybeResolve(reg); }, [self, outer](ErrorResult& aRv) { if (!aRv.Failed()) { Unused << self->GetGlobalIfValid(aRv); if (!aRv.Failed()) { outer->MaybeResolveWithUndefined(); return; } } outer->MaybeReject(aRv); }); return outer.forget(); } Promise* ServiceWorkerContainer::GetReady(ErrorResult& aRv) { if (mReadyPromise) { return mReadyPromise; } nsIGlobalObject* global = GetGlobalIfValid(aRv); if (aRv.Failed()) { return nullptr; } MOZ_DIAGNOSTIC_ASSERT(global); Maybe clientInfo(global->GetClientInfo()); if (clientInfo.isNothing()) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } mReadyPromise = Promise::Create(global, aRv, Promise::ePropagateUserInteraction); if (aRv.Failed()) { return nullptr; } RefPtr self = this; RefPtr outer = mReadyPromise; mInner->GetReady( clientInfo.ref(), [self, outer](const ServiceWorkerRegistrationDescriptor& aDescriptor) { ErrorResult rv; nsIGlobalObject* global = self->GetGlobalIfValid(rv); if (rv.Failed()) { outer->MaybeReject(rv); return; } RefPtr reg = global->GetOrCreateServiceWorkerRegistration(aDescriptor); NS_ENSURE_TRUE_VOID(reg); // Don't resolve the ready promise until the registration has // reached the right version. This ensures that the active // worker property is set correctly on the registration. reg->WhenVersionReached( aDescriptor.Version(), [outer, reg](bool aResult) { outer->MaybeResolve(reg); }); }, [self, outer](ErrorResult& aRv) { outer->MaybeReject(aRv); }); return mReadyPromise; } // Testing only. void ServiceWorkerContainer::GetScopeForUrl(const nsAString& aUrl, nsString& aScope, ErrorResult& aRv) { nsCOMPtr swm = mozilla::services::GetServiceWorkerManager(); if (!swm) { aRv.Throw(NS_ERROR_FAILURE); return; } nsCOMPtr window = GetOwner(); if (NS_WARN_IF(!window)) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } nsCOMPtr doc = window->GetExtantDoc(); if (NS_WARN_IF(!doc)) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } aRv = swm->GetScopeForUrl(doc->NodePrincipal(), aUrl, aScope); } nsIGlobalObject* ServiceWorkerContainer::GetGlobalIfValid( ErrorResult& aRv, const std::function&& aStorageFailureCB) const { // For now we require a window since ServiceWorkerContainer is // not exposed on worker globals yet. The main thing we need // to fix here to support that is the storage access check via // the nsIGlobalObject. nsPIDOMWindowInner* window = GetOwner(); if (NS_WARN_IF(!window)) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } nsCOMPtr doc = window->GetExtantDoc(); if (NS_WARN_IF(!doc)) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } // Don't allow a service worker to access service worker registrations // from a window with storage disabled. If these windows can access // the registration it increases the chance they can bypass the storage // block via postMessage(), etc. auto storageAllowed = StorageAllowedForWindow(window); if (NS_WARN_IF(storageAllowed != StorageAccess::eAllow)) { if (aStorageFailureCB) { aStorageFailureCB(doc); } aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); return nullptr; } // Don't allow service workers when the document is chrome. if (NS_WARN_IF(nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()))) { aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); return nullptr; } return window->AsGlobal(); } void ServiceWorkerContainer::EnqueueReceivedMessageDispatch( RefPtr aMessage) { if (nsPIDOMWindowInner* const window = GetOwner()) { if (auto* const target = window->EventTargetFor(TaskCategory::Other)) { target->Dispatch(NewRunnableMethod>( "ServiceWorkerContainer::DispatchMessage", this, &ServiceWorkerContainer::DispatchMessage, std::move(aMessage))); } } } template void ServiceWorkerContainer::RunWithJSContext(F&& aCallable) { nsCOMPtr globalObject; if (nsPIDOMWindowInner* const window = GetOwner()) { globalObject = do_QueryInterface(window); } // If AutoJSAPI::Init() fails then either global is nullptr or not // in a usable state. AutoJSAPI jsapi; if (!jsapi.Init(globalObject)) { return; } aCallable(jsapi.cx(), globalObject); } void ServiceWorkerContainer::DispatchMessage(RefPtr aMessage) { MOZ_ASSERT(NS_IsMainThread()); // When dispatching a message, either DOMContentLoaded has already // been fired, or someone called startMessages() or set onmessage. // Either way, a global object is supposed to be present. If it's // not, we'd fail to initialize the JS API and exit. RunWithJSContext([this, message = std::move(aMessage)]( JSContext* const aCx, nsIGlobalObject* const aGlobal) { ErrorResult result; bool deserializationFailed = false; RootedDictionary init(aCx); if (!FillInMessageEventInit(aCx, aGlobal, *message, init, result)) { deserializationFailed = result.ErrorCodeIs(NS_ERROR_DOM_DATA_CLONE_ERR); MOZ_ASSERT_IF(deserializationFailed, init.mData.isNull()); MOZ_ASSERT_IF(deserializationFailed, init.mPorts.IsEmpty()); MOZ_ASSERT_IF(deserializationFailed, !init.mOrigin.IsEmpty()); MOZ_ASSERT_IF(deserializationFailed, !init.mSource.IsNull()); if (!deserializationFailed && result.MaybeSetPendingException(aCx)) { return; } } RefPtr event = MessageEvent::Constructor( this, deserializationFailed ? NS_LITERAL_STRING("messageerror") : NS_LITERAL_STRING("message"), init); event->SetTrusted(true); result = NS_OK; DispatchEvent(*event, result); if (result.Failed()) { result.SuppressException(); } }); } namespace { nsresult FillInOriginNoSuffix(const ServiceWorkerDescriptor& aServiceWorker, nsString& aOrigin) { using mozilla::ipc::PrincipalInfoToPrincipal; nsresult rv; nsCOMPtr principal = PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo(), &rv); if (NS_FAILED(rv) || !principal) { return rv; } nsAutoCString originUTF8; rv = principal->GetOriginNoSuffix(originUTF8); if (NS_FAILED(rv)) { return rv; } CopyUTF8toUTF16(originUTF8, aOrigin); return NS_OK; } already_AddRefed GetOrCreateServiceWorkerWithoutWarnings( nsIGlobalObject* const aGlobal, const ServiceWorkerDescriptor& aDescriptor) { // In child-intercept mode we have to verify that the registration // exists in the current process. This exact check is also performed // (indirectly) in nsIGlobalObject::GetOrCreateServiceWorker, but it // also emits a warning when the registration is not present. To // to avoid having too many warnings, we do a precheck here. if (!ServiceWorkerParentInterceptEnabled()) { const RefPtr serviceWorkerManager = ServiceWorkerManager::GetInstance(); if (!serviceWorkerManager) { return nullptr; } const RefPtr registration = serviceWorkerManager->GetRegistration(aDescriptor.PrincipalInfo(), aDescriptor.Scope()); if (!registration) { return nullptr; } } return aGlobal->GetOrCreateServiceWorker(aDescriptor).forget(); } } // namespace bool ServiceWorkerContainer::FillInMessageEventInit( JSContext* const aCx, nsIGlobalObject* const aGlobal, ReceivedMessage& aMessage, MessageEventInit& aInit, ErrorResult& aRv) { // Determining the source and origin should preceed attempting deserialization // because on a "messageerror" event (i.e. when deserialization fails), the // dispatched message needs to contain such an origin and source, per spec: // // "If this throws an exception, catch it, fire an event named messageerror // at destination, using MessageEvent, with the origin attribute initialized // to origin and the source attribute initialized to source, and then abort // these steps." - 6.4 of postMessage // See: https://w3c.github.io/ServiceWorker/#service-worker-postmessage const RefPtr serviceWorkerInstance = GetOrCreateServiceWorkerWithoutWarnings(aGlobal, aMessage.mServiceWorker); if (serviceWorkerInstance) { aInit.mSource.SetValue().SetAsServiceWorker() = serviceWorkerInstance; } const nsresult rv = FillInOriginNoSuffix(aMessage.mServiceWorker, aInit.mOrigin); if (NS_FAILED(rv)) { return false; } JS::Rooted messageData(aCx); aMessage.mClonedData.Read(aCx, &messageData, aRv); if (aRv.Failed()) { return false; } aInit.mData = messageData; if (!aMessage.mClonedData.TakeTransferredPortsAsSequence(aInit.mPorts)) { xpc::Throw(aCx, NS_ERROR_OUT_OF_MEMORY); return false; } return true; } } // namespace dom } // namespace mozilla