# HG changeset patch # Parent 7823187daa4df566b2715b0d6f3af025fda63839 # User Andrea Marchesini diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -13995,17 +13995,22 @@ nsDocShell::ShouldPrepareForIntercept(ns } nsCOMPtr swm = services::GetServiceWorkerManager(); if (!swm) { return NS_OK; } if (aIsNavigate) { - return swm->IsAvailableForURI(aURI, aShouldIntercept); + nsCOMPtr principal = GetInheritedPrincipal(true); + if (!principal) { + return NS_OK; + } + + return swm->IsAvailableForURI(principal, aURI, aShouldIntercept); } nsCOMPtr doc = GetDocument(); if (!doc) { return NS_ERROR_NOT_AVAILABLE; } return swm->IsControlled(doc, aShouldIntercept); @@ -14029,17 +14034,21 @@ nsDocShell::ChannelIntercepted(nsIInterc if (!isNavigation) { doc = GetDocument(); if (!doc) { return NS_ERROR_NOT_AVAILABLE; } } bool isReload = mLoadType & LOAD_CMD_RELOAD; - return swm->DispatchFetchEvent(doc, aChannel, isReload); + + nsCOMPtr principal = GetInheritedPrincipal(true); + MOZ_ASSERT(principal); + + return swm->DispatchFetchEvent(principal, doc, aChannel, isReload); } NS_IMETHODIMP nsDocShell::SetPaymentRequestId(const nsAString& aPaymentRequestId) { mPaymentRequestId = aPaymentRequestId; return NS_OK; } diff --git a/dom/interfaces/base/nsIServiceWorkerManager.idl b/dom/interfaces/base/nsIServiceWorkerManager.idl --- a/dom/interfaces/base/nsIServiceWorkerManager.idl +++ b/dom/interfaces/base/nsIServiceWorkerManager.idl @@ -15,17 +15,17 @@ interface nsIURI; interface nsIServiceWorkerUnregisterCallback : nsISupports { // aState is true if the unregistration succeded. // It's false if this ServiceWorkerRegistration doesn't exist. void unregisterSucceeded(in bool aState); void unregisterFailed(); }; -[scriptable, builtinclass, uuid(8ce0d197-5740-4ddf-aa4a-e5a63e611d03)] +[scriptable, builtinclass, uuid(103763c8-53ba-42e4-8b26-e601d5bc4afe)] interface nsIServiceWorkerInfo : nsISupports { readonly attribute nsIPrincipal principal; readonly attribute DOMString scope; readonly attribute DOMString scriptSpec; readonly attribute DOMString currentWorkerURL; @@ -62,23 +62,25 @@ interface nsIServiceWorkerManager : nsIS // Returns a Promise nsISupports getReadyPromise(in nsIDOMWindow aWindow); // Remove ready pending Promise void removeReadyPromise(in nsIDOMWindow aWindow); // Returns true if a ServiceWorker is available for the scope of aURI. - bool isAvailableForURI(in nsIURI aURI); + bool isAvailableForURI(in nsIPrincipal aPrincipal, in nsIURI aURI); // Returns true if a given document is currently controlled by a ServiceWorker bool isControlled(in nsIDocument aDocument); - // Cause a fetch event to be dispatched to the worker global associated with the given document. - void dispatchFetchEvent(in nsIDocument aDoc, in nsIInterceptedChannel aChannel, + // Cause a fetch event to be dispatched to the worker global associated with + // the given document. + void dispatchFetchEvent(in nsIPrincipal aPrincipal, in nsIDocument aDoc, + in nsIInterceptedChannel aChannel, in boolean aIsReload); /** * Call this to request that document `aDoc` be controlled by a ServiceWorker * if a registration exists for it's scope. * * This MUST only be called once per document! */ @@ -104,17 +106,17 @@ interface nsIServiceWorkerManager : nsIS /* * Returns a ServiceWorker. */ [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow); /* * This implements the soft update algorithm. */ - void softUpdate(in DOMString aScope); + void softUpdate(in nsIPrincipal aPrincipal, in DOMString aScope); /* * Clears ServiceWorker registrations from memory and disk for the specified * host. * - All ServiceWorker instances change their state to redundant. * - Existing ServiceWorker instances handling fetches will keep running. * - All documents will immediately stop being controlled. * - Unregister jobs will be queued for all registrations. @@ -123,23 +125,25 @@ interface nsIServiceWorkerManager : nsIS void remove(in AUTF8String aHost); /* * Clear all registrations for all hosts. See remove(). */ void removeAll(); // Testing - DOMString getScopeForUrl(in DOMString path); + DOMString getScopeForUrl(in nsIPrincipal aPrincipal, in DOMString aPath); // This is meant to be used only by about:serviceworkers. It returns an array // of nsIServiceWorkerInfo. nsIArray getAllRegistrations(); - void sendPushEvent(in ACString scope, in DOMString data); - void sendPushSubscriptionChangeEvent(in ACString scope); + void sendPushEvent(in nsIPrincipal aPrincipal, in ACString aScope, + in DOMString aData); + void sendPushSubscriptionChangeEvent(in nsIPrincipal aPrincipal, + in ACString scope); void updateAllRegistrations(); }; %{ C++ #define SERVICEWORKERMANAGER_CONTRACTID "@mozilla.org/serviceworkers/manager;1" %} diff --git a/dom/push/PushServiceChildPreload.js b/dom/push/PushServiceChildPreload.js --- a/dom/push/PushServiceChildPreload.js +++ b/dom/push/PushServiceChildPreload.js @@ -5,14 +5,16 @@ "use strict"; XPCOMUtils.defineLazyServiceGetter(this, "swm", "@mozilla.org/serviceworkers/manager;1", "nsIServiceWorkerManager"); addMessageListener("push", function (aMessage) { - swm.sendPushEvent(aMessage.data.scope, aMessage.data.payload); + let principal = content.document.nodePrincipal; + swm.sendPushEvent(principal, aMessage.data.scope, aMessage.data.payload); }); addMessageListener("pushsubscriptionchange", function (aMessage) { - swm.sendPushSubscriptionChangeEvent(aMessage.data); + let principal = content.document.nodePrincipal; + swm.sendPushSubscriptionChangeEvent(principal, aMessage.data); }); diff --git a/dom/workers/ServiceWorkerClients.cpp b/dom/workers/ServiceWorkerClients.cpp --- a/dom/workers/ServiceWorkerClients.cpp +++ b/dom/workers/ServiceWorkerClients.cpp @@ -109,17 +109,17 @@ public: if (mPromiseProxy->IsClean()) { // Don't resolve the promise if it was already released. return NS_OK; } nsRefPtr swm = ServiceWorkerManager::GetInstance(); nsTArray result; - swm->GetAllClients(mScope, result); + swm->GetAllClients(mWorkerPrivate->GetPrincipal(), mScope, result); nsRefPtr r = new ResolvePromiseWorkerRunnable(mWorkerPrivate, mPromiseProxy, result); AutoSafeJSContext cx; if (r->Dispatch(cx)) { return NS_OK; } @@ -192,17 +192,19 @@ public: } NS_IMETHOD Run() override { nsRefPtr swm = ServiceWorkerManager::GetInstance(); MOZ_ASSERT(swm); - nsresult rv = swm->ClaimClients(mScope, mServiceWorkerID); + nsresult rv = + swm->ClaimClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), + mScope, mServiceWorkerID); MutexAutoLock lock(mPromiseProxy->GetCleanUpLock()); if (mPromiseProxy->IsClean()) { // Don't resolve the promise if it was already released. return NS_OK; } WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate(); diff --git a/dom/workers/ServiceWorkerContainer.cpp b/dom/workers/ServiceWorkerContainer.cpp --- a/dom/workers/ServiceWorkerContainer.cpp +++ b/dom/workers/ServiceWorkerContainer.cpp @@ -220,19 +220,25 @@ ServiceWorkerContainer::GetReady(ErrorRe } // Testing only. void ServiceWorkerContainer::GetScopeForUrl(const nsAString& aUrl, nsString& aScope, ErrorResult& aRv) { + nsCOMPtr doc = GetOwner()->GetExtantDoc(); + MOZ_ASSERT(doc); + + nsCOMPtr principal = doc->NodePrincipal(); + MOZ_ASSERT(principal); + nsCOMPtr swm = mozilla::services::GetServiceWorkerManager(); if (!swm) { aRv.Throw(NS_ERROR_FAILURE); return; } - aRv = swm->GetScopeForUrl(aUrl, aScope); + aRv = swm->GetScopeForUrl(principal, aUrl, aScope); } } // namespace dom } // namespace mozilla diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -46,16 +46,17 @@ #include "nsProxyRelease.h" #include "nsQueryObject.h" #include "nsTArray.h" #include "RuntimeService.h" #include "ServiceWorker.h" #include "ServiceWorkerClient.h" #include "ServiceWorkerContainer.h" +#include "ServiceWorkerRegistrar.h" #include "ServiceWorkerRegistration.h" #include "ServiceWorkerScriptCache.h" #include "ServiceWorkerEvents.h" #include "WorkerInlines.h" #include "WorkerPrivate.h" #include "WorkerRunnable.h" #include "WorkerScope.h" @@ -76,16 +77,34 @@ static_assert(nsIHttpChannelInternal::CO "RequestMode enumeration value should match Necko CORS mode value."); static_assert(nsIHttpChannelInternal::CORS_MODE_NO_CORS == static_cast(RequestMode::No_cors), "RequestMode enumeration value should match Necko CORS mode value."); static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast(RequestMode::Cors), "RequestMode enumeration value should match Necko CORS mode value."); static_assert(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT == static_cast(RequestMode::Cors_with_forced_preflight), "RequestMode enumeration value should match Necko CORS mode value."); +struct ServiceWorkerManager::RegistrationDataPerPrincipal +{ + // Ordered list of scopes for glob matching. + // Each entry is an absolute URL representing the scope. + // Each value of the hash table is an array of an absolute URLs representing + // the scopes. + // + // An array is used for now since the number of controlled scopes per + // domain is expected to be relatively low. If that assumption was proved + // wrong this should be replaced with a better structure to avoid the + // memmoves associated with inserting stuff in the middle of the array. + nsTArray mOrderedScopes; + + // Scope to registration. + // The scope should be a fully qualified valid URL. + nsRefPtrHashtable mInfos; +}; + struct ServiceWorkerManager::PendingOperation { nsCOMPtr mRunnable; ServiceWorkerJobQueue* mQueue; nsRefPtr mJob; ServiceWorkerRegistrationData mRegistration; @@ -253,17 +272,17 @@ ServiceWorkerManager::ServiceWorkerManag MOZ_ASSERT(NS_SUCCEEDED(rv)); } } } ServiceWorkerManager::~ServiceWorkerManager() { // The map will assert if it is not empty when destroyed. - mServiceWorkerRegistrationInfos.Clear(); + mRegistrationInfos.Clear(); } class ContinueLifecycleTask : public nsISupports { NS_DECL_ISUPPORTS protected: virtual ~ContinueLifecycleTask() @@ -637,17 +656,17 @@ public: if (!swm->HasBackgroundActor()) { nsCOMPtr runnable = NS_NewRunnableMethod(this, &ServiceWorkerRegisterJob::Start); swm->AppendPendingOperation(runnable); return; } if (mJobType == REGISTER_JOB) { - mRegistration = swm->GetRegistration(mScope); + mRegistration = swm->GetRegistration(mPrincipal, mScope); if (mRegistration) { nsRefPtr newest = mRegistration->Newest(); if (newest && mScriptSpec.Equals(newest->ScriptSpec()) && mScriptSpec.Equals(mRegistration->mScriptSpec)) { mRegistration->mPendingUninstall = false; swm->StoreRegistration(mPrincipal, mRegistration); Succeed(); @@ -1432,18 +1451,30 @@ public: return rv; } if (nsContentUtils::IsSystemPrincipal(principal) || isNullPrincipal) { mPromise->MaybeResolve(array); return NS_OK; } - for (uint32_t i = 0; i < swm->mOrderedScopes.Length(); ++i) { - NS_ConvertUTF8toUTF16 scope(swm->mOrderedScopes[i]); + nsAutoCString principalKey; + rv = swm->PrincipalToScopeKey(principal, principalKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + ServiceWorkerManager::RegistrationDataPerPrincipal* data; + if (!swm->mRegistrationInfos.Get(principalKey, &data)) { + mPromise->MaybeResolve(array); + return NS_OK; + } + + for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) { + NS_ConvertUTF8toUTF16 scope(data->mOrderedScopes[i]); nsCOMPtr scopeURI; nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope, nullptr, nullptr); if (NS_WARN_IF(NS_FAILED(rv))) { mPromise->MaybeReject(rv); break; } @@ -1543,17 +1574,17 @@ public: rv = principal->CheckMayLoad(uri, true /* report */, false /* allowIfInheritsPrinciple */); if (NS_FAILED(rv)) { mPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); return NS_OK; } nsRefPtr registration = - swm->GetServiceWorkerRegistrationInfo(uri); + swm->GetServiceWorkerRegistrationInfo(principal, uri); if (!registration) { mPromise->MaybeResolve(JS::UndefinedHandleValue); return NS_OK; } NS_ConvertUTF8toUTF16 scope(registration->mScope); nsRefPtr swr = @@ -1719,57 +1750,64 @@ public: globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr); return true; } }; #endif /* ! MOZ_SIMPLEPUSH */ NS_IMETHODIMP -ServiceWorkerManager::SendPushEvent(const nsACString& aScope, const nsAString& aData) +ServiceWorkerManager::SendPushEvent(nsIPrincipal* aPrincipal, + const nsACString& aScope, + const nsAString& aData) { #ifdef MOZ_SIMPLEPUSH return NS_ERROR_NOT_AVAILABLE; #else - nsRefPtr serviceWorker = CreateServiceWorkerForScope(aScope); + nsRefPtr serviceWorker = + CreateServiceWorkerForScope(aPrincipal, aScope); if (!serviceWorker) { return NS_ERROR_FAILURE; } nsMainThreadPtrHandle serviceWorkerHandle( new nsMainThreadPtrHolder(serviceWorker)); nsRefPtr r = - new SendPushEventRunnable(serviceWorker->GetWorkerPrivate(), aData, serviceWorkerHandle); + new SendPushEventRunnable(serviceWorker->GetWorkerPrivate(), aData, + serviceWorkerHandle); AutoJSAPI jsapi; jsapi.Init(); if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) { return NS_ERROR_FAILURE; } return NS_OK; #endif } NS_IMETHODIMP -ServiceWorkerManager::SendPushSubscriptionChangeEvent(const nsACString& aScope) +ServiceWorkerManager::SendPushSubscriptionChangeEvent(nsIPrincipal* aPrincipal, + const nsACString& aScope) { #ifdef MOZ_SIMPLEPUSH return NS_ERROR_NOT_AVAILABLE; #else - nsRefPtr serviceWorker = CreateServiceWorkerForScope(aScope); + nsRefPtr serviceWorker = + CreateServiceWorkerForScope(aPrincipal, aScope); if (!serviceWorker) { return NS_ERROR_FAILURE; } nsMainThreadPtrHandle serviceWorkerHandle( new nsMainThreadPtrHolder(serviceWorker)); nsRefPtr r = - new SendPushSubscriptionChangeEventRunnable(serviceWorker->GetWorkerPrivate(), serviceWorkerHandle); + new SendPushSubscriptionChangeEventRunnable( + serviceWorker->GetWorkerPrivate(), serviceWorkerHandle); AutoJSAPI jsapi; jsapi.Init(); if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) { return NS_ERROR_FAILURE; } return NS_OK; @@ -1858,41 +1896,52 @@ ServiceWorkerManager::CheckPendingReadyP return PL_DHASH_NEXT; } bool ServiceWorkerManager::CheckReadyPromise(nsPIDOMWindow* aWindow, nsIURI* aURI, Promise* aPromise) { + MOZ_ASSERT(aWindow); + MOZ_ASSERT(aURI); + + nsCOMPtr doc = aWindow->GetExtantDoc(); + MOZ_ASSERT(doc); + + nsCOMPtr principal = doc->NodePrincipal(); + MOZ_ASSERT(principal); + nsRefPtr registration = - GetServiceWorkerRegistrationInfo(aURI); + GetServiceWorkerRegistrationInfo(principal, aURI); if (registration && registration->mActiveWorker) { NS_ConvertUTF8toUTF16 scope(registration->mScope); nsRefPtr swr = new ServiceWorkerRegistrationMainThread(aWindow, scope); aPromise->MaybeResolve(swr); return true; } return false; } already_AddRefed -ServiceWorkerManager::CreateServiceWorkerForScope(const nsACString& aScope) +ServiceWorkerManager::CreateServiceWorkerForScope(nsIPrincipal* aPrincipal, + const nsACString& aScope) { AssertIsOnMainThread(); nsCOMPtr scopeURI; nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr); if (NS_FAILED(rv)) { return nullptr; } - nsRefPtr registration = GetServiceWorkerRegistrationInfo(scopeURI); + nsRefPtr registration = + GetServiceWorkerRegistrationInfo(aPrincipal, scopeURI); if (!registration) { return nullptr; } if (!registration->mActiveWorker) { return nullptr; } @@ -1908,30 +1957,30 @@ ServiceWorkerManager::CreateServiceWorke return sw.forget(); } class ServiceWorkerUnregisterJob final : public ServiceWorkerJob { nsRefPtr mRegistration; const nsCString mScope; nsCOMPtr mCallback; - PrincipalInfo mPrincipalInfo; + nsCOMPtr mPrincipal; ~ServiceWorkerUnregisterJob() { } public: ServiceWorkerUnregisterJob(ServiceWorkerJobQueue* aQueue, const nsACString& aScope, nsIServiceWorkerUnregisterCallback* aCallback, - PrincipalInfo& aPrincipalInfo) + nsIPrincipal* aPrincipal) : ServiceWorkerJob(aQueue) , mScope(aScope) , mCallback(aCallback) - , mPrincipalInfo(aPrincipalInfo) + , mPrincipal(aPrincipal) { AssertIsOnMainThread(); } void Start() override { AssertIsOnMainThread(); @@ -1942,32 +1991,50 @@ public: private: // You probably want UnregisterAndDone(). nsresult Unregister() { AssertIsOnMainThread(); + PrincipalInfo principalInfo; + if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(mPrincipal, + &principalInfo)))) { + return mCallback ? mCallback->UnregisterSucceeded(false) : NS_OK; + } + nsRefPtr swm = ServiceWorkerManager::GetInstance(); + nsAutoCString principalKey; + nsresult rv = swm->PrincipalToScopeKey(mPrincipal, principalKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return mCallback ? mCallback->UnregisterSucceeded(false) : NS_OK; + } + // "Let registration be the result of running [[Get Registration]] // algorithm passing scope as the argument." - nsRefPtr registration; - if (!swm->mServiceWorkerRegistrationInfos.Get(mScope, getter_AddRefs(registration))) { + ServiceWorkerManager::RegistrationDataPerPrincipal* data; + if (!swm->mRegistrationInfos.Get(principalKey, &data)) { // "If registration is null, then, resolve promise with false." return mCallback ? mCallback->UnregisterSucceeded(false) : NS_OK; } + nsRefPtr registration; + if (!data->mInfos.Get(mScope, getter_AddRefs(registration))) { + // "If registration is null, then, resolve promise with false." + return mCallback ? mCallback->UnregisterSucceeded(false) : NS_OK; + } + MOZ_ASSERT(registration); // "Set registration's uninstalling flag." registration->mPendingUninstall = true; // "Resolve promise with true" - nsresult rv = mCallback ? mCallback->UnregisterSucceeded(true) : NS_OK; + rv = mCallback ? mCallback->UnregisterSucceeded(true) : NS_OK; if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } // "If no service worker client is using registration..." if (!registration->IsControllingDocuments()) { // "If registration's uninstalling flag is set.." if (!registration->mPendingUninstall) { @@ -1975,17 +2042,17 @@ private: } // "Invoke [[Clear Registration]]..." registration->Clear(); swm->RemoveRegistration(registration); } MOZ_ASSERT(swm->mActor); - swm->mActor->SendUnregisterServiceWorker(mPrincipalInfo, + swm->mActor->SendUnregisterServiceWorker(principalInfo, NS_ConvertUTF8toUTF16(mScope)); return NS_OK; } // The unregister job is done irrespective of success or failure of any sort. void UnregisterAndDone() @@ -2016,24 +2083,18 @@ ServiceWorkerManager::Unregister(nsIPrin return NS_ERROR_DOM_SECURITY_ERR; } #endif NS_ConvertUTF16toUTF8 scope(aScope); ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(scope); MOZ_ASSERT(queue); - PrincipalInfo principalInfo; - if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal, - &principalInfo)))) { - return NS_ERROR_DOM_SECURITY_ERR; - } - nsRefPtr job = - new ServiceWorkerUnregisterJob(queue, scope, aCallback, principalInfo); + new ServiceWorkerUnregisterJob(queue, scope, aCallback, aPrincipal); if (mActor) { queue->Append(job); return NS_OK; } AppendPendingOperation(queue, job); return NS_OK; @@ -2241,95 +2302,236 @@ ServiceWorkerManager::StoreRegistration( } mActor->SendRegisterServiceWorker(data); } already_AddRefed ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsPIDOMWindow* aWindow) { + MOZ_ASSERT(aWindow); nsCOMPtr document = aWindow->GetExtantDoc(); return GetServiceWorkerRegistrationInfo(document); } already_AddRefed ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIDocument* aDoc) { + MOZ_ASSERT(aDoc); nsCOMPtr documentURI = aDoc->GetDocumentURI(); - return GetServiceWorkerRegistrationInfo(documentURI); + nsCOMPtr principal = aDoc->NodePrincipal(); + return GetServiceWorkerRegistrationInfo(principal, documentURI); } already_AddRefed -ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIURI* aURI) +ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal, + nsIURI* aURI) { - nsCString spec; + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aURI); + + nsAutoCString spec; nsresult rv = aURI->GetSpec(spec); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } - nsCString scope = FindScopeForPath(mOrderedScopes, spec); - if (scope.IsEmpty()) { + nsAutoCString scope; + RegistrationDataPerPrincipal* data; + if (!FindScopeForPath(aPrincipal, spec, &data, scope)) { return nullptr; } + MOZ_ASSERT(data); + nsRefPtr registration; - mServiceWorkerRegistrationInfos.Get(scope, getter_AddRefs(registration)); + data->mInfos.Get(scope, getter_AddRefs(registration)); // ordered scopes and registrations better be in sync. MOZ_ASSERT(registration); if (registration->mPendingUninstall) { return nullptr; } return registration.forget(); } +/* static */ nsresult +ServiceWorkerManager::PrincipalToScopeKey(nsIPrincipal* aPrincipal, + nsACString& aKey) +{ + MOZ_ASSERT(aPrincipal); + + if (nsContentUtils::IsSystemPrincipal(aPrincipal)) { + aKey.AssignLiteral(SERVICEWORKERREGISTRAR_SYSTEM_PRINCIPAL); + return NS_OK; + } + + nsAutoCString key; + nsresult rv; + + bool isNullPrincipal = true; + rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // No null principals. + if (isNullPrincipal) { + return NS_ERROR_FAILURE; + } + + uint32_t appId; + rv = aPrincipal->GetAppId(&appId); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + key.AppendInt(appId); + key.Append('-'); + + bool inBrowserElement; + rv = aPrincipal->GetIsInBrowserElement(&inBrowserElement); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (inBrowserElement) { + key.AppendLiteral(SERVICEWORKERREGISTRAR_TRUE); + } else { + key.AppendLiteral(SERVICEWORKERREGISTRAR_FALSE); + } + + aKey = key; + return NS_OK; +} + /* static */ void -ServiceWorkerManager::AddScope(nsTArray& aList, const nsACString& aScope) +ServiceWorkerManager::AddScopeAndRegistration(nsIPrincipal* aPrincipal, + const nsACString& aScope, + ServiceWorkerRegistrationInfo* aInfo) { - for (uint32_t i = 0; i < aList.Length(); ++i) { - const nsCString& current = aList[i]; + MOZ_ASSERT(aInfo); + + nsRefPtr swm = ServiceWorkerManager::GetInstance(); + MOZ_ASSERT(swm); + + nsAutoCString principalKey; + nsresult rv = swm->PrincipalToScopeKey(aPrincipal, principalKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + RegistrationDataPerPrincipal* data; + if (!swm->mRegistrationInfos.Get(principalKey, &data)) { + data = new RegistrationDataPerPrincipal(); + swm->mRegistrationInfos.Put(principalKey, data); + } + + for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) { + const nsCString& current = data->mOrderedScopes[i]; // Perfect match! if (aScope.Equals(current)) { + data->mInfos.Put(aScope, aInfo); return; } // Sort by length, with longest match first. // /foo/bar should be before /foo/ // Similarly /foo/b is between the two. if (StringBeginsWith(aScope, current)) { - aList.InsertElementAt(i, aScope); + data->mOrderedScopes.InsertElementAt(i, aScope); + data->mInfos.Put(aScope, aInfo); return; } } - aList.AppendElement(aScope); + data->mOrderedScopes.AppendElement(aScope); + data->mInfos.Put(aScope, aInfo); } -/* static */ nsCString -ServiceWorkerManager::FindScopeForPath(nsTArray& aList, const nsACString& aPath) +/* static */ bool +ServiceWorkerManager::FindScopeForPath(nsIPrincipal* aPrincipal, + const nsACString& aPath, + RegistrationDataPerPrincipal** aData, + nsACString& aMatch) { - nsCString match; - - for (uint32_t i = 0; i < aList.Length(); ++i) { - const nsCString& current = aList[i]; + MOZ_ASSERT(aPrincipal); + MOZ_ASSERT(aData); + + nsRefPtr swm = ServiceWorkerManager::GetInstance(); + MOZ_ASSERT(swm); + + nsAutoCString principalKey; + nsresult rv = swm->PrincipalToScopeKey(aPrincipal, principalKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + if (!swm->mRegistrationInfos.Get(principalKey, aData)) { + return false; + } + + for (uint32_t i = 0; i < (*aData)->mOrderedScopes.Length(); ++i) { + const nsCString& current = (*aData)->mOrderedScopes[i]; if (StringBeginsWith(aPath, current)) { - match = current; - break; + aMatch = current; + return true; } } - return match; + return false; } +#ifdef DEBUG +/* static */ bool +ServiceWorkerManager::HasScope(nsIPrincipal* aPrincipal, + const nsACString& aScope) +{ + nsRefPtr swm = ServiceWorkerManager::GetInstance(); + MOZ_ASSERT(swm); + + nsAutoCString principalKey; + nsresult rv = swm->PrincipalToScopeKey(aPrincipal, principalKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + RegistrationDataPerPrincipal* data; + if (!swm->mRegistrationInfos.Get(principalKey, &data)) { + return false; + } + + return data->mOrderedScopes.Contains(aScope); +} +#endif + /* static */ void -ServiceWorkerManager::RemoveScope(nsTArray& aList, const nsACString& aScope) +ServiceWorkerManager::RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo* aRegistration) { - aList.RemoveElement(aScope); + nsRefPtr swm = ServiceWorkerManager::GetInstance(); + MOZ_ASSERT(swm); + + nsAutoCString principalKey; + nsresult rv = swm->PrincipalToScopeKey(aRegistration->mPrincipal, principalKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + RegistrationDataPerPrincipal* data; + if (!swm->mRegistrationInfos.Get(principalKey, &data)) { + return; + } + + data->mInfos.Remove(aRegistration->mScope); + data->mOrderedScopes.RemoveElement(aRegistration->mScope); + + if (data->mOrderedScopes.IsEmpty()) { + swm->mRegistrationInfos.Remove(principalKey); + } } void ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc) { AssertIsOnMainThread(); // We keep a set of documents that service workers may choose to start @@ -2384,25 +2586,29 @@ ServiceWorkerManager::StopControllingADo RemoveRegistration(aRegistration); } else { aRegistration->TryToActivate(); } } } NS_IMETHODIMP -ServiceWorkerManager::GetScopeForUrl(const nsAString& aUrl, nsAString& aScope) +ServiceWorkerManager::GetScopeForUrl(nsIPrincipal* aPrincipal, + const nsAString& aUrl, nsAString& aScope) { + MOZ_ASSERT(aPrincipal); + nsCOMPtr uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl, nullptr, nullptr); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } - nsRefPtr r = GetServiceWorkerRegistrationInfo(uri); + nsRefPtr r = + GetServiceWorkerRegistrationInfo(aPrincipal, uri); if (!r) { return NS_ERROR_FAILURE; } aScope = NS_ConvertUTF8toUTF16(r->mScope); return NS_OK; } @@ -2497,17 +2703,18 @@ ServiceWorkerManager::GetServiceWorkerFo nsCOMPtr documentPrincipal = doc->NodePrincipal(); rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */, false /* allowIfInheritsPrinciple */); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_DOM_SECURITY_ERR; } //////////////////////////////////////////// - nsRefPtr registration = GetRegistration(scope); + nsRefPtr registration = + GetRegistration(documentPrincipal, scope); if (NS_WARN_IF(!registration)) { return NS_ERROR_FAILURE; } nsRefPtr info; if (aWhichWorker == WhichServiceWorker::INSTALLING_WORKER) { info = registration->mInstallingWorker; } else if (aWhichWorker == WhichServiceWorker::WAITING_WORKER) { @@ -2749,19 +2956,22 @@ private: } return true; } }; NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor) NS_IMETHODIMP -ServiceWorkerManager::DispatchFetchEvent(nsIDocument* aDoc, nsIInterceptedChannel* aChannel, +ServiceWorkerManager::DispatchFetchEvent(nsIPrincipal* aPrincipal, + nsIDocument* aDoc, + nsIInterceptedChannel* aChannel, bool aIsReload) { + MOZ_ASSERT(aPrincipal); MOZ_ASSERT(aChannel); nsCOMPtr serviceWorker; bool isNavigation = false; nsresult rv = aChannel->GetIsNavigation(&isNavigation); NS_ENSURE_SUCCESS(rv, rv); nsAutoPtr clientInfo; @@ -2775,17 +2985,17 @@ ServiceWorkerManager::DispatchFetchEvent rv = aChannel->GetChannel(getter_AddRefs(internalChannel)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr uri; rv = internalChannel->GetURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); nsRefPtr registration = - GetServiceWorkerRegistrationInfo(uri); + GetServiceWorkerRegistrationInfo(aPrincipal, uri); if (!registration) { NS_WARNING("No registration found when dispatching the fetch event"); return NS_ERROR_FAILURE; } // This should only happen if IsAvailableForURI() returned true. MOZ_ASSERT(registration->mActiveWorker); nsRefPtr sw; @@ -2803,36 +3013,39 @@ ServiceWorkerManager::DispatchFetchEvent new nsMainThreadPtrHolder(aChannel, false)); nsRefPtr sw = static_cast(serviceWorker.get()); nsMainThreadPtrHandle serviceWorkerHandle( new nsMainThreadPtrHolder(sw)); // clientInfo is null if we don't have a controlled document nsRefPtr event = - new FetchEventRunnable(sw->GetWorkerPrivate(), handle, serviceWorkerHandle, clientInfo, aIsReload); + new FetchEventRunnable(sw->GetWorkerPrivate(), handle, serviceWorkerHandle, + clientInfo, aIsReload); rv = event->Init(); NS_ENSURE_SUCCESS(rv, rv); AutoJSAPI api; api.Init(); if (NS_WARN_IF(!event->Dispatch(api.cx()))) { return NS_ERROR_FAILURE; } return NS_OK; } NS_IMETHODIMP -ServiceWorkerManager::IsAvailableForURI(nsIURI* aURI, bool* aIsAvailable) +ServiceWorkerManager::IsAvailableForURI(nsIPrincipal* aPrincipal, nsIURI* aURI, + bool* aIsAvailable) { + MOZ_ASSERT(aPrincipal); MOZ_ASSERT(aURI); MOZ_ASSERT(aIsAvailable); nsRefPtr registration = - GetServiceWorkerRegistrationInfo(aURI); + GetServiceWorkerRegistrationInfo(aPrincipal, aURI); *aIsAvailable = registration && registration->mActiveWorker; return NS_OK; } NS_IMETHODIMP ServiceWorkerManager::IsControlled(nsIDocument* aDoc, bool* aIsControlled) { MOZ_ASSERT(aDoc); @@ -3003,23 +3216,24 @@ ServiceWorkerManager::InvalidateServiceW if (utf8Scope.Equals(aRegistration->mScope)) { target->InvalidateWorkers(aWhichOnes); } } } NS_IMETHODIMP -ServiceWorkerManager::SoftUpdate(const nsAString& aScope) +ServiceWorkerManager::SoftUpdate(nsIPrincipal* aPrincipal, + const nsAString& aScope) { AssertIsOnMainThread(); NS_ConvertUTF16toUTF8 scope(aScope); - nsRefPtr registration; - mServiceWorkerRegistrationInfos.Get(scope, getter_AddRefs(registration)); + nsRefPtr registration = + GetRegistration(aPrincipal, scope); if (NS_WARN_IF(!registration)) { return NS_OK; } // "If registration's uninstalling flag is set, abort these steps." if (registration->mPendingUninstall) { return NS_OK; } @@ -3151,20 +3365,24 @@ ClaimMatchingClients(nsISupportsHashKey* swm->MaybeClaimClient(document, workerRegistration); return PL_DHASH_NEXT; } } // anonymous namespace void -ServiceWorkerManager::GetAllClients(const nsCString& aScope, +ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal, + const nsCString& aScope, nsTArray& aControlledDocuments) { - nsRefPtr registration = GetRegistration(aScope); + MOZ_ASSERT(aPrincipal); + + nsRefPtr registration = + GetRegistration(aPrincipal, aScope); if (!registration) { // The registration was removed, leave the array empty. return; } FilterRegistrationData data(aControlledDocuments, registration); @@ -3200,20 +3418,21 @@ ServiceWorkerManager::MaybeClaimClient(n StopControllingADocument(controllingRegistration); } StartControllingADocument(aWorkerRegistration, aDocument); FireControllerChangeOnDocument(aDocument); } nsresult -ServiceWorkerManager::ClaimClients(const nsCString& aScope, uint64_t aId) +ServiceWorkerManager::ClaimClients(nsIPrincipal* aPrincipal, + const nsCString& aScope, uint64_t aId) { nsRefPtr registration = - GetRegistration(aScope); + GetRegistration(aPrincipal, aScope); if (!registration || !registration->mActiveWorker || !(registration->mActiveWorker->ID() == aId)) { // The worker is not active. return NS_ERROR_DOM_INVALID_STATE_ERR; } mAllDocuments.EnumerateEntries(ClaimMatchingClients, registration); @@ -3222,31 +3441,52 @@ ServiceWorkerManager::ClaimClients(const } void ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration) { mControlledDocuments.EnumerateRead(FireControllerChangeOnMatchingDocument, aRegistration); } +already_AddRefed +ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal, + const nsCString& aScope) const +{ + nsRefPtr reg; + + nsAutoCString scopeKey; + nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); + if (NS_WARN_IF(NS_FAILED(rv))) { + return reg.forget(); + } + + RegistrationDataPerPrincipal* data; + if (!mRegistrationInfos.Get(scopeKey, &data)) { + return reg.forget(); + } + + data->mInfos.Get(aScope, getter_AddRefs(reg)); + return reg.forget(); +} + ServiceWorkerRegistrationInfo* ServiceWorkerManager::CreateNewRegistration(const nsCString& aScope, nsIPrincipal* aPrincipal) { #ifdef DEBUG AssertIsOnMainThread(); nsCOMPtr scopeURI; nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr); MOZ_ASSERT(NS_SUCCEEDED(rv)); #endif + ServiceWorkerRegistrationInfo* registration = new ServiceWorkerRegistrationInfo(aScope, aPrincipal); // From now on ownership of registration is with // mServiceWorkerRegistrationInfos. - mServiceWorkerRegistrationInfos.Put(aScope, registration); - AddScope(mOrderedScopes, aScope); + AddScopeAndRegistration(aPrincipal, aScope, registration); return registration; } void ServiceWorkerManager::MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration) { MOZ_ASSERT(aRegistration); nsRefPtr newest = aRegistration->Newest(); @@ -3255,18 +3495,16 @@ ServiceWorkerManager::MaybeRemoveRegistr } } void ServiceWorkerManager::RemoveRegistrationInternal(ServiceWorkerRegistrationInfo* aRegistration) { MOZ_ASSERT(aRegistration); MOZ_ASSERT(!aRegistration->IsControllingDocuments()); - MOZ_ASSERT(mServiceWorkerRegistrationInfos.Contains(aRegistration->mScope)); - ServiceWorkerManager::RemoveScope(mOrderedScopes, aRegistration->mScope); // All callers should be either from a job in which case the actor is // available, or from MaybeStopControlling(), in which case, this will only be // called if a valid registration is found. If a valid registration exists, // it means the actor is available since the original map of registrations is // populated by it, and any new registrations wait until the actor is // available before proceeding (See ServiceWorkerRegisterJob::Start). MOZ_ASSERT(mActor); @@ -3274,17 +3512,19 @@ ServiceWorkerManager::RemoveRegistration PrincipalInfo principalInfo; if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aRegistration->mPrincipal, &principalInfo)))) { //XXXnsm I can't think of any other reason a stored principal would fail to //convert. NS_WARNING("Unable to unregister serviceworker due to possible OOM"); return; } - mActor->SendUnregisterServiceWorker(principalInfo, NS_ConvertUTF8toUTF16(aRegistration->mScope)); + + mActor->SendUnregisterServiceWorker(principalInfo, + NS_ConvertUTF8toUTF16(aRegistration->mScope)); } class ServiceWorkerDataInfo final : public nsIServiceWorkerInfo { public: NS_DECL_ISUPPORTS NS_DECL_NSISERVICEWORKERINFO @@ -3304,18 +3544,18 @@ private: nsString mCurrentWorkerURL; nsString mActiveCacheName; nsString mWaitingCacheName; }; void ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration) { RemoveRegistrationInternal(aRegistration); - MOZ_ASSERT(mServiceWorkerRegistrationInfos.Contains(aRegistration->mScope)); - mServiceWorkerRegistrationInfos.Remove(aRegistration->mScope); + MOZ_ASSERT(HasScope(aRegistration->mPrincipal, aRegistration->mScope)); + RemoveScopeAndRegistration(aRegistration); } namespace { /** * See browser/components/sessionstore/Utils.jsm function hasRootDomain(). * * Returns true if the |url| passed in is part of the given root |domain|. * For example, if |url| is "www.mozilla.org", and we pass in |domain| as @@ -3379,16 +3619,27 @@ UnregisterIfMatchesHost(const nsACString if (toRemove) { nsRefPtr swm = ServiceWorkerManager::GetInstance(); swm->ForceUnregister(toRemove); } return PL_DHASH_NEXT; } + +// If host/aData is null, unconditionally unregisters. +PLDHashOperator +UnregisterIfMatchesHostPerPrincipal(const nsACString& aKey, + ServiceWorkerManager::RegistrationDataPerPrincipal* aData, + void* aUserData) +{ + aData->mInfos.EnumerateRead(UnregisterIfMatchesHost, aUserData); + return PL_DHASH_NEXT; +} + } // anonymous namespace NS_IMPL_ISUPPORTS(ServiceWorkerDataInfo, nsIServiceWorkerInfo) /* static */ already_AddRefed ServiceWorkerDataInfo::Create(const ServiceWorkerRegistrationData& aData) { AssertIsOnMainThread(); @@ -3500,46 +3751,56 @@ ServiceWorkerManager::ForceUnregister(Se // Since Unregister is async, it is ok to call it in an enumeration. Unregister(aRegistration->mPrincipal, nullptr, NS_ConvertUTF8toUTF16(aRegistration->mScope)); } NS_IMETHODIMP ServiceWorkerManager::Remove(const nsACString& aHost) { AssertIsOnMainThread(); - mServiceWorkerRegistrationInfos.EnumerateRead(UnregisterIfMatchesHost, &const_cast(aHost)); + mRegistrationInfos.EnumerateRead(UnregisterIfMatchesHostPerPrincipal, + &const_cast(aHost)); return NS_OK; } NS_IMETHODIMP ServiceWorkerManager::RemoveAll() { AssertIsOnMainThread(); - mServiceWorkerRegistrationInfos.EnumerateRead(UnregisterIfMatchesHost, nullptr); + mRegistrationInfos.EnumerateRead(UnregisterIfMatchesHostPerPrincipal, nullptr); return NS_OK; } static PLDHashOperator UpdateEachRegistration(const nsACString& aKey, ServiceWorkerRegistrationInfo* aInfo, void* aUserArg) { auto This = static_cast(aUserArg); MOZ_ASSERT(!aInfo->mScope.IsEmpty()); - nsresult res = This->SoftUpdate(NS_ConvertUTF8toUTF16(aInfo->mScope)); + nsresult res = This->SoftUpdate(aInfo->mPrincipal, + NS_ConvertUTF8toUTF16(aInfo->mScope)); unused << NS_WARN_IF(NS_FAILED(res)); return PL_DHASH_NEXT; } +static PLDHashOperator +UpdateEachRegistrationPerPrincipal(const nsACString& aKey, + ServiceWorkerManager::RegistrationDataPerPrincipal* aData, + void* aUserArg) { + aData->mInfos.EnumerateRead(UpdateEachRegistration, aUserArg); + return PL_DHASH_NEXT; +} + NS_IMETHODIMP ServiceWorkerManager::UpdateAllRegistrations() { AssertIsOnMainThread(); - mServiceWorkerRegistrationInfos.EnumerateRead(UpdateEachRegistration, this); + mRegistrationInfos.EnumerateRead(UpdateEachRegistrationPerPrincipal, this); return NS_OK; } NS_IMETHODIMP ServiceWorkerManager::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h --- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -359,48 +359,33 @@ public: { AssertIsOnMainThread(); ServiceWorkerManager* res = new ServiceWorkerManager; NS_ADDREF(res); return res; } - // Ordered list of scopes for glob matching. - // Each entry is an absolute URL representing the scope. - // - // An array is used for now since the number of controlled scopes per - // domain is expected to be relatively low. If that assumption was proved - // wrong this should be replaced with a better structure to avoid the - // memmoves associated with inserting stuff in the middle of the array. - nsTArray mOrderedScopes; - - // Scope to registration. - // The scope should be a fully qualified valid URL. - nsRefPtrHashtable mServiceWorkerRegistrationInfos; + struct RegistrationDataPerPrincipal; + nsClassHashtable mRegistrationInfos; nsTObserverArray mServiceWorkerRegistrationListeners; nsRefPtrHashtable mControlledDocuments; // Set of all documents that may be controlled by a service worker. nsTHashtable mAllDocuments; // Maps scopes to job queues. nsClassHashtable mJobQueues; nsDataHashtable mSetOfScopesBeingUpdated; already_AddRefed - GetRegistration(const nsCString& aScope) const - { - nsRefPtr reg; - mServiceWorkerRegistrationInfos.Get(aScope, getter_AddRefs(reg)); - return reg.forget(); - } + GetRegistration(nsIPrincipal* aPrincipal, const nsCString& aScope) const; ServiceWorkerRegistrationInfo* CreateNewRegistration(const nsCString& aScope, nsIPrincipal* aPrincipal); void RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration); ServiceWorkerJobQueue* @@ -424,25 +409,26 @@ public: nsString aMessage, nsString aFilename, nsString aLine, uint32_t aLineNumber, uint32_t aColumnNumber, uint32_t aFlags); void - GetAllClients(const nsCString& aScope, + GetAllClients(nsIPrincipal* aPrincipal, + const nsCString& aScope, nsTArray& aControlledDocuments); void MaybeClaimClient(nsIDocument* aDocument, ServiceWorkerRegistrationInfo* aWorkerRegistration); nsresult - ClaimClients(const nsCString& aScope, uint64_t aId); + ClaimClients(nsIPrincipal* aPrincipal, const nsCString& aScope, uint64_t aId); static already_AddRefed GetInstance(); void LoadRegistrations( const nsTArray& aRegistrations); // Used by remove() and removeAll() when clearing history. @@ -482,17 +468,18 @@ private: NS_IMETHODIMP GetServiceWorkerForScope(nsIDOMWindow* aWindow, const nsAString& aScope, WhichServiceWorker aWhichWorker, nsISupports** aServiceWorker); already_AddRefed - CreateServiceWorkerForScope(const nsACString& aScope); + CreateServiceWorkerForScope(nsIPrincipal* aPrincipal, + const nsACString& aScope); void InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration, WhichServiceWorker aWhichOnes); void StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration, nsIDocument* aDoc); @@ -502,26 +489,39 @@ private: already_AddRefed GetServiceWorkerRegistrationInfo(nsPIDOMWindow* aWindow); already_AddRefed GetServiceWorkerRegistrationInfo(nsIDocument* aDoc); already_AddRefed - GetServiceWorkerRegistrationInfo(nsIURI* aURI); + GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal, nsIURI* aURI); + + // This method generates a key using appId and isInElementBrowser from the + // principal. We don't use the origin because it can simple change during the + // loading. + static nsresult + PrincipalToScopeKey(nsIPrincipal* aPrincipal, nsACString& aPrincipalKey); static void - AddScope(nsTArray& aList, const nsACString& aScope); + AddScopeAndRegistration(nsIPrincipal* aPrincipal, const nsACString& aScope, + ServiceWorkerRegistrationInfo* aRegistation); - static nsCString - FindScopeForPath(nsTArray& aList, const nsACString& aPath); + static bool + FindScopeForPath(nsIPrincipal* aPrincipal, const nsACString& aPath, + RegistrationDataPerPrincipal** aData, nsACString& aMatch); + +#ifdef DEBUG + static bool + HasScope(nsIPrincipal* aPrincipal, const nsACString& aScope); +#endif static void - RemoveScope(nsTArray& aList, const nsACString& aScope); + RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo* aRegistration); void QueueFireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration, const nsAString& aName); void FireUpdateFoundOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration); diff --git a/dom/workers/ServiceWorkerRegistration.cpp b/dom/workers/ServiceWorkerRegistration.cpp --- a/dom/workers/ServiceWorkerRegistration.cpp +++ b/dom/workers/ServiceWorkerRegistration.cpp @@ -228,45 +228,108 @@ ServiceWorkerRegistrationMainThread::Inv if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) { mActiveWorker = nullptr; } } namespace { void -UpdateInternal(const nsAString& aScope) +UpdateInternal(nsIPrincipal* aPrincipal, const nsAString& aScope) { AssertIsOnMainThread(); nsCOMPtr swm = mozilla::services::GetServiceWorkerManager(); MOZ_ASSERT(swm); // The spec defines ServiceWorkerRegistration.update() exactly as Soft Update. - swm->SoftUpdate(aScope); + swm->SoftUpdate(aPrincipal, aScope); } +// This Runnable needs to have a valid WorkerPrivate. For this reason it is also +// a WorkerFeature that is registered before dispatching itself to the +// main-thread and it's removed with ReleaseRunnable when the operation is +// completed. This will keep the worker alive as long as necessary. class UpdateRunnable final : public nsRunnable + , public WorkerFeature { public: - explicit UpdateRunnable(const nsAString& aScope) - : mScope(aScope) + UpdateRunnable(WorkerPrivate* aWorkerPrivate, const nsAString& aScope) + : mWorkerPrivate(aWorkerPrivate) + , mScope(aScope) {} NS_IMETHOD Run() override { AssertIsOnMainThread(); - UpdateInternal(mScope); + UpdateInternal(mWorkerPrivate->GetPrincipal(), mScope); + + class ReleaseRunnable final : public MainThreadWorkerControlRunnable + { + nsRefPtr mRunnable; + + public: + ReleaseRunnable(WorkerPrivate* aWorkerPrivate, + UpdateRunnable* aRunnable) + : MainThreadWorkerControlRunnable(aWorkerPrivate) + , mRunnable(aRunnable) + { + MOZ_ASSERT(aRunnable); + } + + virtual bool + WorkerRun(JSContext* aCx, + workers::WorkerPrivate* aWorkerPrivate) override + { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + + aWorkerPrivate->RemoveFeature(aCx, mRunnable); + return true; + } + + private: + ~ReleaseRunnable() + {} + }; + + nsRefPtr runnable = + new ReleaseRunnable(mWorkerPrivate, this); + runnable->Dispatch(nullptr); + return NS_OK; } + virtual bool Notify(JSContext* aCx, workers::Status aStatus) override + { + // We don't care about the notification. We just want to keep the + // mWorkerPrivate alive. + return true; + } + + bool + Dispatch() + { + mWorkerPrivate->AssertIsOnWorkerThread(); + + JSContext* cx = mWorkerPrivate->GetJSContext(); + + if (NS_WARN_IF(!mWorkerPrivate->AddFeature(cx, this))) { + return false; + } + + NS_SUCCEEDED(NS_DispatchToMainThread(this)); + return true; + } + private: ~UpdateRunnable() {} + WorkerPrivate* mWorkerPrivate; const nsString mScope; }; class UnregisterCallback final : public nsIServiceWorkerUnregisterCallback { nsRefPtr mPromise; public: @@ -449,17 +512,23 @@ public: return NS_OK; } }; } // anonymous namespace void ServiceWorkerRegistrationMainThread::Update() { - UpdateInternal(mScope); + nsCOMPtr doc = GetOwner()->GetExtantDoc(); + MOZ_ASSERT(doc); + + nsCOMPtr principal = doc->NodePrincipal(); + MOZ_ASSERT(principal); + + UpdateInternal(principal, mScope); } already_AddRefed ServiceWorkerRegistrationMainThread::Unregister(ErrorResult& aRv) { AssertIsOnMainThread(); nsCOMPtr go = do_QueryInterface(GetOwner()); if (!go) { @@ -731,23 +800,22 @@ ServiceWorkerRegistrationWorkerThread::G { // FIXME(nsm): Will be implemented after Bug 1113522. return nullptr; } void ServiceWorkerRegistrationWorkerThread::Update() { -#ifdef DEBUG WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); -#endif - nsCOMPtr r = new UpdateRunnable(mScope); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r))); + + nsRefPtr r = new UpdateRunnable(worker, mScope); + r->Dispatch(); } already_AddRefed ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv) { WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); diff --git a/toolkit/content/aboutServiceWorkers.js b/toolkit/content/aboutServiceWorkers.js --- a/toolkit/content/aboutServiceWorkers.js +++ b/toolkit/content/aboutServiceWorkers.js @@ -127,17 +127,17 @@ function display(info) { error => { dump("about:serviceworkers - push registration failed\n"); } ); let updateButton = document.createElement("button"); updateButton.appendChild(document.createTextNode(bundle.GetStringFromName('update'))); updateButton.onclick = function() { - gSWM.softUpdate(info.scope); + gSWM.softUpdate(info.principal, info.scope); }; div.appendChild(updateButton); let unregisterButton = document.createElement("button"); unregisterButton.appendChild(document.createTextNode(bundle.GetStringFromName('unregister'))); div.appendChild(unregisterButton); let loadingMessage = document.createElement('span');