LCOV - code coverage report
Current view: top level - dom/workers - ServiceWorkerManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 128 1998 6.4 %
Date: 2017-07-14 16:53:18 Functions: 19 210 9.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "ServiceWorkerManager.h"
       8             : 
       9             : #include "nsAutoPtr.h"
      10             : #include "nsIConsoleService.h"
      11             : #include "nsIDOMEventTarget.h"
      12             : #include "nsIDocument.h"
      13             : #include "nsIScriptSecurityManager.h"
      14             : #include "nsIStreamLoader.h"
      15             : #include "nsIHttpChannel.h"
      16             : #include "nsIHttpChannelInternal.h"
      17             : #include "nsIHttpHeaderVisitor.h"
      18             : #include "nsINetworkInterceptController.h"
      19             : #include "nsIMutableArray.h"
      20             : #include "nsIScriptError.h"
      21             : #include "nsISimpleEnumerator.h"
      22             : #include "nsITimer.h"
      23             : #include "nsIUploadChannel2.h"
      24             : #include "nsPIDOMWindow.h"
      25             : #include "nsServiceManagerUtils.h"
      26             : #include "nsDebug.h"
      27             : #include "nsISupportsPrimitives.h"
      28             : #include "nsIPermissionManager.h"
      29             : 
      30             : #include "jsapi.h"
      31             : 
      32             : #include "mozilla/BasePrincipal.h"
      33             : #include "mozilla/ClearOnShutdown.h"
      34             : #include "mozilla/ErrorNames.h"
      35             : #include "mozilla/LoadContext.h"
      36             : #include "mozilla/Telemetry.h"
      37             : #include "mozilla/dom/BindingUtils.h"
      38             : #include "mozilla/dom/ContentParent.h"
      39             : #include "mozilla/dom/ContentChild.h"
      40             : #include "mozilla/dom/DOMError.h"
      41             : #include "mozilla/dom/ErrorEvent.h"
      42             : #include "mozilla/dom/Headers.h"
      43             : #include "mozilla/dom/InternalHeaders.h"
      44             : #include "mozilla/dom/Navigator.h"
      45             : #include "mozilla/dom/NotificationEvent.h"
      46             : #include "mozilla/dom/PromiseNativeHandler.h"
      47             : #include "mozilla/dom/Request.h"
      48             : #include "mozilla/dom/RootedDictionary.h"
      49             : #include "mozilla/dom/TypedArray.h"
      50             : #include "mozilla/ipc/BackgroundChild.h"
      51             : #include "mozilla/ipc/PBackgroundChild.h"
      52             : #include "mozilla/ipc/PBackgroundSharedTypes.h"
      53             : #include "mozilla/dom/ScriptLoader.h"
      54             : #include "mozilla/Unused.h"
      55             : #include "mozilla/EnumSet.h"
      56             : 
      57             : #include "nsContentPolicyUtils.h"
      58             : #include "nsContentSecurityManager.h"
      59             : #include "nsContentUtils.h"
      60             : #include "nsGlobalWindow.h"
      61             : #include "nsNetUtil.h"
      62             : #include "nsProxyRelease.h"
      63             : #include "nsQueryObject.h"
      64             : #include "nsTArray.h"
      65             : 
      66             : #include "RuntimeService.h"
      67             : #include "ServiceWorker.h"
      68             : #include "ServiceWorkerClient.h"
      69             : #include "ServiceWorkerContainer.h"
      70             : #include "ServiceWorkerInfo.h"
      71             : #include "ServiceWorkerJobQueue.h"
      72             : #include "ServiceWorkerManagerChild.h"
      73             : #include "ServiceWorkerPrivate.h"
      74             : #include "ServiceWorkerRegisterJob.h"
      75             : #include "ServiceWorkerRegistrar.h"
      76             : #include "ServiceWorkerRegistration.h"
      77             : #include "ServiceWorkerScriptCache.h"
      78             : #include "ServiceWorkerEvents.h"
      79             : #include "ServiceWorkerUnregisterJob.h"
      80             : #include "ServiceWorkerUpdateJob.h"
      81             : #include "ServiceWorkerUpdaterChild.h"
      82             : #include "SharedWorker.h"
      83             : #include "WorkerInlines.h"
      84             : #include "WorkerPrivate.h"
      85             : #include "WorkerRunnable.h"
      86             : #include "WorkerScope.h"
      87             : 
      88             : #ifdef PostMessage
      89             : #undef PostMessage
      90             : #endif
      91             : 
      92             : using namespace mozilla;
      93             : using namespace mozilla::dom;
      94             : using namespace mozilla::ipc;
      95             : 
      96             : BEGIN_WORKERS_NAMESPACE
      97             : 
      98             : #define PURGE_DOMAIN_DATA "browser:purge-domain-data"
      99             : #define PURGE_SESSION_HISTORY "browser:purge-session-history"
     100             : #define CLEAR_ORIGIN_DATA "clear-origin-attributes-data"
     101             : 
     102             : static_assert(nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN == static_cast<uint32_t>(RequestMode::Same_origin),
     103             :               "RequestMode enumeration value should match Necko CORS mode value.");
     104             : static_assert(nsIHttpChannelInternal::CORS_MODE_NO_CORS == static_cast<uint32_t>(RequestMode::No_cors),
     105             :               "RequestMode enumeration value should match Necko CORS mode value.");
     106             : static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(RequestMode::Cors),
     107             :               "RequestMode enumeration value should match Necko CORS mode value.");
     108             : static_assert(nsIHttpChannelInternal::CORS_MODE_NAVIGATE == static_cast<uint32_t>(RequestMode::Navigate),
     109             :               "RequestMode enumeration value should match Necko CORS mode value.");
     110             : 
     111             : static_assert(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW == static_cast<uint32_t>(RequestRedirect::Follow),
     112             :               "RequestRedirect enumeration value should make Necko Redirect mode value.");
     113             : static_assert(nsIHttpChannelInternal::REDIRECT_MODE_ERROR == static_cast<uint32_t>(RequestRedirect::Error),
     114             :               "RequestRedirect enumeration value should make Necko Redirect mode value.");
     115             : static_assert(nsIHttpChannelInternal::REDIRECT_MODE_MANUAL == static_cast<uint32_t>(RequestRedirect::Manual),
     116             :               "RequestRedirect enumeration value should make Necko Redirect mode value.");
     117             : static_assert(3 == static_cast<uint32_t>(RequestRedirect::EndGuard_),
     118             :               "RequestRedirect enumeration value should make Necko Redirect mode value.");
     119             : 
     120             : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT == static_cast<uint32_t>(RequestCache::Default),
     121             :              "RequestCache enumeration value should match Necko Cache mode value.");
     122             : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE == static_cast<uint32_t>(RequestCache::No_store),
     123             :              "RequestCache enumeration value should match Necko Cache mode value.");
     124             : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD == static_cast<uint32_t>(RequestCache::Reload),
     125             :              "RequestCache enumeration value should match Necko Cache mode value.");
     126             : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE == static_cast<uint32_t>(RequestCache::No_cache),
     127             :              "RequestCache enumeration value should match Necko Cache mode value.");
     128             : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE == static_cast<uint32_t>(RequestCache::Force_cache),
     129             :              "RequestCache enumeration value should match Necko Cache mode value.");
     130             : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED == static_cast<uint32_t>(RequestCache::Only_if_cached),
     131             :              "RequestCache enumeration value should match Necko Cache mode value.");
     132             : static_assert(6 == static_cast<uint32_t>(RequestCache::EndGuard_),
     133             :              "RequestCache enumeration value should match Necko Cache mode value.");
     134             : 
     135           3 : static StaticRefPtr<ServiceWorkerManager> gInstance;
     136             : 
     137           0 : struct ServiceWorkerManager::RegistrationDataPerPrincipal final
     138             : {
     139             :   // Ordered list of scopes for glob matching.
     140             :   // Each entry is an absolute URL representing the scope.
     141             :   // Each value of the hash table is an array of an absolute URLs representing
     142             :   // the scopes.
     143             :   //
     144             :   // An array is used for now since the number of controlled scopes per
     145             :   // domain is expected to be relatively low. If that assumption was proved
     146             :   // wrong this should be replaced with a better structure to avoid the
     147             :   // memmoves associated with inserting stuff in the middle of the array.
     148             :   nsTArray<nsCString> mOrderedScopes;
     149             : 
     150             :   // Scope to registration.
     151             :   // The scope should be a fully qualified valid URL.
     152             :   nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mInfos;
     153             : 
     154             :   // Maps scopes to job queues.
     155             :   nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues;
     156             : 
     157             :   // Map scopes to scheduled update timers.
     158             :   nsInterfaceHashtable<nsCStringHashKey, nsITimer> mUpdateTimers;
     159             : };
     160             : 
     161             : namespace {
     162             : 
     163             : nsresult
     164           0 : PopulateRegistrationData(nsIPrincipal* aPrincipal,
     165             :                          const ServiceWorkerRegistrationInfo* aRegistration,
     166             :                          ServiceWorkerRegistrationData& aData)
     167             : {
     168           0 :   MOZ_ASSERT(aPrincipal);
     169           0 :   MOZ_ASSERT(aRegistration);
     170             : 
     171           0 :   if (NS_WARN_IF(!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal())) {
     172           0 :     return NS_ERROR_FAILURE;
     173             :   }
     174             : 
     175           0 :   nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aData.principal());
     176           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     177           0 :     return rv;
     178             :   }
     179             : 
     180           0 :   aData.scope() = aRegistration->mScope;
     181             : 
     182           0 :   RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
     183           0 :   if (NS_WARN_IF(!newest)) {
     184           0 :     return NS_ERROR_FAILURE;
     185             :   }
     186             : 
     187           0 :   if (aRegistration->GetActive()) {
     188           0 :     aData.currentWorkerURL() = aRegistration->GetActive()->ScriptSpec();
     189           0 :     aData.cacheName() = aRegistration->GetActive()->CacheName();
     190           0 :     aData.currentWorkerHandlesFetch() = aRegistration->GetActive()->HandlesFetch();
     191             : 
     192           0 :     aData.currentWorkerInstalledTime() =
     193           0 :       aRegistration->GetActive()->GetInstalledTime();
     194           0 :     aData.currentWorkerActivatedTime() =
     195           0 :       aRegistration->GetActive()->GetActivatedTime();
     196             :   }
     197             : 
     198           0 :   aData.loadFlags() = aRegistration->GetLoadFlags();
     199             : 
     200           0 :   aData.lastUpdateTime() = aRegistration->GetLastUpdateTime();
     201             : 
     202           0 :   return NS_OK;
     203             : }
     204             : 
     205             : class TeardownRunnable final : public Runnable
     206             : {
     207             : public:
     208           0 :   explicit TeardownRunnable(ServiceWorkerManagerChild* aActor)
     209           0 :     : Runnable("dom::workers::TeardownRunnable")
     210           0 :     , mActor(aActor)
     211             :   {
     212           0 :     MOZ_ASSERT(mActor);
     213           0 :   }
     214             : 
     215           0 :   NS_IMETHOD Run() override
     216             :   {
     217           0 :     MOZ_ASSERT(mActor);
     218           0 :     mActor->SendShutdown();
     219           0 :     return NS_OK;
     220             :   }
     221             : 
     222             : private:
     223           0 :   ~TeardownRunnable() {}
     224             : 
     225             :   RefPtr<ServiceWorkerManagerChild> mActor;
     226             : };
     227             : 
     228             : } // namespace
     229             : 
     230             : //////////////////////////
     231             : // ServiceWorkerManager //
     232             : //////////////////////////
     233             : 
     234         101 : NS_IMPL_ADDREF(ServiceWorkerManager)
     235          87 : NS_IMPL_RELEASE(ServiceWorkerManager)
     236             : 
     237          16 : NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
     238          16 :   NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager)
     239           6 :   NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
     240           1 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     241           1 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager)
     242           0 : NS_INTERFACE_MAP_END
     243             : 
     244           3 : ServiceWorkerManager::ServiceWorkerManager()
     245             :   : mActor(nullptr)
     246           3 :   , mShuttingDown(false)
     247             : {
     248           3 : }
     249             : 
     250           0 : ServiceWorkerManager::~ServiceWorkerManager()
     251             : {
     252             :   // The map will assert if it is not empty when destroyed.
     253           0 :   mRegistrationInfos.Clear();
     254           0 :   MOZ_ASSERT(!mActor);
     255           0 : }
     256             : 
     257             : void
     258           3 : ServiceWorkerManager::Init(ServiceWorkerRegistrar* aRegistrar)
     259             : {
     260           6 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     261           3 :   if (obs) {
     262           6 :     DebugOnly<nsresult> rv;
     263           3 :     rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false /* ownsWeak */);
     264           3 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     265             :   }
     266             : 
     267           3 :   if (XRE_IsParentProcess()) {
     268           1 :     MOZ_DIAGNOSTIC_ASSERT(aRegistrar);
     269             : 
     270           2 :     nsTArray<ServiceWorkerRegistrationData> data;
     271           1 :     aRegistrar->GetRegistrations(data);
     272           1 :     LoadRegistrations(data);
     273             : 
     274           1 :     if (obs) {
     275           2 :       DebugOnly<nsresult> rv;
     276           1 :       rv = obs->AddObserver(this, PURGE_SESSION_HISTORY, false /* ownsWeak */);
     277           1 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
     278           1 :       rv = obs->AddObserver(this, PURGE_DOMAIN_DATA, false /* ownsWeak */);
     279           1 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
     280           1 :       rv = obs->AddObserver(this, CLEAR_ORIGIN_DATA, false /* ownsWeak */);
     281           1 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
     282             :     }
     283             :   }
     284             : 
     285           3 :   if (!BackgroundChild::GetOrCreateForCurrentThread(this)) {
     286             :     // Make sure to do this last as our failure cleanup expects Init() to have
     287             :     // executed.
     288           0 :     ActorFailed();
     289             :   }
     290           3 : }
     291             : 
     292             : void
     293           0 : ServiceWorkerManager::MaybeStartShutdown()
     294             : {
     295           0 :   MOZ_ASSERT(NS_IsMainThread());
     296             : 
     297           0 :   if (mShuttingDown) {
     298           0 :     return;
     299             :   }
     300             : 
     301           0 :   mShuttingDown = true;
     302             : 
     303           0 :   for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
     304           0 :     for (auto it2 = it1.UserData()->mUpdateTimers.Iter(); !it2.Done(); it2.Next()) {
     305           0 :       nsCOMPtr<nsITimer> timer = it2.UserData();
     306           0 :       timer->Cancel();
     307             :     }
     308           0 :     it1.UserData()->mUpdateTimers.Clear();
     309             : 
     310           0 :     for (auto it2 = it1.UserData()->mJobQueues.Iter(); !it2.Done(); it2.Next()) {
     311           0 :       RefPtr<ServiceWorkerJobQueue> queue = it2.UserData();
     312           0 :       queue->CancelAll();
     313             :     }
     314           0 :     it1.UserData()->mJobQueues.Clear();
     315             :   }
     316             : 
     317           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     318           0 :   if (obs) {
     319           0 :     obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     320             : 
     321           0 :     if (XRE_IsParentProcess()) {
     322           0 :       obs->RemoveObserver(this, PURGE_SESSION_HISTORY);
     323           0 :       obs->RemoveObserver(this, PURGE_DOMAIN_DATA);
     324           0 :       obs->RemoveObserver(this, CLEAR_ORIGIN_DATA);
     325             :     }
     326             :   }
     327             : 
     328           0 :   mPendingOperations.Clear();
     329             : 
     330           0 :   if (!mActor) {
     331           0 :     return;
     332             :   }
     333             : 
     334           0 :   mActor->ManagerShuttingDown();
     335             : 
     336           0 :   RefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
     337           0 :   nsresult rv = NS_DispatchToMainThread(runnable);
     338           0 :   Unused << NS_WARN_IF(NS_FAILED(rv));
     339           0 :   mActor = nullptr;
     340             : }
     341             : 
     342             : class ServiceWorkerResolveWindowPromiseOnRegisterCallback final : public ServiceWorkerJob::Callback
     343             : {
     344             :   RefPtr<nsPIDOMWindowInner> mWindow;
     345             :   // The promise "returned" by the call to Update up to
     346             :   // navigator.serviceWorker.register().
     347             :   RefPtr<Promise> mPromise;
     348             : 
     349           0 :   ~ServiceWorkerResolveWindowPromiseOnRegisterCallback()
     350           0 :   {}
     351             : 
     352             :   virtual void
     353           0 :   JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override
     354             :   {
     355           0 :     AssertIsOnMainThread();
     356           0 :     MOZ_ASSERT(aJob);
     357             : 
     358           0 :     if (aStatus.Failed()) {
     359           0 :       mPromise->MaybeReject(aStatus);
     360           0 :       return;
     361             :     }
     362             : 
     363           0 :     MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Register);
     364             :     RefPtr<ServiceWorkerRegisterJob> registerJob =
     365           0 :       static_cast<ServiceWorkerRegisterJob*>(aJob);
     366           0 :     RefPtr<ServiceWorkerRegistrationInfo> reg = registerJob->GetRegistration();
     367             : 
     368             :     RefPtr<ServiceWorkerRegistration> swr =
     369           0 :       mWindow->GetServiceWorkerRegistration(NS_ConvertUTF8toUTF16(reg->mScope));
     370           0 :     mPromise->MaybeResolve(swr);
     371             :   }
     372             : 
     373             : public:
     374           0 :   ServiceWorkerResolveWindowPromiseOnRegisterCallback(nsPIDOMWindowInner* aWindow,
     375             :                                                       Promise* aPromise)
     376           0 :     : mWindow(aWindow)
     377           0 :     , mPromise(aPromise)
     378           0 :   {}
     379             : 
     380           0 :   NS_INLINE_DECL_REFCOUNTING(ServiceWorkerResolveWindowPromiseOnRegisterCallback, override)
     381             : };
     382             : 
     383             : namespace {
     384             : 
     385             : class PropagateSoftUpdateRunnable final : public Runnable
     386             : {
     387             : public:
     388           0 :   PropagateSoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
     389             :                               const nsAString& aScope)
     390           0 :     : Runnable("dom::workers::PropagateSoftUpdateRunnable")
     391             :     , mOriginAttributes(aOriginAttributes)
     392           0 :     , mScope(aScope)
     393           0 :   {}
     394             : 
     395           0 :   NS_IMETHOD Run() override
     396             :   {
     397           0 :     AssertIsOnMainThread();
     398             : 
     399           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     400           0 :     if (swm) {
     401           0 :       swm->PropagateSoftUpdate(mOriginAttributes, mScope);
     402             :     }
     403             : 
     404           0 :     return NS_OK;
     405             :   }
     406             : 
     407             : private:
     408           0 :   ~PropagateSoftUpdateRunnable()
     409           0 :   {}
     410             : 
     411             :   const OriginAttributes mOriginAttributes;
     412             :   const nsString mScope;
     413             : };
     414             : 
     415             : class PropagateUnregisterRunnable final : public Runnable
     416             : {
     417             : public:
     418           0 :   PropagateUnregisterRunnable(nsIPrincipal* aPrincipal,
     419             :                               nsIServiceWorkerUnregisterCallback* aCallback,
     420             :                               const nsAString& aScope)
     421           0 :     : Runnable("dom::workers::PropagateUnregisterRunnable")
     422             :     , mPrincipal(aPrincipal)
     423             :     , mCallback(aCallback)
     424           0 :     , mScope(aScope)
     425             :   {
     426           0 :     MOZ_ASSERT(aPrincipal);
     427           0 :   }
     428             : 
     429           0 :   NS_IMETHOD Run() override
     430             :   {
     431           0 :     AssertIsOnMainThread();
     432             : 
     433           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     434           0 :     if (swm) {
     435           0 :       swm->PropagateUnregister(mPrincipal, mCallback, mScope);
     436             :     }
     437             : 
     438           0 :     return NS_OK;
     439             :   }
     440             : 
     441             : private:
     442           0 :   ~PropagateUnregisterRunnable()
     443           0 :   {}
     444             : 
     445             :   nsCOMPtr<nsIPrincipal> mPrincipal;
     446             :   nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
     447             :   const nsString mScope;
     448             : };
     449             : 
     450             : class RemoveRunnable final : public Runnable
     451             : {
     452             : public:
     453           0 :   explicit RemoveRunnable(const nsACString& aHost)
     454           0 :     : Runnable("dom::workers::RemoveRunnable")
     455             :   {
     456           0 :   }
     457             : 
     458           0 :   NS_IMETHOD Run() override
     459             :   {
     460           0 :     AssertIsOnMainThread();
     461             : 
     462           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     463           0 :     if (swm) {
     464           0 :       swm->Remove(mHost);
     465             :     }
     466             : 
     467           0 :     return NS_OK;
     468             :   }
     469             : 
     470             : private:
     471           0 :   ~RemoveRunnable()
     472           0 :   {}
     473             : 
     474             :   const nsCString mHost;
     475             : };
     476             : 
     477             : class PropagateRemoveRunnable final : public Runnable
     478             : {
     479             : public:
     480           0 :   explicit PropagateRemoveRunnable(const nsACString& aHost)
     481           0 :     : Runnable("dom::workers::PropagateRemoveRunnable")
     482             :   {
     483           0 :   }
     484             : 
     485           0 :   NS_IMETHOD Run() override
     486             :   {
     487           0 :     AssertIsOnMainThread();
     488             : 
     489           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     490           0 :     if (swm) {
     491           0 :       swm->PropagateRemove(mHost);
     492             :     }
     493             : 
     494           0 :     return NS_OK;
     495             :   }
     496             : 
     497             : private:
     498           0 :   ~PropagateRemoveRunnable()
     499           0 :   {}
     500             : 
     501             :   const nsCString mHost;
     502             : };
     503             : 
     504             : class PropagateRemoveAllRunnable final : public Runnable
     505             : {
     506             : public:
     507           0 :   PropagateRemoveAllRunnable()
     508           0 :     : Runnable("dom::workers::PropagateRemoveAllRunnable")
     509             :   {
     510           0 :   }
     511             : 
     512           0 :   NS_IMETHOD Run() override
     513             :   {
     514           0 :     AssertIsOnMainThread();
     515             : 
     516           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     517           0 :     if (swm) {
     518           0 :       swm->PropagateRemoveAll();
     519             :     }
     520             : 
     521           0 :     return NS_OK;
     522             :   }
     523             : 
     524             : private:
     525           0 :   ~PropagateRemoveAllRunnable()
     526           0 :   {}
     527             : };
     528             : 
     529             : class PromiseResolverCallback final : public ServiceWorkerUpdateFinishCallback
     530             : {
     531             : public:
     532           0 :   PromiseResolverCallback(ServiceWorkerUpdateFinishCallback* aCallback,
     533             :                           GenericPromise::Private* aPromise)
     534           0 :     : mCallback(aCallback)
     535           0 :     , mPromise(aPromise)
     536             :   {
     537           0 :     MOZ_DIAGNOSTIC_ASSERT(mPromise);
     538           0 :   }
     539             : 
     540           0 :   void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override
     541             :   {
     542           0 :     MOZ_DIAGNOSTIC_ASSERT(mPromise);
     543             : 
     544           0 :     if (mCallback) {
     545           0 :       mCallback->UpdateSucceeded(aInfo);
     546             :     }
     547             : 
     548           0 :     MaybeResolve();
     549           0 :   }
     550             : 
     551           0 :   void UpdateFailed(ErrorResult& aStatus) override
     552             :   {
     553           0 :     MOZ_DIAGNOSTIC_ASSERT(mPromise);
     554             : 
     555           0 :     if (mCallback) {
     556           0 :       mCallback->UpdateFailed(aStatus);
     557             :     }
     558             : 
     559           0 :     MaybeResolve();
     560           0 :   }
     561             : 
     562             : private:
     563           0 :   ~PromiseResolverCallback()
     564           0 :   {
     565           0 :     MaybeResolve();
     566           0 :   }
     567             : 
     568             :   void
     569           0 :   MaybeResolve()
     570             :   {
     571           0 :     if (mPromise) {
     572           0 :       mPromise->Resolve(true, __func__);
     573           0 :       mPromise = nullptr;
     574             :     }
     575           0 :   }
     576             : 
     577             :   RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
     578             :   RefPtr<GenericPromise::Private> mPromise;
     579             : };
     580             : 
     581             : // This runnable is used for 2 different tasks:
     582             : // - to postpone the SoftUpdate() until the IPC SWM actor is created
     583             : //   (aInternalMethod == false)
     584             : // - to call the 'real' SoftUpdate when the ServiceWorkerUpdaterChild is
     585             : //   notified by the parent (aInternalMethod == true)
     586             : class SoftUpdateRunnable final : public CancelableRunnable
     587             : {
     588             : public:
     589           0 :   SoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
     590             :                      const nsACString& aScope,
     591             :                      bool aInternalMethod,
     592             :                      GenericPromise::Private* aPromise)
     593           0 :     : CancelableRunnable("dom::workers::SoftUpdateRunnable")
     594             :     , mAttrs(aOriginAttributes)
     595             :     , mScope(aScope)
     596             :     , mInternalMethod(aInternalMethod)
     597           0 :     , mPromise(aPromise)
     598           0 :   {}
     599             : 
     600           0 :   NS_IMETHOD Run() override
     601             :   {
     602           0 :     AssertIsOnMainThread();
     603             : 
     604           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     605           0 :     if (!swm) {
     606           0 :       return NS_ERROR_FAILURE;
     607             :     }
     608             : 
     609           0 :     if (mInternalMethod) {
     610             :       RefPtr<PromiseResolverCallback> callback =
     611           0 :         new PromiseResolverCallback(nullptr, mPromise);
     612           0 :       mPromise = nullptr;
     613             : 
     614           0 :       swm->SoftUpdateInternal(mAttrs, mScope, callback);
     615             :     } else {
     616           0 :       swm->SoftUpdate(mAttrs, mScope);
     617             :     }
     618             : 
     619           0 :     return NS_OK;
     620             :   }
     621             : 
     622             :   nsresult
     623           0 :   Cancel() override
     624             :   {
     625           0 :     mPromise = nullptr;
     626           0 :     return NS_OK;
     627             :   }
     628             : 
     629             : private:
     630           0 :   ~SoftUpdateRunnable()
     631           0 :   {
     632           0 :     if (mPromise) {
     633           0 :       mPromise->Resolve(true, __func__);
     634             :     }
     635           0 :   }
     636             : 
     637             :   const OriginAttributes mAttrs;
     638             :   const nsCString mScope;
     639             :   bool mInternalMethod;
     640             : 
     641             :   RefPtr<GenericPromise::Private> mPromise;
     642             : };
     643             : 
     644             : // This runnable is used for 3 different tasks:
     645             : // - to postpone the Update() until the IPC SWM actor is created
     646             : //   (aType == ePostpone)
     647             : // - to call the 'real' Update when the ServiceWorkerUpdaterChild is
     648             : //   notified by the parent (aType == eSuccess)
     649             : // - an error must be propagated (aType == eFailure)
     650             : class UpdateRunnable final : public CancelableRunnable
     651             : {
     652             : public:
     653             :   enum Type {
     654             :     ePostpone,
     655             :     eSuccess,
     656             :     eFailure,
     657             :   };
     658             : 
     659           0 :   UpdateRunnable(nsIPrincipal* aPrincipal,
     660             :                  const nsACString& aScope,
     661             :                  ServiceWorkerUpdateFinishCallback* aCallback,
     662             :                  Type aType,
     663             :                  GenericPromise::Private* aPromise)
     664           0 :     : CancelableRunnable("dom::workers::UpdateRunnable")
     665             :     , mPrincipal(aPrincipal)
     666             :     , mScope(aScope)
     667             :     , mCallback(aCallback)
     668             :     , mType(aType)
     669           0 :     , mPromise(aPromise)
     670           0 :   {}
     671             : 
     672           0 :   NS_IMETHOD Run() override
     673             :   {
     674           0 :     AssertIsOnMainThread();
     675             : 
     676           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     677           0 :     if (!swm) {
     678           0 :       return NS_ERROR_FAILURE;
     679             :     }
     680             : 
     681           0 :     if (mType == ePostpone) {
     682           0 :       swm->Update(mPrincipal, mScope, mCallback);
     683           0 :       return NS_OK;
     684             :     }
     685             : 
     686           0 :     MOZ_ASSERT(mPromise);
     687             : 
     688             :     RefPtr<PromiseResolverCallback> callback =
     689           0 :       new PromiseResolverCallback(mCallback, mPromise);
     690           0 :     mPromise = nullptr;
     691             : 
     692           0 :     if (mType == eSuccess) {
     693           0 :       swm->UpdateInternal(mPrincipal, mScope, callback);
     694           0 :       return NS_OK;
     695             :     }
     696             : 
     697           0 :     ErrorResult error(NS_ERROR_DOM_ABORT_ERR);
     698           0 :     callback->UpdateFailed(error);
     699           0 :     return NS_OK;
     700             :   }
     701             : 
     702             :   nsresult
     703           0 :   Cancel() override
     704             :   {
     705           0 :     mPromise = nullptr;
     706           0 :     return NS_OK;
     707             :   }
     708             : 
     709             : private:
     710           0 :   ~UpdateRunnable()
     711           0 :   {
     712           0 :     if (mPromise) {
     713           0 :       mPromise->Resolve(true, __func__);
     714             :     }
     715           0 :   }
     716             : 
     717             :   nsCOMPtr<nsIPrincipal> mPrincipal;
     718             :   const nsCString mScope;
     719             :   RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
     720             :   Type mType;
     721             : 
     722             :   RefPtr<GenericPromise::Private> mPromise;
     723             : };
     724             : 
     725             : class ResolvePromiseRunnable final : public CancelableRunnable
     726             : {
     727             : public:
     728           0 :   explicit ResolvePromiseRunnable(GenericPromise::Private* aPromise)
     729           0 :     : CancelableRunnable("dom::workers::ResolvePromiseRunnable")
     730           0 :     , mPromise(aPromise)
     731           0 :   {}
     732             : 
     733             :   NS_IMETHOD
     734           0 :   Run() override
     735             :   {
     736           0 :     MaybeResolve();
     737           0 :     return NS_OK;
     738             :   }
     739             : 
     740             :   nsresult
     741           0 :   Cancel() override
     742             :   {
     743           0 :     mPromise = nullptr;
     744           0 :     return NS_OK;
     745             :   }
     746             : 
     747             : private:
     748           0 :   ~ResolvePromiseRunnable()
     749           0 :   {
     750           0 :     MaybeResolve();
     751           0 :   }
     752             : 
     753             :   void
     754           0 :   MaybeResolve()
     755             :   {
     756           0 :     if (mPromise) {
     757           0 :       mPromise->Resolve(true, __func__);
     758           0 :       mPromise = nullptr;
     759             :     }
     760           0 :   }
     761             : 
     762             :   RefPtr<GenericPromise::Private> mPromise;
     763             : };
     764             : 
     765             : } // namespace
     766             : 
     767             : // This function implements parts of the step 3 of the following algorithm:
     768             : // https://w3c.github.io/webappsec/specs/powerfulfeatures/#settings-secure
     769             : static bool
     770           0 : IsFromAuthenticatedOrigin(nsIDocument* aDoc)
     771             : {
     772           0 :   MOZ_ASSERT(aDoc);
     773           0 :   nsCOMPtr<nsIDocument> doc(aDoc);
     774           0 :   nsCOMPtr<nsIContentSecurityManager> csm = do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
     775           0 :   if (NS_WARN_IF(!csm)) {
     776           0 :     return false;
     777             :   }
     778             : 
     779           0 :   while (doc && !nsContentUtils::IsChromeDoc(doc)) {
     780           0 :     bool trustworthyOrigin = false;
     781             : 
     782             :     // The origin of the document may be different from the document URI
     783             :     // itself.  Check the principal, not the document URI itself.
     784           0 :     nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal();
     785             : 
     786             :     // The check for IsChromeDoc() above should mean we never see a system
     787             :     // principal inside the loop.
     788           0 :     MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(documentPrincipal));
     789             : 
     790           0 :     csm->IsOriginPotentiallyTrustworthy(documentPrincipal, &trustworthyOrigin);
     791           0 :     if (!trustworthyOrigin) {
     792           0 :       return false;
     793             :     }
     794             : 
     795           0 :     doc = doc->GetParentDocument();
     796             :   }
     797           0 :   return true;
     798             : }
     799             : 
     800             : // If we return an error code here, the ServiceWorkerContainer will
     801             : // automatically reject the Promise.
     802             : NS_IMETHODIMP
     803           0 : ServiceWorkerManager::Register(mozIDOMWindow* aWindow,
     804             :                                nsIURI* aScopeURI,
     805             :                                nsIURI* aScriptURI,
     806             :                                nsLoadFlags aLoadFlags,
     807             :                                nsISupports** aPromise)
     808             : {
     809           0 :   AssertIsOnMainThread();
     810             : 
     811           0 :   if (NS_WARN_IF(!aWindow)) {
     812           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
     813             :   }
     814             : 
     815           0 :   auto* window = nsPIDOMWindowInner::From(aWindow);
     816           0 :   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
     817           0 :   if (!doc) {
     818           0 :     return NS_ERROR_FAILURE;
     819             :   }
     820             : 
     821             :   // Don't allow service workers to register when the *document* is chrome.
     822           0 :   if (NS_WARN_IF(nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()))) {
     823           0 :     return NS_ERROR_DOM_SECURITY_ERR;
     824             :   }
     825             : 
     826           0 :   nsCOMPtr<nsPIDOMWindowOuter> outerWindow = window->GetOuterWindow();
     827             :   bool serviceWorkersTestingEnabled =
     828           0 :     outerWindow->GetServiceWorkersTestingEnabled();
     829             : 
     830             :   bool authenticatedOrigin;
     831           0 :   if (Preferences::GetBool("dom.serviceWorkers.testing.enabled") ||
     832             :       serviceWorkersTestingEnabled) {
     833           0 :     authenticatedOrigin = true;
     834             :   } else {
     835           0 :     authenticatedOrigin = IsFromAuthenticatedOrigin(doc);
     836             :   }
     837             : 
     838           0 :   if (!authenticatedOrigin) {
     839           0 :     NS_WARNING("ServiceWorker registration from insecure websites is not allowed.");
     840           0 :     return NS_ERROR_DOM_SECURITY_ERR;
     841             :   }
     842             : 
     843             :   // Data URLs are not allowed.
     844           0 :   nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal();
     845             : 
     846           0 :   nsresult rv = documentPrincipal->CheckMayLoad(aScriptURI, true /* report */,
     847           0 :                                                 false /* allowIfInheritsPrincipal */);
     848           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     849           0 :     return NS_ERROR_DOM_SECURITY_ERR;
     850             :   }
     851             : 
     852             :   // Check content policy.
     853           0 :   int16_t decision = nsIContentPolicy::ACCEPT;
     854           0 :   rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER,
     855             :                                  aScriptURI,
     856             :                                  documentPrincipal,
     857             :                                  doc,
     858           0 :                                  EmptyCString(),
     859             :                                  nullptr,
     860           0 :                                  &decision);
     861           0 :   NS_ENSURE_SUCCESS(rv, rv);
     862           0 :   if (NS_WARN_IF(decision != nsIContentPolicy::ACCEPT)) {
     863           0 :     return NS_ERROR_CONTENT_BLOCKED;
     864             :   }
     865             : 
     866             : 
     867           0 :   rv = documentPrincipal->CheckMayLoad(aScopeURI, true /* report */,
     868           0 :                                        false /* allowIfInheritsPrinciple */);
     869           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     870           0 :     return NS_ERROR_DOM_SECURITY_ERR;
     871             :   }
     872             : 
     873             :   // The IsOriginPotentiallyTrustworthy() check allows file:// and possibly other
     874             :   // URI schemes.  We need to explicitly only allows http and https schemes.
     875             :   // Note, we just use the aScriptURI here for the check since its already
     876             :   // been verified as same origin with the document principal.  This also
     877             :   // is a good block against accidentally allowing blob: script URIs which
     878             :   // might inherit the origin.
     879           0 :   bool isHttp = false;
     880           0 :   bool isHttps = false;
     881           0 :   aScriptURI->SchemeIs("http", &isHttp);
     882           0 :   aScriptURI->SchemeIs("https", &isHttps);
     883           0 :   if (NS_WARN_IF(!isHttp && !isHttps)) {
     884           0 :     return NS_ERROR_DOM_SECURITY_ERR;
     885             :   }
     886             : 
     887           0 :   nsCString cleanedScope;
     888           0 :   rv = aScopeURI->GetSpecIgnoringRef(cleanedScope);
     889           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     890           0 :     return NS_ERROR_FAILURE;
     891             :   }
     892             : 
     893           0 :   nsAutoCString spec;
     894           0 :   rv = aScriptURI->GetSpecIgnoringRef(spec);
     895           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     896           0 :     return rv;
     897             :   }
     898             : 
     899           0 :   nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
     900           0 :   ErrorResult result;
     901           0 :   RefPtr<Promise> promise = Promise::Create(sgo, result);
     902           0 :   if (result.Failed()) {
     903           0 :     return result.StealNSResult();
     904             :   }
     905             : 
     906           0 :   nsAutoCString scopeKey;
     907           0 :   rv = PrincipalToScopeKey(documentPrincipal, scopeKey);
     908           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     909           0 :     return rv;
     910             :   }
     911             : 
     912           0 :   AddRegisteringDocument(cleanedScope, doc);
     913             : 
     914           0 :   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
     915           0 :                                                             cleanedScope);
     916             : 
     917             :   RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb =
     918           0 :     new ServiceWorkerResolveWindowPromiseOnRegisterCallback(window, promise);
     919             : 
     920           0 :   nsCOMPtr<nsILoadGroup> docLoadGroup = doc->GetDocumentLoadGroup();
     921             :   RefPtr<WorkerLoadInfo::InterfaceRequestor> ir =
     922           0 :     new WorkerLoadInfo::InterfaceRequestor(documentPrincipal, docLoadGroup);
     923           0 :   ir->MaybeAddTabChild(docLoadGroup);
     924             : 
     925             :   // Create a load group that is separate from, yet related to, the document's load group.
     926             :   // This allows checks for interfaces like nsILoadContext to yield the values used by the
     927             :   // the document, yet will not cancel the update job if the document's load group is cancelled.
     928           0 :   nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
     929           0 :   MOZ_ALWAYS_SUCCEEDS(loadGroup->SetNotificationCallbacks(ir));
     930             : 
     931             :   RefPtr<ServiceWorkerRegisterJob> job =
     932             :     new ServiceWorkerRegisterJob(documentPrincipal, cleanedScope, spec,
     933           0 :                                  loadGroup, aLoadFlags);
     934           0 :   job->AppendResultCallback(cb);
     935           0 :   queue->ScheduleJob(job);
     936             : 
     937           0 :   AssertIsOnMainThread();
     938           0 :   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REGISTRATIONS, 1);
     939             : 
     940           0 :   ContentChild* contentChild = ContentChild::GetSingleton();
     941           0 :   if (contentChild &&
     942           0 :       contentChild->GetRemoteType().EqualsLiteral(FILE_REMOTE_TYPE)) {
     943           0 :     nsString message(NS_LITERAL_STRING("ServiceWorker registered by document "
     944             :                                        "embedded in a file:/// URI.  This may "
     945           0 :                                        "result in unexpected behavior."));
     946           0 :     ReportToAllClients(cleanedScope, message, EmptyString(),
     947           0 :                        EmptyString(), 0, 0, nsIScriptError::warningFlag);
     948           0 :     Telemetry::Accumulate(Telemetry::FILE_EMBEDDED_SERVICEWORKERS, 1);
     949             :   }
     950             : 
     951           0 :   promise.forget(aPromise);
     952           0 :   return NS_OK;
     953             : }
     954             : 
     955             : void
     956           0 : ServiceWorkerManager::AppendPendingOperation(nsIRunnable* aRunnable)
     957             : {
     958           0 :   MOZ_ASSERT(!mActor);
     959           0 :   MOZ_ASSERT(aRunnable);
     960             : 
     961           0 :   if (!mShuttingDown) {
     962           0 :     mPendingOperations.AppendElement(aRunnable);
     963             :   }
     964           0 : }
     965             : 
     966             : /*
     967             :  * Implements the async aspects of the getRegistrations algorithm.
     968             :  */
     969           0 : class GetRegistrationsRunnable final : public Runnable
     970             : {
     971             :   nsCOMPtr<nsPIDOMWindowInner> mWindow;
     972             :   RefPtr<Promise> mPromise;
     973             : public:
     974           0 :   GetRegistrationsRunnable(nsPIDOMWindowInner* aWindow, Promise* aPromise)
     975           0 :     : Runnable("dom::workers::GetRegistrationsRunnable")
     976             :     , mWindow(aWindow)
     977           0 :     , mPromise(aPromise)
     978           0 :   {}
     979             : 
     980             :   NS_IMETHOD
     981           0 :   Run() override
     982             :   {
     983           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     984           0 :     if (!swm) {
     985           0 :       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
     986           0 :       return NS_OK;
     987             :     }
     988             : 
     989           0 :     nsIDocument* doc = mWindow->GetExtantDoc();
     990           0 :     if (!doc) {
     991           0 :       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
     992           0 :       return NS_OK;
     993             :     }
     994             : 
     995           0 :     nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
     996           0 :     if (!docURI) {
     997           0 :       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
     998           0 :       return NS_OK;
     999             :     }
    1000             : 
    1001           0 :     nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
    1002           0 :     if (!principal) {
    1003           0 :       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
    1004           0 :       return NS_OK;
    1005             :     }
    1006             : 
    1007           0 :     nsTArray<RefPtr<ServiceWorkerRegistration>> array;
    1008             : 
    1009           0 :     if (NS_WARN_IF(!BasePrincipal::Cast(principal)->IsCodebasePrincipal())) {
    1010           0 :       return NS_OK;
    1011             :     }
    1012             : 
    1013           0 :     nsAutoCString scopeKey;
    1014           0 :     nsresult rv = swm->PrincipalToScopeKey(principal, scopeKey);
    1015           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1016           0 :       return rv;
    1017             :     }
    1018             : 
    1019             :     ServiceWorkerManager::RegistrationDataPerPrincipal* data;
    1020           0 :     if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
    1021           0 :       mPromise->MaybeResolve(array);
    1022           0 :       return NS_OK;
    1023             :     }
    1024             : 
    1025           0 :     for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) {
    1026             :       RefPtr<ServiceWorkerRegistrationInfo> info =
    1027           0 :         data->mInfos.GetWeak(data->mOrderedScopes[i]);
    1028           0 :       if (info->mPendingUninstall) {
    1029           0 :         continue;
    1030             :       }
    1031             : 
    1032           0 :       NS_ConvertUTF8toUTF16 scope(data->mOrderedScopes[i]);
    1033             : 
    1034           0 :       nsCOMPtr<nsIURI> scopeURI;
    1035           0 :       nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope, nullptr, nullptr);
    1036           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1037           0 :         mPromise->MaybeReject(rv);
    1038           0 :         break;
    1039             :       }
    1040             : 
    1041           0 :       rv = principal->CheckMayLoad(scopeURI, true /* report */,
    1042           0 :                                    false /* allowIfInheritsPrincipal */);
    1043           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1044           0 :         continue;
    1045             :       }
    1046             : 
    1047             :       RefPtr<ServiceWorkerRegistration> swr =
    1048           0 :         mWindow->GetServiceWorkerRegistration(scope);
    1049             : 
    1050           0 :       array.AppendElement(swr);
    1051             :     }
    1052             : 
    1053           0 :     mPromise->MaybeResolve(array);
    1054           0 :     return NS_OK;
    1055             :   }
    1056             : };
    1057             : 
    1058             : // If we return an error code here, the ServiceWorkerContainer will
    1059             : // automatically reject the Promise.
    1060             : NS_IMETHODIMP
    1061           0 : ServiceWorkerManager::GetRegistrations(mozIDOMWindow* aWindow,
    1062             :                                        nsISupports** aPromise)
    1063             : {
    1064           0 :   AssertIsOnMainThread();
    1065             : 
    1066           0 :   if (NS_WARN_IF(!aWindow)) {
    1067           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1068             :   }
    1069             : 
    1070           0 :   auto* window = nsPIDOMWindowInner::From(aWindow);
    1071           0 :   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
    1072           0 :   if (NS_WARN_IF(!doc)) {
    1073           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1074             :   }
    1075             : 
    1076             :   // Don't allow service workers to register when the *document* is chrome for
    1077             :   // now.
    1078           0 :   MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
    1079             : 
    1080           0 :   nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
    1081           0 :   ErrorResult result;
    1082           0 :   RefPtr<Promise> promise = Promise::Create(sgo, result);
    1083           0 :   if (result.Failed()) {
    1084           0 :     return result.StealNSResult();
    1085             :   }
    1086             : 
    1087             :   nsCOMPtr<nsIRunnable> runnable =
    1088           0 :     new GetRegistrationsRunnable(window, promise);
    1089           0 :   promise.forget(aPromise);
    1090           0 :   return NS_DispatchToCurrentThread(runnable);
    1091             : }
    1092             : 
    1093             : /*
    1094             :  * Implements the async aspects of the getRegistration algorithm.
    1095             :  */
    1096           0 : class GetRegistrationRunnable final : public Runnable
    1097             : {
    1098             :   nsCOMPtr<nsPIDOMWindowInner> mWindow;
    1099             :   RefPtr<Promise> mPromise;
    1100             :   nsString mDocumentURL;
    1101             : 
    1102             : public:
    1103           0 :   GetRegistrationRunnable(nsPIDOMWindowInner* aWindow,
    1104             :                           Promise* aPromise,
    1105             :                           const nsAString& aDocumentURL)
    1106           0 :     : Runnable("dom::workers::GetRegistrationRunnable")
    1107             :     , mWindow(aWindow)
    1108             :     , mPromise(aPromise)
    1109           0 :     , mDocumentURL(aDocumentURL)
    1110           0 :   {}
    1111             : 
    1112             :   NS_IMETHOD
    1113           0 :   Run() override
    1114             :   {
    1115           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    1116           0 :     if (!swm) {
    1117           0 :       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
    1118           0 :       return NS_OK;
    1119             :     }
    1120             : 
    1121           0 :     nsIDocument* doc = mWindow->GetExtantDoc();
    1122           0 :     if (!doc) {
    1123           0 :       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
    1124           0 :       return NS_OK;
    1125             :     }
    1126             : 
    1127           0 :     nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
    1128           0 :     if (!docURI) {
    1129           0 :       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
    1130           0 :       return NS_OK;
    1131             :     }
    1132             : 
    1133           0 :     nsCOMPtr<nsIURI> uri;
    1134           0 :     nsresult rv = NS_NewURI(getter_AddRefs(uri), mDocumentURL, nullptr, docURI);
    1135           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1136           0 :       mPromise->MaybeReject(rv);
    1137           0 :       return NS_OK;
    1138             :     }
    1139             : 
    1140           0 :     nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
    1141           0 :     if (!principal) {
    1142           0 :       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
    1143           0 :       return NS_OK;
    1144             :     }
    1145             : 
    1146           0 :     rv = principal->CheckMayLoad(uri, true /* report */,
    1147           0 :                                  false /* allowIfInheritsPrinciple */);
    1148           0 :     if (NS_FAILED(rv)) {
    1149           0 :       mPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
    1150           0 :       return NS_OK;
    1151             :     }
    1152             : 
    1153             :     RefPtr<ServiceWorkerRegistrationInfo> registration =
    1154           0 :       swm->GetServiceWorkerRegistrationInfo(principal, uri);
    1155             : 
    1156           0 :     if (!registration) {
    1157           0 :       mPromise->MaybeResolveWithUndefined();
    1158           0 :       return NS_OK;
    1159             :     }
    1160             : 
    1161           0 :     NS_ConvertUTF8toUTF16 scope(registration->mScope);
    1162             :     RefPtr<ServiceWorkerRegistration> swr =
    1163           0 :       mWindow->GetServiceWorkerRegistration(scope);
    1164           0 :     mPromise->MaybeResolve(swr);
    1165             : 
    1166           0 :     return NS_OK;
    1167             :   }
    1168             : };
    1169             : 
    1170             : // If we return an error code here, the ServiceWorkerContainer will
    1171             : // automatically reject the Promise.
    1172             : NS_IMETHODIMP
    1173           0 : ServiceWorkerManager::GetRegistration(mozIDOMWindow* aWindow,
    1174             :                                       const nsAString& aDocumentURL,
    1175             :                                       nsISupports** aPromise)
    1176             : {
    1177           0 :   AssertIsOnMainThread();
    1178             : 
    1179           0 :   if (NS_WARN_IF(!aWindow)) {
    1180           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1181             :   }
    1182             : 
    1183           0 :   auto* window = nsPIDOMWindowInner::From(aWindow);
    1184           0 :   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
    1185           0 :   if (NS_WARN_IF(!doc)) {
    1186           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1187             :   }
    1188             : 
    1189             :   // Don't allow service workers to register when the *document* is chrome for
    1190             :   // now.
    1191           0 :   MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
    1192             : 
    1193           0 :   nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
    1194           0 :   ErrorResult result;
    1195           0 :   RefPtr<Promise> promise = Promise::Create(sgo, result);
    1196           0 :   if (result.Failed()) {
    1197           0 :     return result.StealNSResult();
    1198             :   }
    1199             : 
    1200             :   nsCOMPtr<nsIRunnable> runnable =
    1201           0 :     new GetRegistrationRunnable(window, promise, aDocumentURL);
    1202           0 :   promise.forget(aPromise);
    1203           0 :   return NS_DispatchToCurrentThread(runnable);
    1204             : }
    1205             : 
    1206           0 : class GetReadyPromiseRunnable final : public Runnable
    1207             : {
    1208             :   nsCOMPtr<nsPIDOMWindowInner> mWindow;
    1209             :   RefPtr<Promise> mPromise;
    1210             : 
    1211             : public:
    1212           0 :   GetReadyPromiseRunnable(nsPIDOMWindowInner* aWindow, Promise* aPromise)
    1213           0 :     : Runnable("dom::workers::GetReadyPromiseRunnable")
    1214             :     , mWindow(aWindow)
    1215           0 :     , mPromise(aPromise)
    1216           0 :   {}
    1217             : 
    1218             :   NS_IMETHOD
    1219           0 :   Run() override
    1220             :   {
    1221           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    1222           0 :     if (!swm) {
    1223           0 :       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
    1224           0 :       return NS_OK;
    1225             :     }
    1226             : 
    1227           0 :     nsIDocument* doc = mWindow->GetExtantDoc();
    1228           0 :     if (!doc) {
    1229           0 :       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
    1230           0 :       return NS_OK;
    1231             :     }
    1232             : 
    1233           0 :     nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
    1234           0 :     if (!docURI) {
    1235           0 :       mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
    1236           0 :       return NS_OK;
    1237             :     }
    1238             : 
    1239           0 :     if (!swm->CheckReadyPromise(mWindow, docURI, mPromise)) {
    1240           0 :       swm->StorePendingReadyPromise(mWindow, docURI, mPromise);
    1241             :     }
    1242             : 
    1243           0 :     return NS_OK;
    1244             :   }
    1245             : };
    1246             : 
    1247             : NS_IMETHODIMP
    1248           0 : ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes,
    1249             :                                     const nsACString& aScope,
    1250             :                                     uint32_t aDataLength,
    1251             :                                     uint8_t* aDataBytes,
    1252             :                                     uint8_t optional_argc)
    1253             : {
    1254           0 :   if (optional_argc == 2) {
    1255           0 :     nsTArray<uint8_t> data;
    1256           0 :     if (!data.InsertElementsAt(0, aDataBytes, aDataLength, fallible)) {
    1257           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1258             :     }
    1259           0 :     return SendPushEvent(aOriginAttributes, aScope, EmptyString(), Some(data));
    1260             :   }
    1261           0 :   MOZ_ASSERT(optional_argc == 0);
    1262           0 :   return SendPushEvent(aOriginAttributes, aScope, EmptyString(), Nothing());
    1263             : }
    1264             : 
    1265             : nsresult
    1266           0 : ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes,
    1267             :                                     const nsACString& aScope,
    1268             :                                     const nsAString& aMessageId,
    1269             :                                     const Maybe<nsTArray<uint8_t>>& aData)
    1270             : {
    1271           0 :   OriginAttributes attrs;
    1272           0 :   if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
    1273           0 :     return NS_ERROR_INVALID_ARG;
    1274             :   }
    1275             : 
    1276           0 :   ServiceWorkerInfo* serviceWorker = GetActiveWorkerInfoForScope(attrs, aScope);
    1277           0 :   if (NS_WARN_IF(!serviceWorker)) {
    1278           0 :     return NS_ERROR_FAILURE;
    1279             :   }
    1280             : 
    1281             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    1282           0 :     GetRegistration(serviceWorker->Principal(), aScope);
    1283           0 :   MOZ_DIAGNOSTIC_ASSERT(registration);
    1284             : 
    1285           0 :   return serviceWorker->WorkerPrivate()->SendPushEvent(aMessageId, aData,
    1286           0 :                                                        registration);
    1287             : }
    1288             : 
    1289             : NS_IMETHODIMP
    1290           0 : ServiceWorkerManager::SendPushSubscriptionChangeEvent(const nsACString& aOriginAttributes,
    1291             :                                                       const nsACString& aScope)
    1292             : {
    1293           0 :   OriginAttributes attrs;
    1294           0 :   if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
    1295           0 :     return NS_ERROR_INVALID_ARG;
    1296             :   }
    1297             : 
    1298           0 :   ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
    1299           0 :   if (!info) {
    1300           0 :     return NS_ERROR_FAILURE;
    1301             :   }
    1302           0 :   return info->WorkerPrivate()->SendPushSubscriptionChangeEvent();
    1303             : }
    1304             : 
    1305             : nsresult
    1306           0 : ServiceWorkerManager::SendNotificationEvent(const nsAString& aEventName,
    1307             :                                             const nsACString& aOriginSuffix,
    1308             :                                             const nsACString& aScope,
    1309             :                                             const nsAString& aID,
    1310             :                                             const nsAString& aTitle,
    1311             :                                             const nsAString& aDir,
    1312             :                                             const nsAString& aLang,
    1313             :                                             const nsAString& aBody,
    1314             :                                             const nsAString& aTag,
    1315             :                                             const nsAString& aIcon,
    1316             :                                             const nsAString& aData,
    1317             :                                             const nsAString& aBehavior)
    1318             : {
    1319           0 :   OriginAttributes attrs;
    1320           0 :   if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
    1321           0 :     return NS_ERROR_INVALID_ARG;
    1322             :   }
    1323             : 
    1324           0 :   ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
    1325           0 :   if (!info) {
    1326           0 :     return NS_ERROR_FAILURE;
    1327             :   }
    1328             : 
    1329           0 :   ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate();
    1330             :   return workerPrivate->SendNotificationEvent(aEventName, aID, aTitle, aDir,
    1331             :                                               aLang, aBody, aTag,
    1332             :                                               aIcon, aData, aBehavior,
    1333           0 :                                               NS_ConvertUTF8toUTF16(aScope));
    1334             : }
    1335             : 
    1336             : NS_IMETHODIMP
    1337           0 : ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix,
    1338             :                                                  const nsACString& aScope,
    1339             :                                                  const nsAString& aID,
    1340             :                                                  const nsAString& aTitle,
    1341             :                                                  const nsAString& aDir,
    1342             :                                                  const nsAString& aLang,
    1343             :                                                  const nsAString& aBody,
    1344             :                                                  const nsAString& aTag,
    1345             :                                                  const nsAString& aIcon,
    1346             :                                                  const nsAString& aData,
    1347             :                                                  const nsAString& aBehavior)
    1348             : {
    1349           0 :   return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLICK_EVENT_NAME),
    1350             :                                aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
    1351           0 :                                aBody, aTag, aIcon, aData, aBehavior);
    1352             : }
    1353             : 
    1354             : NS_IMETHODIMP
    1355           0 : ServiceWorkerManager::SendNotificationCloseEvent(const nsACString& aOriginSuffix,
    1356             :                                                  const nsACString& aScope,
    1357             :                                                  const nsAString& aID,
    1358             :                                                  const nsAString& aTitle,
    1359             :                                                  const nsAString& aDir,
    1360             :                                                  const nsAString& aLang,
    1361             :                                                  const nsAString& aBody,
    1362             :                                                  const nsAString& aTag,
    1363             :                                                  const nsAString& aIcon,
    1364             :                                                  const nsAString& aData,
    1365             :                                                  const nsAString& aBehavior)
    1366             : {
    1367           0 :   return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLOSE_EVENT_NAME),
    1368             :                                aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
    1369           0 :                                aBody, aTag, aIcon, aData, aBehavior);
    1370             : }
    1371             : 
    1372             : NS_IMETHODIMP
    1373           0 : ServiceWorkerManager::GetReadyPromise(mozIDOMWindow* aWindow,
    1374             :                                       nsISupports** aPromise)
    1375             : {
    1376           0 :   AssertIsOnMainThread();
    1377             : 
    1378           0 :   if (NS_WARN_IF(!aWindow)) {
    1379           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    1380             :   }
    1381             : 
    1382           0 :   auto* window = nsPIDOMWindowInner::From(aWindow);
    1383             : 
    1384           0 :   nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
    1385           0 :   if (NS_WARN_IF(!doc)) {
    1386           0 :     return NS_ERROR_FAILURE;
    1387             :   }
    1388             : 
    1389             :   // Don't allow service workers to register when the *document* is chrome for
    1390             :   // now.
    1391           0 :   MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
    1392             : 
    1393           0 :   MOZ_ASSERT(!mPendingReadyPromises.Contains(window));
    1394             : 
    1395           0 :   nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
    1396           0 :   ErrorResult result;
    1397           0 :   RefPtr<Promise> promise = Promise::Create(sgo, result);
    1398           0 :   if (result.Failed()) {
    1399           0 :     return result.StealNSResult();
    1400             :   }
    1401             : 
    1402             :   nsCOMPtr<nsIRunnable> runnable =
    1403           0 :     new GetReadyPromiseRunnable(window, promise);
    1404           0 :   promise.forget(aPromise);
    1405           0 :   return NS_DispatchToCurrentThread(runnable);
    1406             : }
    1407             : 
    1408             : NS_IMETHODIMP
    1409           0 : ServiceWorkerManager::RemoveReadyPromise(mozIDOMWindow* aWindow)
    1410             : {
    1411           0 :   AssertIsOnMainThread();
    1412           0 :   MOZ_ASSERT(aWindow);
    1413             : 
    1414           0 :   if (!aWindow) {
    1415           0 :     return NS_ERROR_FAILURE;
    1416             :   }
    1417             : 
    1418           0 :   mPendingReadyPromises.Remove(aWindow);
    1419           0 :   return NS_OK;
    1420             : }
    1421             : 
    1422             : void
    1423           0 : ServiceWorkerManager::StorePendingReadyPromise(nsPIDOMWindowInner* aWindow,
    1424             :                                                nsIURI* aURI,
    1425             :                                                Promise* aPromise)
    1426             : {
    1427             :   PendingReadyPromise* data;
    1428             : 
    1429             :   // We should not have 2 pending promises for the same window.
    1430           0 :   MOZ_ASSERT(!mPendingReadyPromises.Get(aWindow, &data));
    1431             : 
    1432           0 :   data = new PendingReadyPromise(aURI, aPromise);
    1433           0 :   mPendingReadyPromises.Put(aWindow, data);
    1434           0 : }
    1435             : 
    1436             : void
    1437           0 : ServiceWorkerManager::CheckPendingReadyPromises()
    1438             : {
    1439           0 :   for (auto iter = mPendingReadyPromises.Iter(); !iter.Done(); iter.Next()) {
    1440           0 :     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(iter.Key());
    1441           0 :     MOZ_ASSERT(window);
    1442             : 
    1443           0 :     nsAutoPtr<PendingReadyPromise>& pendingReadyPromise = iter.Data();
    1444           0 :     if (CheckReadyPromise(window, pendingReadyPromise->mURI,
    1445           0 :                           pendingReadyPromise->mPromise)) {
    1446           0 :       iter.Remove();
    1447             :     }
    1448             :   }
    1449           0 : }
    1450             : 
    1451             : bool
    1452           0 : ServiceWorkerManager::CheckReadyPromise(nsPIDOMWindowInner* aWindow,
    1453             :                                         nsIURI* aURI, Promise* aPromise)
    1454             : {
    1455           0 :   MOZ_ASSERT(aWindow);
    1456           0 :   MOZ_ASSERT(aURI);
    1457             : 
    1458           0 :   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
    1459           0 :   MOZ_ASSERT(doc);
    1460             : 
    1461           0 :   nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
    1462           0 :   MOZ_ASSERT(principal);
    1463             : 
    1464             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    1465           0 :     GetServiceWorkerRegistrationInfo(principal, aURI);
    1466             : 
    1467           0 :   if (registration && registration->GetActive()) {
    1468           0 :     NS_ConvertUTF8toUTF16 scope(registration->mScope);
    1469             :     RefPtr<ServiceWorkerRegistration> swr =
    1470           0 :       aWindow->GetServiceWorkerRegistration(scope);
    1471           0 :     aPromise->MaybeResolve(swr);
    1472           0 :     return true;
    1473             :   }
    1474             : 
    1475           0 :   return false;
    1476             : }
    1477             : 
    1478             : ServiceWorkerInfo*
    1479           0 : ServiceWorkerManager::GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes,
    1480             :                                                   const nsACString& aScope)
    1481             : {
    1482           0 :   AssertIsOnMainThread();
    1483             : 
    1484           0 :   nsCOMPtr<nsIURI> scopeURI;
    1485           0 :   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
    1486           0 :   if (NS_FAILED(rv)) {
    1487           0 :     return nullptr;
    1488             :   }
    1489             :   nsCOMPtr<nsIPrincipal> principal =
    1490           0 :     BasePrincipal::CreateCodebasePrincipal(scopeURI, aOriginAttributes);
    1491             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    1492           0 :     GetServiceWorkerRegistrationInfo(principal, scopeURI);
    1493           0 :   if (!registration) {
    1494           0 :     return nullptr;
    1495             :   }
    1496             : 
    1497           0 :   return registration->GetActive();
    1498             : }
    1499             : 
    1500             : ServiceWorkerInfo*
    1501           0 : ServiceWorkerManager::GetActiveWorkerInfoForDocument(nsIDocument* aDocument)
    1502             : {
    1503           0 :   AssertIsOnMainThread();
    1504             : 
    1505           0 :   RefPtr<ServiceWorkerRegistrationInfo> registration;
    1506           0 :   GetDocumentRegistration(aDocument, getter_AddRefs(registration));
    1507             : 
    1508           0 :   if (!registration) {
    1509           0 :     return nullptr;
    1510             :   }
    1511             : 
    1512           0 :   return registration->GetActive();
    1513             : }
    1514             : 
    1515             : namespace {
    1516             : 
    1517             : class UnregisterJobCallback final : public ServiceWorkerJob::Callback
    1518             : {
    1519             :   nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
    1520             : 
    1521           0 :   ~UnregisterJobCallback()
    1522           0 :   {
    1523           0 :   }
    1524             : 
    1525             : public:
    1526           0 :   explicit UnregisterJobCallback(nsIServiceWorkerUnregisterCallback* aCallback)
    1527           0 :     : mCallback(aCallback)
    1528             :   {
    1529           0 :     AssertIsOnMainThread();
    1530           0 :     MOZ_ASSERT(mCallback);
    1531           0 :   }
    1532             : 
    1533             :   void
    1534           0 :   JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus)
    1535             :   {
    1536           0 :     AssertIsOnMainThread();
    1537           0 :     MOZ_ASSERT(aJob);
    1538             : 
    1539           0 :     if (aStatus.Failed()) {
    1540           0 :       mCallback->UnregisterFailed();
    1541           0 :       return;
    1542             :     }
    1543             : 
    1544           0 :     MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Unregister);
    1545             :     RefPtr<ServiceWorkerUnregisterJob> unregisterJob =
    1546           0 :       static_cast<ServiceWorkerUnregisterJob*>(aJob);
    1547           0 :     mCallback->UnregisterSucceeded(unregisterJob->GetResult());
    1548             :   }
    1549             : 
    1550           0 :   NS_INLINE_DECL_REFCOUNTING(UnregisterJobCallback)
    1551             : };
    1552             : 
    1553             : } // anonymous namespace
    1554             : 
    1555             : NS_IMETHODIMP
    1556           0 : ServiceWorkerManager::Unregister(nsIPrincipal* aPrincipal,
    1557             :                                  nsIServiceWorkerUnregisterCallback* aCallback,
    1558             :                                  const nsAString& aScope)
    1559             : {
    1560           0 :   AssertIsOnMainThread();
    1561             : 
    1562           0 :   if (!aPrincipal) {
    1563           0 :     return NS_ERROR_FAILURE;
    1564             :   }
    1565             : 
    1566             :   nsresult rv;
    1567             : 
    1568             : // This is not accessible by content, and callers should always ensure scope is
    1569             : // a correct URI, so this is wrapped in DEBUG
    1570             : #ifdef DEBUG
    1571           0 :   nsCOMPtr<nsIURI> scopeURI;
    1572           0 :   rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
    1573           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1574           0 :     return NS_ERROR_DOM_SECURITY_ERR;
    1575             :   }
    1576             : #endif
    1577             : 
    1578           0 :   nsAutoCString scopeKey;
    1579           0 :   rv = PrincipalToScopeKey(aPrincipal, scopeKey);
    1580           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1581           0 :     return rv;
    1582             :   }
    1583             : 
    1584           0 :   NS_ConvertUTF16toUTF8 scope(aScope);
    1585           0 :   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope);
    1586             : 
    1587             :   RefPtr<ServiceWorkerUnregisterJob> job =
    1588           0 :     new ServiceWorkerUnregisterJob(aPrincipal, scope, true /* send to parent */);
    1589             : 
    1590           0 :   if (aCallback) {
    1591           0 :     RefPtr<UnregisterJobCallback> cb = new UnregisterJobCallback(aCallback);
    1592           0 :     job->AppendResultCallback(cb);
    1593             :   }
    1594             : 
    1595           0 :   queue->ScheduleJob(job);
    1596           0 :   return NS_OK;
    1597             : }
    1598             : 
    1599             : nsresult
    1600           0 : ServiceWorkerManager::NotifyUnregister(nsIPrincipal* aPrincipal,
    1601             :                                        const nsAString& aScope)
    1602             : {
    1603           0 :   AssertIsOnMainThread();
    1604           0 :   MOZ_ASSERT(aPrincipal);
    1605             : 
    1606             :   nsresult rv;
    1607             : 
    1608             : // This is not accessible by content, and callers should always ensure scope is
    1609             : // a correct URI, so this is wrapped in DEBUG
    1610             : #ifdef DEBUG
    1611           0 :   nsCOMPtr<nsIURI> scopeURI;
    1612           0 :   rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
    1613           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1614           0 :     return rv;
    1615             :   }
    1616             : #endif
    1617             : 
    1618           0 :   nsAutoCString scopeKey;
    1619           0 :   rv = PrincipalToScopeKey(aPrincipal, scopeKey);
    1620           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1621           0 :     return rv;
    1622             :   }
    1623             : 
    1624           0 :   NS_ConvertUTF16toUTF8 scope(aScope);
    1625           0 :   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope);
    1626             : 
    1627             :   RefPtr<ServiceWorkerUnregisterJob> job =
    1628             :     new ServiceWorkerUnregisterJob(aPrincipal, scope,
    1629           0 :                                     false /* send to parent */);
    1630             : 
    1631           0 :   queue->ScheduleJob(job);
    1632           0 :   return NS_OK;
    1633             : }
    1634             : 
    1635             : void
    1636           0 : ServiceWorkerManager::WorkerIsIdle(ServiceWorkerInfo* aWorker)
    1637             : {
    1638           0 :   AssertIsOnMainThread();
    1639           0 :   MOZ_DIAGNOSTIC_ASSERT(aWorker);
    1640             : 
    1641             :   RefPtr<ServiceWorkerRegistrationInfo> reg =
    1642           0 :     GetRegistration(aWorker->Principal(), aWorker->Scope());
    1643           0 :   if (!reg) {
    1644           0 :     return;
    1645             :   }
    1646             : 
    1647           0 :   if (reg->GetActive() != aWorker) {
    1648           0 :     return;
    1649             :   }
    1650             : 
    1651           0 :   if (!reg->IsControllingDocuments() && reg->mPendingUninstall) {
    1652           0 :     RemoveRegistration(reg);
    1653           0 :     return;
    1654             :   }
    1655             : 
    1656           0 :   reg->TryToActivateAsync();
    1657             : }
    1658             : 
    1659             : already_AddRefed<ServiceWorkerJobQueue>
    1660           0 : ServiceWorkerManager::GetOrCreateJobQueue(const nsACString& aKey,
    1661             :                                           const nsACString& aScope)
    1662             : {
    1663           0 :   MOZ_ASSERT(!aKey.IsEmpty());
    1664             :   ServiceWorkerManager::RegistrationDataPerPrincipal* data;
    1665             :   // XXX we could use LookupForAdd here to avoid a hashtable lookup, except that
    1666             :   // leads to a false positive assertion, see bug 1370674 comment 7.
    1667           0 :   if (!mRegistrationInfos.Get(aKey, &data)) {
    1668           0 :     data = new RegistrationDataPerPrincipal();
    1669           0 :     mRegistrationInfos.Put(aKey, data);
    1670             :   }
    1671             : 
    1672             :   RefPtr<ServiceWorkerJobQueue> queue =
    1673           0 :     data->mJobQueues.LookupForAdd(aScope).OrInsert(
    1674           0 :       []() { return new ServiceWorkerJobQueue(); });
    1675             : 
    1676           0 :   return queue.forget();
    1677             : }
    1678             : 
    1679             : /* static */
    1680             : already_AddRefed<ServiceWorkerManager>
    1681          67 : ServiceWorkerManager::GetInstance()
    1682             : {
    1683             :   // Note: We don't simply check gInstance for null-ness here, since otherwise
    1684             :   // this can resurrect the ServiceWorkerManager pretty late during shutdown.
    1685             :   static bool firstTime = true;
    1686          67 :   if (firstTime) {
    1687           6 :     RefPtr<ServiceWorkerRegistrar> swr;
    1688             : 
    1689             :     // Don't create the ServiceWorkerManager until the ServiceWorkerRegistrar is
    1690             :     // initialized.
    1691           3 :     if (XRE_IsParentProcess()) {
    1692           1 :       swr = ServiceWorkerRegistrar::Get();
    1693           1 :       if (!swr) {
    1694           0 :         return nullptr;
    1695             :       }
    1696             :     }
    1697             : 
    1698           3 :     firstTime = false;
    1699             : 
    1700           3 :     AssertIsOnMainThread();
    1701             : 
    1702           3 :     gInstance = new ServiceWorkerManager();
    1703           3 :     gInstance->Init(swr);
    1704           3 :     ClearOnShutdown(&gInstance);
    1705             :   }
    1706         134 :   RefPtr<ServiceWorkerManager> copy = gInstance.get();
    1707          67 :   return copy.forget();
    1708             : }
    1709             : 
    1710             : void
    1711           0 : ServiceWorkerManager::FinishFetch(ServiceWorkerRegistrationInfo* aRegistration)
    1712             : {
    1713           0 : }
    1714             : 
    1715             : void
    1716           0 : ServiceWorkerManager::ReportToAllClients(const nsCString& aScope,
    1717             :                                          const nsString& aMessage,
    1718             :                                          const nsString& aFilename,
    1719             :                                          const nsString& aLine,
    1720             :                                          uint32_t aLineNumber,
    1721             :                                          uint32_t aColumnNumber,
    1722             :                                          uint32_t aFlags)
    1723             : {
    1724           0 :   nsCOMPtr<nsIURI> uri;
    1725             :   nsresult rv;
    1726             : 
    1727           0 :   if (!aFilename.IsEmpty()) {
    1728           0 :     rv = NS_NewURI(getter_AddRefs(uri), aFilename);
    1729           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1730           0 :       return;
    1731             :     }
    1732             :   }
    1733             : 
    1734           0 :   AutoTArray<uint64_t, 16> windows;
    1735             : 
    1736             :   // Report errors to every controlled document.
    1737           0 :   for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
    1738           0 :     ServiceWorkerRegistrationInfo* reg = iter.UserData();
    1739           0 :     MOZ_ASSERT(reg);
    1740           0 :     if (!reg->mScope.Equals(aScope)) {
    1741           0 :       continue;
    1742             :     }
    1743             : 
    1744           0 :     nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
    1745           0 :     if (!doc || !doc->IsCurrentActiveDocument() || !doc->GetWindow()) {
    1746           0 :       continue;
    1747             :     }
    1748             : 
    1749           0 :     windows.AppendElement(doc->InnerWindowID());
    1750             : 
    1751           0 :     nsContentUtils::ReportToConsoleNonLocalized(aMessage,
    1752             :                                                 aFlags,
    1753           0 :                                                 NS_LITERAL_CSTRING("Service Workers"),
    1754             :                                                 doc,
    1755             :                                                 uri,
    1756             :                                                 aLine,
    1757             :                                                 aLineNumber,
    1758             :                                                 aColumnNumber,
    1759           0 :                                                 nsContentUtils::eOMIT_LOCATION);
    1760             :   }
    1761             : 
    1762             :   // Report to any documents that have called .register() for this scope.  They
    1763             :   // may not be controlled, but will still want to see error reports.
    1764           0 :   WeakDocumentList* regList = mRegisteringDocuments.Get(aScope);
    1765           0 :   if (regList) {
    1766           0 :     for (int32_t i = regList->Length() - 1; i >= 0; --i) {
    1767           0 :       nsCOMPtr<nsIDocument> doc = do_QueryReferent(regList->ElementAt(i));
    1768           0 :       if (!doc) {
    1769           0 :         regList->RemoveElementAt(i);
    1770           0 :         continue;
    1771             :       }
    1772             : 
    1773           0 :       if (!doc->IsCurrentActiveDocument()) {
    1774           0 :         continue;
    1775             :       }
    1776             : 
    1777           0 :       uint64_t innerWindowId = doc->InnerWindowID();
    1778           0 :       if (windows.Contains(innerWindowId)) {
    1779           0 :         continue;
    1780             :       }
    1781             : 
    1782           0 :       windows.AppendElement(innerWindowId);
    1783             : 
    1784           0 :       nsContentUtils::ReportToConsoleNonLocalized(aMessage,
    1785             :                                                   aFlags,
    1786           0 :                                                   NS_LITERAL_CSTRING("Service Workers"),
    1787             :                                                   doc,
    1788             :                                                   uri,
    1789             :                                                   aLine,
    1790             :                                                   aLineNumber,
    1791             :                                                   aColumnNumber,
    1792           0 :                                                   nsContentUtils::eOMIT_LOCATION);
    1793             :     }
    1794             : 
    1795           0 :     if (regList->IsEmpty()) {
    1796           0 :       regList = nullptr;
    1797           0 :       mRegisteringDocuments.Remove(aScope);
    1798             :     }
    1799             :   }
    1800             : 
    1801           0 :   InterceptionList* intList = mNavigationInterceptions.Get(aScope);
    1802           0 :   if (intList) {
    1803           0 :     nsIConsoleService* consoleService = nullptr;
    1804           0 :     for (uint32_t i = 0; i < intList->Length(); ++i) {
    1805           0 :       nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
    1806             : 
    1807           0 :       nsCOMPtr<nsIChannel> inner;
    1808           0 :       rv = channel->GetChannel(getter_AddRefs(inner));
    1809           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1810           0 :         continue;
    1811             :       }
    1812             : 
    1813           0 :       uint64_t innerWindowId = nsContentUtils::GetInnerWindowID(inner);
    1814           0 :       if (innerWindowId == 0 || windows.Contains(innerWindowId)) {
    1815           0 :         continue;
    1816             :       }
    1817             : 
    1818           0 :       windows.AppendElement(innerWindowId);
    1819             : 
    1820             :       // Unfortunately the nsContentUtils helpers don't provide a convenient
    1821             :       // way to log to a window ID without a document.  Use console service
    1822             :       // directly.
    1823             :       nsCOMPtr<nsIScriptError> errorObject =
    1824           0 :         do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
    1825           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1826           0 :         return;
    1827             :       }
    1828             : 
    1829           0 :       rv = errorObject->InitWithWindowID(aMessage,
    1830             :                                          aFilename,
    1831             :                                          aLine,
    1832             :                                          aLineNumber,
    1833             :                                          aColumnNumber,
    1834             :                                          aFlags,
    1835           0 :                                          NS_LITERAL_CSTRING("Service Workers"),
    1836           0 :                                          innerWindowId);
    1837           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1838           0 :         return;
    1839             :       }
    1840             : 
    1841           0 :       if (!consoleService) {
    1842           0 :         rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &consoleService);
    1843           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    1844           0 :           return;
    1845             :         }
    1846             :       }
    1847             : 
    1848           0 :       consoleService->LogMessage(errorObject);
    1849             :     }
    1850             :   }
    1851             : 
    1852             :   // If there are no documents to report to, at least report something to the
    1853             :   // browser console.
    1854           0 :   if (windows.IsEmpty()) {
    1855           0 :     nsContentUtils::ReportToConsoleNonLocalized(aMessage,
    1856             :                                                 aFlags,
    1857           0 :                                                 NS_LITERAL_CSTRING("Service Workers"),
    1858             :                                                 nullptr,  // document
    1859             :                                                 uri,
    1860             :                                                 aLine,
    1861             :                                                 aLineNumber,
    1862             :                                                 aColumnNumber,
    1863           0 :                                                 nsContentUtils::eOMIT_LOCATION);
    1864           0 :     return;
    1865             :   }
    1866             : }
    1867             : 
    1868             : /* static */
    1869             : void
    1870           0 : ServiceWorkerManager::LocalizeAndReportToAllClients(
    1871             :                                           const nsCString& aScope,
    1872             :                                           const char* aStringKey,
    1873             :                                           const nsTArray<nsString>& aParamArray,
    1874             :                                           uint32_t aFlags,
    1875             :                                           const nsString& aFilename,
    1876             :                                           const nsString& aLine,
    1877             :                                           uint32_t aLineNumber,
    1878             :                                           uint32_t aColumnNumber)
    1879             : {
    1880           0 :   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    1881           0 :   if (!swm) {
    1882           0 :     return;
    1883             :   }
    1884             : 
    1885             :   nsresult rv;
    1886           0 :   nsXPIDLString message;
    1887             :   rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
    1888           0 :                                              aStringKey, aParamArray, message);
    1889           0 :   if (NS_SUCCEEDED(rv)) {
    1890           0 :     swm->ReportToAllClients(aScope, message,
    1891             :                             aFilename, aLine, aLineNumber, aColumnNumber,
    1892           0 :                             aFlags);
    1893             :   } else {
    1894           0 :     NS_WARNING("Failed to format and therefore report localized error.");
    1895             :   }
    1896             : }
    1897             : 
    1898             : void
    1899           0 : ServiceWorkerManager::FlushReportsToAllClients(const nsACString& aScope,
    1900             :                                                nsIConsoleReportCollector* aReporter)
    1901             : {
    1902           0 :   AutoTArray<uint64_t, 16> windows;
    1903             : 
    1904             :   // Report errors to every controlled document.
    1905           0 :   for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
    1906           0 :     ServiceWorkerRegistrationInfo* reg = iter.UserData();
    1907           0 :     MOZ_ASSERT(reg);
    1908           0 :     if (!reg->mScope.Equals(aScope)) {
    1909           0 :       continue;
    1910             :     }
    1911             : 
    1912           0 :     nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
    1913           0 :     if (!doc || !doc->IsCurrentActiveDocument() || !doc->GetWindow()) {
    1914           0 :       continue;
    1915             :     }
    1916             : 
    1917           0 :     uint64_t innerWindowId = doc->InnerWindowID();
    1918           0 :     windows.AppendElement(innerWindowId);
    1919             : 
    1920             :     aReporter->FlushReportsToConsole(
    1921           0 :       innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
    1922             :   }
    1923             : 
    1924             :   // Report to any documents that have called .register() for this scope.  They
    1925             :   // may not be controlled, but will still want to see error reports.
    1926           0 :   WeakDocumentList* regList = mRegisteringDocuments.Get(aScope);
    1927           0 :   if (regList) {
    1928           0 :     for (int32_t i = regList->Length() - 1; i >= 0; --i) {
    1929           0 :       nsCOMPtr<nsIDocument> doc = do_QueryReferent(regList->ElementAt(i));
    1930           0 :       if (!doc) {
    1931           0 :         regList->RemoveElementAt(i);
    1932           0 :         continue;
    1933             :       }
    1934             : 
    1935           0 :       if (!doc->IsCurrentActiveDocument()) {
    1936           0 :         continue;
    1937             :       }
    1938             : 
    1939           0 :       uint64_t innerWindowId = doc->InnerWindowID();
    1940           0 :       if (windows.Contains(innerWindowId)) {
    1941           0 :         continue;
    1942             :       }
    1943             : 
    1944           0 :       windows.AppendElement(innerWindowId);
    1945             : 
    1946             :       aReporter->FlushReportsToConsole(
    1947           0 :         innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
    1948             :     }
    1949             : 
    1950           0 :     if (regList->IsEmpty()) {
    1951           0 :       regList = nullptr;
    1952           0 :       mRegisteringDocuments.Remove(aScope);
    1953             :     }
    1954             :   }
    1955             : 
    1956             :   nsresult rv;
    1957           0 :   InterceptionList* intList = mNavigationInterceptions.Get(aScope);
    1958           0 :   if (intList) {
    1959           0 :     for (uint32_t i = 0; i < intList->Length(); ++i) {
    1960           0 :       nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
    1961             : 
    1962           0 :       nsCOMPtr<nsIChannel> inner;
    1963           0 :       rv = channel->GetChannel(getter_AddRefs(inner));
    1964           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1965           0 :         continue;
    1966             :       }
    1967             : 
    1968           0 :       uint64_t innerWindowId = nsContentUtils::GetInnerWindowID(inner);
    1969           0 :       if (innerWindowId == 0 || windows.Contains(innerWindowId)) {
    1970           0 :         continue;
    1971             :       }
    1972             : 
    1973           0 :       windows.AppendElement(innerWindowId);
    1974             : 
    1975             :       aReporter->FlushReportsToConsole(
    1976           0 :         innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
    1977             :     }
    1978             :   }
    1979             : 
    1980             :   // If there are no documents to report to, at least report something to the
    1981             :   // browser console.
    1982           0 :   if (windows.IsEmpty()) {
    1983           0 :     aReporter->FlushReportsToConsole(0);
    1984           0 :     return;
    1985             :   }
    1986             : 
    1987           0 :   aReporter->ClearConsoleReports();
    1988             : }
    1989             : 
    1990             : void
    1991           0 : ServiceWorkerManager::HandleError(JSContext* aCx,
    1992             :                                   nsIPrincipal* aPrincipal,
    1993             :                                   const nsCString& aScope,
    1994             :                                   const nsString& aWorkerURL,
    1995             :                                   const nsString& aMessage,
    1996             :                                   const nsString& aFilename,
    1997             :                                   const nsString& aLine,
    1998             :                                   uint32_t aLineNumber,
    1999             :                                   uint32_t aColumnNumber,
    2000             :                                   uint32_t aFlags,
    2001             :                                   JSExnType aExnType)
    2002             : {
    2003           0 :   AssertIsOnMainThread();
    2004           0 :   MOZ_ASSERT(aPrincipal);
    2005             : 
    2006           0 :   nsAutoCString scopeKey;
    2007           0 :   nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
    2008           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2009           0 :     return;
    2010             :   }
    2011             : 
    2012             :   ServiceWorkerManager::RegistrationDataPerPrincipal* data;
    2013           0 :   if (NS_WARN_IF(!mRegistrationInfos.Get(scopeKey, &data))) {
    2014           0 :     return;
    2015             :   }
    2016             : 
    2017             :   // Always report any uncaught exceptions or errors to the console of
    2018             :   // each client.
    2019             :   ReportToAllClients(aScope, aMessage, aFilename, aLine, aLineNumber,
    2020           0 :                      aColumnNumber, aFlags);
    2021             : }
    2022             : 
    2023             : void
    2024           0 : ServiceWorkerManager::LoadRegistration(
    2025             :                              const ServiceWorkerRegistrationData& aRegistration)
    2026             : {
    2027           0 :   AssertIsOnMainThread();
    2028             : 
    2029             :   nsCOMPtr<nsIPrincipal> principal =
    2030           0 :     PrincipalInfoToPrincipal(aRegistration.principal());
    2031           0 :   if (!principal) {
    2032           0 :     return;
    2033             :   }
    2034             : 
    2035             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    2036           0 :     GetRegistration(principal, aRegistration.scope());
    2037           0 :   if (!registration) {
    2038           0 :     registration = CreateNewRegistration(aRegistration.scope(), principal,
    2039           0 :                                          aRegistration.loadFlags());
    2040             :   } else {
    2041             :     // If active worker script matches our expectations for a "current worker",
    2042             :     // then we are done. Since scripts with the same URL might have different
    2043             :     // contents such as updated scripts or scripts with different LoadFlags, we
    2044             :     // use the CacheName to judje whether the two scripts are identical, where
    2045             :     // the CacheName is an UUID generated when a new script is found.
    2046           0 :     if (registration->GetActive() &&
    2047           0 :         registration->GetActive()->CacheName() == aRegistration.cacheName()) {
    2048             :       // No needs for updates.
    2049           0 :       return;
    2050             :     }
    2051             :   }
    2052             : 
    2053           0 :   registration->SetLastUpdateTime(aRegistration.lastUpdateTime());
    2054             : 
    2055           0 :   const nsCString& currentWorkerURL = aRegistration.currentWorkerURL();
    2056           0 :   if (!currentWorkerURL.IsEmpty()) {
    2057           0 :     registration->SetActive(
    2058           0 :       new ServiceWorkerInfo(registration->mPrincipal,
    2059           0 :                             registration->mScope,
    2060             :                             currentWorkerURL,
    2061           0 :                             aRegistration.cacheName(),
    2062           0 :                             registration->GetLoadFlags()));
    2063           0 :     registration->GetActive()->SetHandlesFetch(aRegistration.currentWorkerHandlesFetch());
    2064           0 :     registration->GetActive()->SetInstalledTime(aRegistration.currentWorkerInstalledTime());
    2065           0 :     registration->GetActive()->SetActivatedTime(aRegistration.currentWorkerActivatedTime());
    2066             :   }
    2067             : }
    2068             : 
    2069             : void
    2070           3 : ServiceWorkerManager::LoadRegistrations(
    2071             :                   const nsTArray<ServiceWorkerRegistrationData>& aRegistrations)
    2072             : {
    2073           3 :   AssertIsOnMainThread();
    2074             : 
    2075           3 :   for (uint32_t i = 0, len = aRegistrations.Length(); i < len; ++i) {
    2076           0 :     LoadRegistration(aRegistrations[i]);
    2077             :   }
    2078           3 : }
    2079             : 
    2080             : void
    2081           0 : ServiceWorkerManager::ActorFailed()
    2082             : {
    2083           0 :   MOZ_DIAGNOSTIC_ASSERT(!mActor);
    2084           0 :   MaybeStartShutdown();
    2085           0 : }
    2086             : 
    2087             : void
    2088           2 : ServiceWorkerManager::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
    2089             : {
    2090           2 :   MOZ_ASSERT(aActor);
    2091           2 :   MOZ_ASSERT(!mActor);
    2092             : 
    2093           2 :   if (mShuttingDown) {
    2094           0 :     MOZ_DIAGNOSTIC_ASSERT(mPendingOperations.IsEmpty());
    2095           0 :     return;
    2096             :   }
    2097             : 
    2098             :   PServiceWorkerManagerChild* actor =
    2099           2 :     aActor->SendPServiceWorkerManagerConstructor();
    2100           2 :   if (!actor) {
    2101           0 :     ActorFailed();
    2102           0 :     return;
    2103             :   }
    2104             : 
    2105           2 :   mActor = static_cast<ServiceWorkerManagerChild*>(actor);
    2106             : 
    2107             :   // Flush the pending requests.
    2108           2 :   for (uint32_t i = 0, len = mPendingOperations.Length(); i < len; ++i) {
    2109           0 :     MOZ_ASSERT(mPendingOperations[i]);
    2110           0 :     nsresult rv = NS_DispatchToCurrentThread(mPendingOperations[i].forget());
    2111           0 :     if (NS_FAILED(rv)) {
    2112           0 :       NS_WARNING("Failed to dispatch a runnable.");
    2113             :     }
    2114             :   }
    2115             : 
    2116           2 :   mPendingOperations.Clear();
    2117             : }
    2118             : 
    2119             : void
    2120           0 : ServiceWorkerManager::StoreRegistration(
    2121             :                                    nsIPrincipal* aPrincipal,
    2122             :                                    ServiceWorkerRegistrationInfo* aRegistration)
    2123             : {
    2124           0 :   MOZ_ASSERT(aPrincipal);
    2125           0 :   MOZ_ASSERT(aRegistration);
    2126             : 
    2127           0 :   if (mShuttingDown) {
    2128           0 :     return;
    2129             :   }
    2130             : 
    2131           0 :   MOZ_DIAGNOSTIC_ASSERT(mActor);
    2132           0 :   if (!mActor) {
    2133           0 :     return;
    2134             :   }
    2135             : 
    2136           0 :   ServiceWorkerRegistrationData data;
    2137           0 :   nsresult rv = PopulateRegistrationData(aPrincipal, aRegistration, data);
    2138           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2139           0 :     return;
    2140             :   }
    2141             : 
    2142           0 :   PrincipalInfo principalInfo;
    2143           0 :   if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
    2144             :                                                     &principalInfo)))) {
    2145           0 :     return;
    2146             :   }
    2147             : 
    2148           0 :   mActor->SendRegister(data);
    2149             : }
    2150             : 
    2151             : already_AddRefed<ServiceWorkerRegistrationInfo>
    2152           0 : ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow)
    2153             : {
    2154           0 :   MOZ_ASSERT(aWindow);
    2155           0 :   nsCOMPtr<nsIDocument> document = aWindow->GetExtantDoc();
    2156           0 :   return GetServiceWorkerRegistrationInfo(document);
    2157             : }
    2158             : 
    2159             : already_AddRefed<ServiceWorkerRegistrationInfo>
    2160           4 : ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIDocument* aDoc)
    2161             : {
    2162           4 :   MOZ_ASSERT(aDoc);
    2163           8 :   nsCOMPtr<nsIURI> documentURI = aDoc->GetDocumentURI();
    2164           8 :   nsCOMPtr<nsIPrincipal> principal = aDoc->NodePrincipal();
    2165           8 :   return GetServiceWorkerRegistrationInfo(principal, documentURI);
    2166             : }
    2167             : 
    2168             : already_AddRefed<ServiceWorkerRegistrationInfo>
    2169           5 : ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal,
    2170             :                                                        nsIURI* aURI)
    2171             : {
    2172           5 :   MOZ_ASSERT(aPrincipal);
    2173           5 :   MOZ_ASSERT(aURI);
    2174             : 
    2175             :   //XXXnsm Temporary fix until Bug 1171432 is fixed.
    2176           5 :   if (NS_WARN_IF(BasePrincipal::Cast(aPrincipal)->AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID)) {
    2177           0 :     return nullptr;
    2178             :   }
    2179             : 
    2180          10 :   nsAutoCString scopeKey;
    2181           5 :   nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
    2182           5 :   if (NS_FAILED(rv)) {
    2183           2 :     return nullptr;
    2184             :   }
    2185             : 
    2186           3 :   return GetServiceWorkerRegistrationInfo(scopeKey, aURI);
    2187             : }
    2188             : 
    2189             : already_AddRefed<ServiceWorkerRegistrationInfo>
    2190           3 : ServiceWorkerManager::GetServiceWorkerRegistrationInfo(const nsACString& aScopeKey,
    2191             :                                                        nsIURI* aURI)
    2192             : {
    2193           3 :   MOZ_ASSERT(aURI);
    2194             : 
    2195           6 :   nsAutoCString spec;
    2196           3 :   nsresult rv = aURI->GetSpec(spec);
    2197           3 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2198           0 :     return nullptr;
    2199             :   }
    2200             : 
    2201           6 :   nsAutoCString scope;
    2202             :   RegistrationDataPerPrincipal* data;
    2203           3 :   if (!FindScopeForPath(aScopeKey, spec, &data, scope)) {
    2204           3 :     return nullptr;
    2205             :   }
    2206             : 
    2207           0 :   MOZ_ASSERT(data);
    2208             : 
    2209           0 :   RefPtr<ServiceWorkerRegistrationInfo> registration;
    2210           0 :   data->mInfos.Get(scope, getter_AddRefs(registration));
    2211             :   // ordered scopes and registrations better be in sync.
    2212           0 :   MOZ_ASSERT(registration);
    2213             : 
    2214             : #ifdef DEBUG
    2215           0 :   nsAutoCString origin;
    2216           0 :   rv = registration->mPrincipal->GetOrigin(origin);
    2217           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    2218           0 :   MOZ_ASSERT(origin.Equals(aScopeKey));
    2219             : #endif
    2220             : 
    2221           0 :   if (registration->mPendingUninstall) {
    2222           0 :     return nullptr;
    2223             :   }
    2224           0 :   return registration.forget();
    2225             : }
    2226             : 
    2227             : /* static */ nsresult
    2228           5 : ServiceWorkerManager::PrincipalToScopeKey(nsIPrincipal* aPrincipal,
    2229             :                                           nsACString& aKey)
    2230             : {
    2231           5 :   MOZ_ASSERT(aPrincipal);
    2232             : 
    2233           5 :   if (!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal()) {
    2234           2 :     return NS_ERROR_FAILURE;
    2235             :   }
    2236             : 
    2237           3 :   nsresult rv = aPrincipal->GetOrigin(aKey);
    2238           3 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2239           0 :     return rv;
    2240             :   }
    2241             : 
    2242           3 :   return NS_OK;
    2243             : }
    2244             : 
    2245             : /* static */ void
    2246           0 : ServiceWorkerManager::AddScopeAndRegistration(const nsACString& aScope,
    2247             :                                               ServiceWorkerRegistrationInfo* aInfo)
    2248             : {
    2249           0 :   MOZ_ASSERT(aInfo);
    2250           0 :   MOZ_ASSERT(aInfo->mPrincipal);
    2251             : 
    2252           0 :   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    2253           0 :   if (!swm) {
    2254             :     // browser shutdown
    2255           0 :     return;
    2256             :   }
    2257             : 
    2258           0 :   nsAutoCString scopeKey;
    2259           0 :   nsresult rv = swm->PrincipalToScopeKey(aInfo->mPrincipal, scopeKey);
    2260           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2261           0 :     return;
    2262             :   }
    2263             : 
    2264           0 :   MOZ_ASSERT(!scopeKey.IsEmpty());
    2265             : 
    2266             :   RegistrationDataPerPrincipal* data =
    2267           0 :     swm->mRegistrationInfos.LookupForAdd(scopeKey).OrInsert(
    2268           0 :       []() { return new RegistrationDataPerPrincipal(); });
    2269             : 
    2270           0 :   for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) {
    2271           0 :     const nsCString& current = data->mOrderedScopes[i];
    2272             : 
    2273             :     // Perfect match!
    2274           0 :     if (aScope.Equals(current)) {
    2275           0 :       data->mInfos.Put(aScope, aInfo);
    2276           0 :       swm->NotifyListenersOnRegister(aInfo);
    2277           0 :       return;
    2278             :     }
    2279             : 
    2280             :     // Sort by length, with longest match first.
    2281             :     // /foo/bar should be before /foo/
    2282             :     // Similarly /foo/b is between the two.
    2283           0 :     if (StringBeginsWith(aScope, current)) {
    2284           0 :       data->mOrderedScopes.InsertElementAt(i, aScope);
    2285           0 :       data->mInfos.Put(aScope, aInfo);
    2286           0 :       swm->NotifyListenersOnRegister(aInfo);
    2287           0 :       return;
    2288             :     }
    2289             :   }
    2290             : 
    2291           0 :   data->mOrderedScopes.AppendElement(aScope);
    2292           0 :   data->mInfos.Put(aScope, aInfo);
    2293           0 :   swm->NotifyListenersOnRegister(aInfo);
    2294             : }
    2295             : 
    2296             : /* static */ bool
    2297           3 : ServiceWorkerManager::FindScopeForPath(const nsACString& aScopeKey,
    2298             :                                        const nsACString& aPath,
    2299             :                                        RegistrationDataPerPrincipal** aData,
    2300             :                                        nsACString& aMatch)
    2301             : {
    2302           3 :   MOZ_ASSERT(aData);
    2303             : 
    2304           6 :   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    2305             : 
    2306           3 :   if (!swm || !swm->mRegistrationInfos.Get(aScopeKey, aData)) {
    2307           3 :     return false;
    2308             :   }
    2309             : 
    2310           0 :   for (uint32_t i = 0; i < (*aData)->mOrderedScopes.Length(); ++i) {
    2311           0 :     const nsCString& current = (*aData)->mOrderedScopes[i];
    2312           0 :     if (StringBeginsWith(aPath, current)) {
    2313           0 :       aMatch = current;
    2314           0 :       return true;
    2315             :     }
    2316             :   }
    2317             : 
    2318           0 :   return false;
    2319             : }
    2320             : 
    2321             : /* static */ bool
    2322           0 : ServiceWorkerManager::HasScope(nsIPrincipal* aPrincipal,
    2323             :                                const nsACString& aScope)
    2324             : {
    2325           0 :   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    2326           0 :   if (!swm) {
    2327           0 :     return false;
    2328             :   }
    2329             : 
    2330           0 :   nsAutoCString scopeKey;
    2331           0 :   nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
    2332           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2333           0 :     return false;
    2334             :   }
    2335             : 
    2336             :   RegistrationDataPerPrincipal* data;
    2337           0 :   if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
    2338           0 :     return false;
    2339             :   }
    2340             : 
    2341           0 :   return data->mOrderedScopes.Contains(aScope);
    2342             : }
    2343             : 
    2344             : /* static */ void
    2345           0 : ServiceWorkerManager::RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo* aRegistration)
    2346             : {
    2347           0 :   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    2348           0 :   if (!swm) {
    2349           0 :     return;
    2350             :   }
    2351             : 
    2352           0 :   nsAutoCString scopeKey;
    2353           0 :   nsresult rv = swm->PrincipalToScopeKey(aRegistration->mPrincipal, scopeKey);
    2354           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2355           0 :     return;
    2356             :   }
    2357             : 
    2358             :   RegistrationDataPerPrincipal* data;
    2359           0 :   if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
    2360           0 :     return;
    2361             :   }
    2362             : 
    2363           0 :   if (auto entry = data->mUpdateTimers.Lookup(aRegistration->mScope)) {
    2364           0 :     entry.Data()->Cancel();
    2365           0 :     entry.Remove();
    2366             :   }
    2367             : 
    2368             :   // The registration should generally only be removed if there are no controlled
    2369             :   // documents, but mControlledDocuments can contain references to potentially
    2370             :   // controlled docs.  This happens when the service worker is not active yet.
    2371             :   // We must purge these references since we are evicting the registration.
    2372           0 :   for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
    2373           0 :     ServiceWorkerRegistrationInfo* reg = iter.UserData();
    2374           0 :     MOZ_ASSERT(reg);
    2375           0 :     if (reg->mScope.Equals(aRegistration->mScope)) {
    2376           0 :       iter.Remove();
    2377             :     }
    2378             :   }
    2379             : 
    2380           0 :   RefPtr<ServiceWorkerRegistrationInfo> info;
    2381           0 :   data->mInfos.Remove(aRegistration->mScope, getter_AddRefs(info));
    2382           0 :   data->mOrderedScopes.RemoveElement(aRegistration->mScope);
    2383           0 :   swm->NotifyListenersOnUnregister(info);
    2384             : 
    2385           0 :   swm->MaybeRemoveRegistrationInfo(scopeKey);
    2386           0 :   swm->NotifyServiceWorkerRegistrationRemoved(aRegistration);
    2387             : }
    2388             : 
    2389             : void
    2390           0 : ServiceWorkerManager::MaybeRemoveRegistrationInfo(const nsACString& aScopeKey)
    2391             : {
    2392           0 :   if (auto entry = mRegistrationInfos.Lookup(aScopeKey)) {
    2393           0 :     if (entry.Data()->mOrderedScopes.IsEmpty() &&
    2394           0 :         entry.Data()->mJobQueues.Count() == 0) {
    2395           0 :       entry.Remove();
    2396             :     }
    2397             :   }
    2398           0 : }
    2399             : 
    2400             : void
    2401           4 : ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc,
    2402             :                                             const nsAString& aDocumentId)
    2403             : {
    2404           4 :   AssertIsOnMainThread();
    2405           4 :   MOZ_ASSERT(aDoc);
    2406             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    2407           8 :     GetServiceWorkerRegistrationInfo(aDoc);
    2408           4 :   if (registration) {
    2409           0 :     MOZ_ASSERT(!mControlledDocuments.Contains(aDoc));
    2410           0 :     StartControllingADocument(registration, aDoc, aDocumentId);
    2411             :   }
    2412           4 : }
    2413             : 
    2414             : void
    2415           4 : ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
    2416             : {
    2417           4 :   AssertIsOnMainThread();
    2418           4 :   MOZ_ASSERT(aDoc);
    2419           8 :   RefPtr<ServiceWorkerRegistrationInfo> registration;
    2420           4 :   mControlledDocuments.Remove(aDoc, getter_AddRefs(registration));
    2421             :   // A document which was uncontrolled does not maintain that state itself, so
    2422             :   // it will always call MaybeStopControlling() even if there isn't an
    2423             :   // associated registration. So this check is required.
    2424           4 :   if (registration) {
    2425           0 :     StopControllingADocument(registration);
    2426             :   }
    2427           4 : }
    2428             : 
    2429             : void
    2430           4 : ServiceWorkerManager::MaybeCheckNavigationUpdate(nsIDocument* aDoc)
    2431             : {
    2432           4 :   AssertIsOnMainThread();
    2433           4 :   MOZ_ASSERT(aDoc);
    2434             :   // We perform these success path navigation update steps when the
    2435             :   // document tells us its more or less done loading.  This avoids
    2436             :   // slowing down page load and also lets pages consistently get
    2437             :   // updatefound events when they fire.
    2438             :   //
    2439             :   // 9.8.20 If respondWithEntered is false, then:
    2440             :   // 9.8.22 Else: (respondWith was entered and succeeded)
    2441             :   //    If request is a non-subresource request, then: Invoke Soft Update
    2442             :   //    algorithm.
    2443           8 :   RefPtr<ServiceWorkerRegistrationInfo> registration;
    2444           4 :   mControlledDocuments.Get(aDoc, getter_AddRefs(registration));
    2445           4 :   if (registration) {
    2446           0 :     registration->MaybeScheduleUpdate();
    2447             :   }
    2448           4 : }
    2449             : 
    2450             : void
    2451           0 : ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
    2452             :                                                 nsIDocument* aDoc,
    2453             :                                                 const nsAString& aDocumentId)
    2454             : {
    2455           0 :   MOZ_ASSERT(aRegistration);
    2456           0 :   MOZ_ASSERT(aDoc);
    2457             : 
    2458           0 :   aRegistration->StartControllingADocument();
    2459           0 :   mControlledDocuments.Put(aDoc, aRegistration);
    2460           0 :   if (!aDocumentId.IsEmpty()) {
    2461           0 :     aDoc->SetId(aDocumentId);
    2462             :   }
    2463           0 :   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
    2464           0 : }
    2465             : 
    2466             : void
    2467           0 : ServiceWorkerManager::StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration)
    2468             : {
    2469           0 :   aRegistration->StopControllingADocument();
    2470           0 :   if (aRegistration->IsControllingDocuments() || !aRegistration->IsIdle()) {
    2471           0 :     return;
    2472             :   }
    2473             : 
    2474           0 :   if (aRegistration->mPendingUninstall) {
    2475           0 :     RemoveRegistration(aRegistration);
    2476           0 :     return;
    2477             :   }
    2478             : 
    2479             :   // We use to aggressively terminate the worker at this point, but it
    2480             :   // caused problems.  There are more uses for a service worker than actively
    2481             :   // controlled documents.  We need to let the worker naturally terminate
    2482             :   // in case its handling push events, message events, etc.
    2483           0 :   aRegistration->TryToActivateAsync();
    2484             : }
    2485             : 
    2486             : NS_IMETHODIMP
    2487           0 : ServiceWorkerManager::GetScopeForUrl(nsIPrincipal* aPrincipal,
    2488             :                                      const nsAString& aUrl, nsAString& aScope)
    2489             : {
    2490           0 :   MOZ_ASSERT(aPrincipal);
    2491             : 
    2492           0 :   nsCOMPtr<nsIURI> uri;
    2493           0 :   nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl, nullptr, nullptr);
    2494           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2495           0 :     return NS_ERROR_FAILURE;
    2496             :   }
    2497             : 
    2498             :   RefPtr<ServiceWorkerRegistrationInfo> r =
    2499           0 :     GetServiceWorkerRegistrationInfo(aPrincipal, uri);
    2500           0 :   if (!r) {
    2501           0 :       return NS_ERROR_FAILURE;
    2502             :   }
    2503             : 
    2504           0 :   aScope = NS_ConvertUTF8toUTF16(r->mScope);
    2505           0 :   return NS_OK;
    2506             : }
    2507             : 
    2508             : NS_IMETHODIMP
    2509           0 : ServiceWorkerManager::AddRegistrationEventListener(const nsAString& aScope,
    2510             :                                                    ServiceWorkerRegistrationListener* aListener)
    2511             : {
    2512           0 :   AssertIsOnMainThread();
    2513           0 :   MOZ_ASSERT(aListener);
    2514             : #ifdef DEBUG
    2515             :   // Ensure a registration is only listening for it's own scope.
    2516           0 :   nsAutoString regScope;
    2517           0 :   aListener->GetScope(regScope);
    2518           0 :   MOZ_ASSERT(!regScope.IsEmpty());
    2519           0 :   MOZ_ASSERT(aScope.Equals(regScope));
    2520             : #endif
    2521             : 
    2522           0 :   MOZ_ASSERT(!mServiceWorkerRegistrationListeners.Contains(aListener));
    2523           0 :   mServiceWorkerRegistrationListeners.AppendElement(aListener);
    2524           0 :   return NS_OK;
    2525             : }
    2526             : 
    2527             : NS_IMETHODIMP
    2528           0 : ServiceWorkerManager::RemoveRegistrationEventListener(const nsAString& aScope,
    2529             :                                                       ServiceWorkerRegistrationListener* aListener)
    2530             : {
    2531           0 :   AssertIsOnMainThread();
    2532           0 :   MOZ_ASSERT(aListener);
    2533             : #ifdef DEBUG
    2534             :   // Ensure a registration is unregistering for it's own scope.
    2535           0 :   nsAutoString regScope;
    2536           0 :   aListener->GetScope(regScope);
    2537           0 :   MOZ_ASSERT(!regScope.IsEmpty());
    2538           0 :   MOZ_ASSERT(aScope.Equals(regScope));
    2539             : #endif
    2540             : 
    2541           0 :   MOZ_ASSERT(mServiceWorkerRegistrationListeners.Contains(aListener));
    2542           0 :   mServiceWorkerRegistrationListeners.RemoveElement(aListener);
    2543           0 :   return NS_OK;
    2544             : }
    2545             : 
    2546             : void
    2547           0 : ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations(
    2548             :   ServiceWorkerRegistrationInfo* aRegistration)
    2549             : {
    2550           0 :   AssertIsOnMainThread();
    2551             : 
    2552           0 :   nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
    2553           0 :   while (it.HasMore()) {
    2554           0 :     RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
    2555           0 :     nsAutoString regScope;
    2556           0 :     target->GetScope(regScope);
    2557           0 :     MOZ_ASSERT(!regScope.IsEmpty());
    2558             : 
    2559           0 :     NS_ConvertUTF16toUTF8 utf8Scope(regScope);
    2560           0 :     if (utf8Scope.Equals(aRegistration->mScope)) {
    2561           0 :       target->UpdateFound();
    2562             :     }
    2563             :   }
    2564           0 : }
    2565             : 
    2566             : /*
    2567             :  * This is used for installing, waiting and active.
    2568             :  */
    2569             : nsresult
    2570           0 : ServiceWorkerManager::GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow,
    2571             :                                                const nsAString& aScope,
    2572             :                                                WhichServiceWorker aWhichWorker,
    2573             :                                                nsISupports** aServiceWorker)
    2574             : {
    2575           0 :   AssertIsOnMainThread();
    2576             : 
    2577           0 :   if (NS_WARN_IF(!aWindow)) {
    2578           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    2579             :   }
    2580             : 
    2581           0 :   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
    2582           0 :   MOZ_ASSERT(doc);
    2583             : 
    2584             :   ///////////////////////////////////////////
    2585             :   // Security check
    2586           0 :   nsAutoCString scope = NS_ConvertUTF16toUTF8(aScope);
    2587           0 :   nsCOMPtr<nsIURI> scopeURI;
    2588             :   // We pass nullptr as the base URI since scopes obtained from
    2589             :   // ServiceWorkerRegistrations MUST be fully qualified URIs.
    2590           0 :   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope, nullptr, nullptr);
    2591           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2592           0 :     return NS_ERROR_DOM_SECURITY_ERR;
    2593             :   }
    2594             : 
    2595           0 :   nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal();
    2596           0 :   rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */,
    2597           0 :                                        false /* allowIfInheritsPrinciple */);
    2598           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2599           0 :     return NS_ERROR_DOM_SECURITY_ERR;
    2600             :   }
    2601             :   ////////////////////////////////////////////
    2602             : 
    2603             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    2604           0 :     GetRegistration(documentPrincipal, scope);
    2605           0 :   if (NS_WARN_IF(!registration)) {
    2606           0 :     return NS_ERROR_FAILURE;
    2607             :   }
    2608             : 
    2609           0 :   RefPtr<ServiceWorkerInfo> info;
    2610           0 :   if (aWhichWorker == WhichServiceWorker::INSTALLING_WORKER) {
    2611           0 :     info = registration->GetInstalling();
    2612           0 :   } else if (aWhichWorker == WhichServiceWorker::WAITING_WORKER) {
    2613           0 :     info = registration->GetWaiting();
    2614           0 :   } else if (aWhichWorker == WhichServiceWorker::ACTIVE_WORKER) {
    2615           0 :     info = registration->GetActive();
    2616             :   } else {
    2617           0 :     MOZ_CRASH("Invalid worker type");
    2618             :   }
    2619             : 
    2620           0 :   if (NS_WARN_IF(!info)) {
    2621           0 :     return NS_ERROR_DOM_NOT_FOUND_ERR;
    2622             :   }
    2623             : 
    2624           0 :   RefPtr<ServiceWorker> serviceWorker = info->GetOrCreateInstance(aWindow);
    2625             : 
    2626           0 :   serviceWorker->SetState(info->State());
    2627           0 :   serviceWorker.forget(aServiceWorker);
    2628           0 :   return NS_OK;
    2629             : }
    2630             : 
    2631             : namespace {
    2632             : 
    2633           0 : class ContinueDispatchFetchEventRunnable : public Runnable
    2634             : {
    2635             :   RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
    2636             :   nsCOMPtr<nsIInterceptedChannel> mChannel;
    2637             :   nsCOMPtr<nsILoadGroup> mLoadGroup;
    2638             :   nsString mDocumentId;
    2639             :   bool mIsReload;
    2640             : public:
    2641           0 :   ContinueDispatchFetchEventRunnable(
    2642             :     ServiceWorkerPrivate* aServiceWorkerPrivate,
    2643             :     nsIInterceptedChannel* aChannel,
    2644             :     nsILoadGroup* aLoadGroup,
    2645             :     const nsAString& aDocumentId,
    2646             :     bool aIsReload)
    2647           0 :     : Runnable("dom::workers::ContinueDispatchFetchEventRunnable")
    2648             :     , mServiceWorkerPrivate(aServiceWorkerPrivate)
    2649             :     , mChannel(aChannel)
    2650             :     , mLoadGroup(aLoadGroup)
    2651             :     , mDocumentId(aDocumentId)
    2652           0 :     , mIsReload(aIsReload)
    2653             :   {
    2654           0 :     MOZ_ASSERT(aServiceWorkerPrivate);
    2655           0 :     MOZ_ASSERT(aChannel);
    2656           0 :   }
    2657             : 
    2658             :   void
    2659           0 :   HandleError()
    2660             :   {
    2661           0 :     AssertIsOnMainThread();
    2662           0 :     NS_WARNING("Unexpected error while dispatching fetch event!");
    2663           0 :     DebugOnly<nsresult> rv = mChannel->ResetInterception();
    2664           0 :     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    2665             :                          "Failed to resume intercepted network request");
    2666           0 :   }
    2667             : 
    2668             :   NS_IMETHOD
    2669           0 :   Run() override
    2670             :   {
    2671           0 :     AssertIsOnMainThread();
    2672             : 
    2673           0 :     nsCOMPtr<nsIChannel> channel;
    2674           0 :     nsresult rv = mChannel->GetChannel(getter_AddRefs(channel));
    2675           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2676           0 :       HandleError();
    2677           0 :       return NS_OK;
    2678             :     }
    2679             : 
    2680             :     // The channel might have encountered an unexpected error while ensuring
    2681             :     // the upload stream is cloneable.  Check here and reset the interception
    2682             :     // if that happens.
    2683             :     nsresult status;
    2684           0 :     rv = channel->GetStatus(&status);
    2685           0 :     if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(status))) {
    2686           0 :       HandleError();
    2687           0 :       return NS_OK;
    2688             :     }
    2689             : 
    2690           0 :     rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup,
    2691           0 :                                                mDocumentId, mIsReload);
    2692           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2693           0 :       HandleError();
    2694             :     }
    2695             : 
    2696           0 :     return NS_OK;
    2697             :   }
    2698             : };
    2699             : 
    2700             : } // anonymous namespace
    2701             : 
    2702             : void
    2703           0 : ServiceWorkerManager::DispatchFetchEvent(const OriginAttributes& aOriginAttributes,
    2704             :                                          nsIDocument* aDoc,
    2705             :                                          const nsAString& aDocumentIdForTopLevelNavigation,
    2706             :                                          nsIInterceptedChannel* aChannel,
    2707             :                                          bool aIsReload,
    2708             :                                          bool aIsSubresourceLoad,
    2709             :                                          ErrorResult& aRv)
    2710             : {
    2711           0 :   MOZ_ASSERT(aChannel);
    2712           0 :   AssertIsOnMainThread();
    2713             : 
    2714           0 :   RefPtr<ServiceWorkerInfo> serviceWorker;
    2715           0 :   nsCOMPtr<nsILoadGroup> loadGroup;
    2716           0 :   nsAutoString documentId;
    2717             : 
    2718           0 :   if (aIsSubresourceLoad) {
    2719           0 :     MOZ_ASSERT(aDoc);
    2720             : 
    2721           0 :     serviceWorker = GetActiveWorkerInfoForDocument(aDoc);
    2722           0 :     if (!serviceWorker) {
    2723           0 :       aRv.Throw(NS_ERROR_FAILURE);
    2724           0 :       return;
    2725             :     }
    2726             : 
    2727           0 :     loadGroup = aDoc->GetDocumentLoadGroup();
    2728           0 :     nsresult rv = aDoc->GetOrCreateId(documentId);
    2729           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2730           0 :       return;
    2731             :     }
    2732             :   } else {
    2733           0 :     nsCOMPtr<nsIChannel> internalChannel;
    2734           0 :     aRv = aChannel->GetChannel(getter_AddRefs(internalChannel));
    2735           0 :     if (NS_WARN_IF(aRv.Failed())) {
    2736           0 :       return;
    2737             :     }
    2738             : 
    2739           0 :     internalChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    2740             : 
    2741             :     // TODO: Use aDocumentIdForTopLevelNavigation for potentialClientId, pending
    2742             :     // the spec change.
    2743             : 
    2744           0 :     nsCOMPtr<nsIURI> uri;
    2745           0 :     aRv = aChannel->GetSecureUpgradedChannelURI(getter_AddRefs(uri));
    2746           0 :     if (NS_WARN_IF(aRv.Failed())) {
    2747           0 :       return;
    2748             :     }
    2749             : 
    2750             :     // non-subresource request means the URI contains the principal
    2751             :     nsCOMPtr<nsIPrincipal> principal =
    2752           0 :       BasePrincipal::CreateCodebasePrincipal(uri, aOriginAttributes);
    2753             : 
    2754             :     RefPtr<ServiceWorkerRegistrationInfo> registration =
    2755           0 :       GetServiceWorkerRegistrationInfo(principal, uri);
    2756           0 :     if (!registration) {
    2757           0 :       NS_WARNING("No registration found when dispatching the fetch event");
    2758           0 :       aRv.Throw(NS_ERROR_FAILURE);
    2759           0 :       return;
    2760             :     }
    2761             : 
    2762             :     // While we only enter this method if IsAvailable() previously saw
    2763             :     // an active worker, it is possible for that worker to be removed
    2764             :     // before we get to this point.  Therefore we must handle a nullptr
    2765             :     // active worker here.
    2766           0 :     serviceWorker = registration->GetActive();
    2767           0 :     if (!serviceWorker) {
    2768           0 :       aRv.Throw(NS_ERROR_FAILURE);
    2769           0 :       return;
    2770             :     }
    2771             : 
    2772           0 :     AddNavigationInterception(serviceWorker->Scope(), aChannel);
    2773             :   }
    2774             : 
    2775           0 :   if (NS_WARN_IF(aRv.Failed())) {
    2776           0 :     return;
    2777             :   }
    2778             : 
    2779           0 :   MOZ_DIAGNOSTIC_ASSERT(serviceWorker);
    2780             : 
    2781             :   nsCOMPtr<nsIRunnable> continueRunnable =
    2782           0 :     new ContinueDispatchFetchEventRunnable(serviceWorker->WorkerPrivate(),
    2783             :                                            aChannel, loadGroup,
    2784           0 :                                            documentId, aIsReload);
    2785             : 
    2786             :   // When this service worker was registered, we also sent down the permissions
    2787             :   // for the runnable. They should have arrived by now, but we still need to
    2788             :   // wait for them if they have not.
    2789           0 :   nsCOMPtr<nsIRunnable> permissionsRunnable = NS_NewRunnableFunction(
    2790           0 :     "dom::workers::ServiceWorkerManager::DispatchFetchEvent", [=]() {
    2791           0 :       nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
    2792           0 :       MOZ_ALWAYS_SUCCEEDS(permMgr->WhenPermissionsAvailable(serviceWorker->Principal(),
    2793             :                                                             continueRunnable));
    2794           0 :     });
    2795             : 
    2796           0 :   nsCOMPtr<nsIChannel> innerChannel;
    2797           0 :   aRv = aChannel->GetChannel(getter_AddRefs(innerChannel));
    2798           0 :   if (NS_WARN_IF(aRv.Failed())) {
    2799           0 :     return;
    2800             :   }
    2801             : 
    2802           0 :   nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(innerChannel);
    2803             : 
    2804             :   // If there is no upload stream, then continue immediately
    2805           0 :   if (!uploadChannel) {
    2806           0 :     MOZ_ALWAYS_SUCCEEDS(permissionsRunnable->Run());
    2807           0 :     return;
    2808             :   }
    2809             :   // Otherwise, ensure the upload stream can be cloned directly.  This may
    2810             :   // require some async copying, so provide a callback.
    2811           0 :   aRv = uploadChannel->EnsureUploadStreamIsCloneable(permissionsRunnable);
    2812             : }
    2813             : 
    2814             : bool
    2815           1 : ServiceWorkerManager::IsAvailable(nsIPrincipal* aPrincipal,
    2816             :                                   nsIURI* aURI)
    2817             : {
    2818           1 :   MOZ_ASSERT(aPrincipal);
    2819           1 :   MOZ_ASSERT(aURI);
    2820             : 
    2821             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    2822           2 :     GetServiceWorkerRegistrationInfo(aPrincipal, aURI);
    2823           2 :   return registration && registration->GetActive();
    2824             : }
    2825             : 
    2826             : bool
    2827          55 : ServiceWorkerManager::IsControlled(nsIDocument* aDoc, ErrorResult& aRv)
    2828             : {
    2829          55 :   MOZ_ASSERT(aDoc);
    2830             : 
    2831          55 :   if (nsContentUtils::IsInPrivateBrowsing(aDoc)) {
    2832             :     // Handle the case where a service worker was previously registered in
    2833             :     // a non-private window (bug 1255621).
    2834           0 :     return false;
    2835             :   }
    2836             : 
    2837         110 :   RefPtr<ServiceWorkerRegistrationInfo> registration;
    2838          55 :   nsresult rv = GetDocumentRegistration(aDoc, getter_AddRefs(registration));
    2839          55 :   if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE)) {
    2840             :     // It's OK to ignore the case where we don't have a registration.
    2841           0 :     aRv.Throw(rv);
    2842           0 :     return false;
    2843             :   }
    2844             : 
    2845          55 :   return !!registration;
    2846             : }
    2847             : 
    2848             : nsresult
    2849          55 : ServiceWorkerManager::GetDocumentRegistration(nsIDocument* aDoc,
    2850             :                                               ServiceWorkerRegistrationInfo** aRegistrationInfo)
    2851             : {
    2852         110 :   RefPtr<ServiceWorkerRegistrationInfo> registration;
    2853          55 :   if (!mControlledDocuments.Get(aDoc, getter_AddRefs(registration))) {
    2854          55 :     return NS_ERROR_NOT_AVAILABLE;
    2855             :   }
    2856             : 
    2857             :   // If the document is controlled, the current worker MUST be non-null.
    2858           0 :   if (!registration->GetActive()) {
    2859           0 :     return NS_ERROR_NOT_AVAILABLE;
    2860             :   }
    2861             : 
    2862           0 :   registration.forget(aRegistrationInfo);
    2863           0 :   return NS_OK;
    2864             : }
    2865             : 
    2866             : /*
    2867             :  * The .controller is for the registration associated with the document when
    2868             :  * the document was loaded.
    2869             :  */
    2870             : NS_IMETHODIMP
    2871           0 : ServiceWorkerManager::GetDocumentController(nsPIDOMWindowInner* aWindow,
    2872             :                                             nsISupports** aServiceWorker)
    2873             : {
    2874           0 :   if (NS_WARN_IF(!aWindow)) {
    2875           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    2876             :   }
    2877             : 
    2878           0 :   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
    2879           0 :   if (!doc) {
    2880           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    2881             :   }
    2882             : 
    2883           0 :   RefPtr<ServiceWorkerRegistrationInfo> registration;
    2884           0 :   nsresult rv = GetDocumentRegistration(doc, getter_AddRefs(registration));
    2885           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2886           0 :     return rv;
    2887             :   }
    2888             : 
    2889           0 :   MOZ_ASSERT(registration->GetActive());
    2890             :   RefPtr<ServiceWorker> serviceWorker =
    2891           0 :     registration->GetActive()->GetOrCreateInstance(aWindow);
    2892             : 
    2893           0 :   serviceWorker.forget(aServiceWorker);
    2894           0 :   return NS_OK;
    2895             : }
    2896             : 
    2897             : NS_IMETHODIMP
    2898           0 : ServiceWorkerManager::GetInstalling(nsPIDOMWindowInner* aWindow,
    2899             :                                     const nsAString& aScope,
    2900             :                                     nsISupports** aServiceWorker)
    2901             : {
    2902             :   return GetServiceWorkerForScope(aWindow, aScope,
    2903             :                                   WhichServiceWorker::INSTALLING_WORKER,
    2904           0 :                                   aServiceWorker);
    2905             : }
    2906             : 
    2907             : NS_IMETHODIMP
    2908           0 : ServiceWorkerManager::GetWaiting(nsPIDOMWindowInner* aWindow,
    2909             :                                  const nsAString& aScope,
    2910             :                                  nsISupports** aServiceWorker)
    2911             : {
    2912             :   return GetServiceWorkerForScope(aWindow, aScope,
    2913             :                                   WhichServiceWorker::WAITING_WORKER,
    2914           0 :                                   aServiceWorker);
    2915             : }
    2916             : 
    2917             : NS_IMETHODIMP
    2918           0 : ServiceWorkerManager::GetActive(nsPIDOMWindowInner* aWindow,
    2919             :                                 const nsAString& aScope,
    2920             :                                 nsISupports** aServiceWorker)
    2921             : {
    2922             :   return GetServiceWorkerForScope(aWindow, aScope,
    2923             :                                   WhichServiceWorker::ACTIVE_WORKER,
    2924           0 :                                   aServiceWorker);
    2925             : }
    2926             : 
    2927             : void
    2928           0 : ServiceWorkerManager::TransitionServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
    2929             :                                                                 WhichServiceWorker aWhichOne)
    2930             : {
    2931           0 :   AssertIsOnMainThread();
    2932           0 :   nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
    2933           0 :   while (it.HasMore()) {
    2934           0 :     RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
    2935           0 :     nsAutoString regScope;
    2936           0 :     target->GetScope(regScope);
    2937           0 :     MOZ_ASSERT(!regScope.IsEmpty());
    2938             : 
    2939           0 :     NS_ConvertUTF16toUTF8 utf8Scope(regScope);
    2940             : 
    2941           0 :     if (utf8Scope.Equals(aRegistration->mScope)) {
    2942           0 :       target->TransitionWorker(aWhichOne);
    2943             :     }
    2944             :   }
    2945           0 : }
    2946             : 
    2947             : void
    2948           0 : ServiceWorkerManager::InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
    2949             :                                                                 WhichServiceWorker aWhichOnes)
    2950             : {
    2951           0 :   AssertIsOnMainThread();
    2952           0 :   nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
    2953           0 :   while (it.HasMore()) {
    2954           0 :     RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
    2955           0 :     nsAutoString regScope;
    2956           0 :     target->GetScope(regScope);
    2957           0 :     MOZ_ASSERT(!regScope.IsEmpty());
    2958             : 
    2959           0 :     NS_ConvertUTF16toUTF8 utf8Scope(regScope);
    2960             : 
    2961           0 :     if (utf8Scope.Equals(aRegistration->mScope)) {
    2962           0 :       target->InvalidateWorkers(aWhichOnes);
    2963             :     }
    2964             :   }
    2965           0 : }
    2966             : 
    2967             : void
    2968           0 : ServiceWorkerManager::NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration)
    2969             : {
    2970           0 :   AssertIsOnMainThread();
    2971           0 :   nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
    2972           0 :   while (it.HasMore()) {
    2973           0 :     RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
    2974           0 :     nsAutoString regScope;
    2975           0 :     target->GetScope(regScope);
    2976           0 :     MOZ_ASSERT(!regScope.IsEmpty());
    2977             : 
    2978           0 :     NS_ConvertUTF16toUTF8 utf8Scope(regScope);
    2979             : 
    2980           0 :     if (utf8Scope.Equals(aRegistration->mScope)) {
    2981           0 :       target->RegistrationRemoved();
    2982             :     }
    2983             :   }
    2984           0 : }
    2985             : 
    2986             : void
    2987           0 : ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes,
    2988             :                                  const nsACString& aScope)
    2989             : {
    2990           0 :   AssertIsOnMainThread();
    2991             : 
    2992           0 :   if (mShuttingDown) {
    2993           0 :     return;
    2994             :   }
    2995             : 
    2996           0 :   if (!mActor) {
    2997             :     RefPtr<Runnable> runnable =
    2998           0 :       new SoftUpdateRunnable(aOriginAttributes, aScope, false, nullptr);
    2999           0 :     AppendPendingOperation(runnable);
    3000           0 :     return;
    3001             :   }
    3002             : 
    3003             :   RefPtr<GenericPromise::Private> promise =
    3004           0 :     new GenericPromise::Private(__func__);
    3005             : 
    3006             :   RefPtr<CancelableRunnable> successRunnable =
    3007           0 :     new SoftUpdateRunnable(aOriginAttributes, aScope, true, promise);
    3008             : 
    3009             :   RefPtr<CancelableRunnable> failureRunnable =
    3010           0 :     new ResolvePromiseRunnable(promise);
    3011             : 
    3012             :   ServiceWorkerUpdaterChild* actor =
    3013           0 :     new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable);
    3014             : 
    3015           0 :   mActor->SendPServiceWorkerUpdaterConstructor(actor, aOriginAttributes,
    3016           0 :                                                nsCString(aScope));
    3017             : }
    3018             : 
    3019             : namespace {
    3020             : 
    3021             : class UpdateJobCallback final : public ServiceWorkerJob::Callback
    3022             : {
    3023             :   RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
    3024             : 
    3025           0 :   ~UpdateJobCallback() = default;
    3026             : 
    3027             : public:
    3028           0 :   explicit UpdateJobCallback(ServiceWorkerUpdateFinishCallback* aCallback)
    3029           0 :     : mCallback(aCallback)
    3030             :   {
    3031           0 :     AssertIsOnMainThread();
    3032           0 :     MOZ_ASSERT(mCallback);
    3033           0 :   }
    3034             : 
    3035             :   void
    3036           0 :   JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus)
    3037             :   {
    3038           0 :     AssertIsOnMainThread();
    3039           0 :     MOZ_ASSERT(aJob);
    3040             : 
    3041           0 :     if (aStatus.Failed()) {
    3042           0 :       mCallback->UpdateFailed(aStatus);
    3043           0 :       return;
    3044             :     }
    3045             : 
    3046           0 :     MOZ_DIAGNOSTIC_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Update);
    3047             :     RefPtr<ServiceWorkerUpdateJob> updateJob =
    3048           0 :       static_cast<ServiceWorkerUpdateJob*>(aJob);
    3049           0 :     RefPtr<ServiceWorkerRegistrationInfo> reg = updateJob->GetRegistration();
    3050           0 :     mCallback->UpdateSucceeded(reg);
    3051             :   }
    3052             : 
    3053           0 :   NS_INLINE_DECL_REFCOUNTING(UpdateJobCallback)
    3054             : };
    3055             : 
    3056             : } // anonymous namespace
    3057             : 
    3058             : void
    3059           0 : ServiceWorkerManager::SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
    3060             :                                          const nsACString& aScope,
    3061             :                                          ServiceWorkerUpdateFinishCallback* aCallback)
    3062             : {
    3063           0 :   AssertIsOnMainThread();
    3064           0 :   MOZ_ASSERT(aCallback);
    3065             : 
    3066           0 :   if (mShuttingDown) {
    3067           0 :     return;
    3068             :   }
    3069             : 
    3070           0 :   nsCOMPtr<nsIURI> scopeURI;
    3071           0 :   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope);
    3072           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3073           0 :     return;
    3074             :   }
    3075             : 
    3076             :   nsCOMPtr<nsIPrincipal> principal =
    3077           0 :     BasePrincipal::CreateCodebasePrincipal(scopeURI, aOriginAttributes);
    3078           0 :   if (NS_WARN_IF(!principal)) {
    3079           0 :     return;
    3080             :   }
    3081             : 
    3082           0 :   nsAutoCString scopeKey;
    3083           0 :   rv = PrincipalToScopeKey(principal, scopeKey);
    3084           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3085           0 :     return;
    3086             :   }
    3087             : 
    3088             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    3089           0 :     GetRegistration(scopeKey, aScope);
    3090           0 :   if (NS_WARN_IF(!registration)) {
    3091           0 :     return;
    3092             :   }
    3093             : 
    3094             :   // "If registration's uninstalling flag is set, abort these steps."
    3095           0 :   if (registration->mPendingUninstall) {
    3096           0 :     return;
    3097             :   }
    3098             : 
    3099             :   // "If registration's installing worker is not null, abort these steps."
    3100           0 :   if (registration->GetInstalling()) {
    3101           0 :     return;
    3102             :   }
    3103             : 
    3104             :   // "Let newestWorker be the result of running Get Newest Worker algorithm
    3105             :   // passing registration as its argument.
    3106             :   // If newestWorker is null, abort these steps."
    3107           0 :   RefPtr<ServiceWorkerInfo> newest = registration->Newest();
    3108           0 :   if (!newest) {
    3109           0 :     return;
    3110             :   }
    3111             : 
    3112             :   // "If the registration queue for registration is empty, invoke Update algorithm,
    3113             :   // or its equivalent, with client, registration as its argument."
    3114             :   // TODO(catalinb): We don't implement the force bypass cache flag.
    3115             :   // See: https://github.com/slightlyoff/ServiceWorker/issues/759
    3116           0 :   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
    3117           0 :                                                             aScope);
    3118             : 
    3119             :   RefPtr<ServiceWorkerUpdateJob> job =
    3120           0 :     new ServiceWorkerUpdateJob(principal, registration->mScope,
    3121           0 :                                newest->ScriptSpec(), nullptr,
    3122           0 :                                registration->GetLoadFlags());
    3123             : 
    3124           0 :   RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
    3125           0 :   job->AppendResultCallback(cb);
    3126             : 
    3127           0 :   queue->ScheduleJob(job);
    3128             : }
    3129             : 
    3130             : void
    3131           0 : ServiceWorkerManager::Update(nsIPrincipal* aPrincipal,
    3132             :                              const nsACString& aScope,
    3133             :                              ServiceWorkerUpdateFinishCallback* aCallback)
    3134             : {
    3135           0 :   AssertIsOnMainThread();
    3136             : 
    3137           0 :   if (!mActor) {
    3138             :     RefPtr<Runnable> runnable =
    3139             :       new UpdateRunnable(aPrincipal, aScope, aCallback,
    3140           0 :                          UpdateRunnable::ePostpone, nullptr);
    3141           0 :     AppendPendingOperation(runnable);
    3142           0 :     return;
    3143             :   }
    3144             : 
    3145             :   RefPtr<GenericPromise::Private> promise =
    3146           0 :     new GenericPromise::Private(__func__);
    3147             : 
    3148             :   RefPtr<CancelableRunnable> successRunnable =
    3149             :     new UpdateRunnable(aPrincipal, aScope, aCallback,
    3150           0 :                        UpdateRunnable::eSuccess, promise);
    3151             : 
    3152             :   RefPtr<CancelableRunnable> failureRunnable =
    3153             :     new UpdateRunnable(aPrincipal, aScope, aCallback,
    3154           0 :                        UpdateRunnable::eFailure, promise);
    3155             : 
    3156             :   ServiceWorkerUpdaterChild* actor =
    3157           0 :     new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable);
    3158             : 
    3159           0 :   mActor->SendPServiceWorkerUpdaterConstructor(actor,
    3160           0 :                                                aPrincipal->OriginAttributesRef(),
    3161           0 :                                                nsCString(aScope));
    3162             : }
    3163             : 
    3164             : void
    3165           0 : ServiceWorkerManager::UpdateInternal(nsIPrincipal* aPrincipal,
    3166             :                                      const nsACString& aScope,
    3167             :                                      ServiceWorkerUpdateFinishCallback* aCallback)
    3168             : {
    3169           0 :   MOZ_ASSERT(aPrincipal);
    3170           0 :   MOZ_ASSERT(aCallback);
    3171             : 
    3172           0 :   nsAutoCString scopeKey;
    3173           0 :   nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
    3174           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3175           0 :     return;
    3176             :   }
    3177             : 
    3178             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    3179           0 :     GetRegistration(scopeKey, aScope);
    3180           0 :   if (NS_WARN_IF(!registration)) {
    3181           0 :     return;
    3182             :   }
    3183             : 
    3184             :   // "Let newestWorker be the result of running Get Newest Worker algorithm
    3185             :   // passing registration as its argument.
    3186             :   // If newestWorker is null, return a promise rejected with "InvalidStateError"
    3187           0 :   RefPtr<ServiceWorkerInfo> newest = registration->Newest();
    3188           0 :   if (!newest) {
    3189           0 :     ErrorResult error(NS_ERROR_DOM_INVALID_STATE_ERR);
    3190           0 :     aCallback->UpdateFailed(error);
    3191             : 
    3192             :     // In case the callback does not consume the exception
    3193           0 :     error.SuppressException();
    3194             : 
    3195           0 :     return;
    3196             :   }
    3197             : 
    3198           0 :   RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, aScope);
    3199             : 
    3200             :   // "Invoke Update algorithm, or its equivalent, with client, registration as
    3201             :   // its argument."
    3202             :   RefPtr<ServiceWorkerUpdateJob> job =
    3203           0 :     new ServiceWorkerUpdateJob(aPrincipal, registration->mScope,
    3204           0 :                                newest->ScriptSpec(), nullptr,
    3205           0 :                                registration->GetLoadFlags());
    3206             : 
    3207           0 :   RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
    3208           0 :   job->AppendResultCallback(cb);
    3209             : 
    3210           0 :   queue->ScheduleJob(job);
    3211             : }
    3212             : 
    3213             : namespace {
    3214             : 
    3215             : static void
    3216           0 : FireControllerChangeOnDocument(nsIDocument* aDocument)
    3217             : {
    3218           0 :   AssertIsOnMainThread();
    3219           0 :   MOZ_ASSERT(aDocument);
    3220             : 
    3221           0 :   nsCOMPtr<nsPIDOMWindowInner> w = aDocument->GetInnerWindow();
    3222           0 :   if (!w) {
    3223           0 :     NS_WARNING("Failed to dispatch controllerchange event");
    3224           0 :     return;
    3225             :   }
    3226             : 
    3227           0 :   auto* window = nsGlobalWindow::Cast(w.get());
    3228           0 :   dom::Navigator* navigator = window->Navigator();
    3229           0 :   if (!navigator) {
    3230           0 :     return;
    3231             :   }
    3232             : 
    3233           0 :   RefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker();
    3234           0 :   ErrorResult result;
    3235           0 :   container->ControllerChanged(result);
    3236           0 :   if (result.Failed()) {
    3237           0 :     NS_WARNING("Failed to dispatch controllerchange event");
    3238             :   }
    3239             : }
    3240             : 
    3241             : } // anonymous namespace
    3242             : 
    3243             : UniquePtr<ServiceWorkerClientInfo>
    3244           0 : ServiceWorkerManager::GetClient(nsIPrincipal* aPrincipal,
    3245             :                                 const nsAString& aClientId,
    3246             :                                 ErrorResult& aRv)
    3247             : {
    3248           0 :   UniquePtr<ServiceWorkerClientInfo> clientInfo;
    3249             :   nsCOMPtr<nsISupportsInterfacePointer> ifptr =
    3250           0 :     do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
    3251           0 :   if (NS_WARN_IF(!ifptr)) {
    3252           0 :     return clientInfo;
    3253             :   }
    3254           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    3255           0 :   if (NS_WARN_IF(!obs)) {
    3256           0 :     return clientInfo;
    3257             :   }
    3258             : 
    3259           0 :   nsresult rv = obs->NotifyObservers(ifptr, "service-worker-get-client",
    3260           0 :                                      PromiseFlatString(aClientId).get());
    3261           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3262           0 :     return clientInfo;
    3263             :   }
    3264             : 
    3265           0 :   nsCOMPtr<nsISupports> ptr;
    3266           0 :   ifptr->GetData(getter_AddRefs(ptr));
    3267           0 :   nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
    3268           0 :   if (NS_WARN_IF(!doc)) {
    3269           0 :     return clientInfo;
    3270             :   }
    3271             : 
    3272           0 :   bool equals = false;
    3273           0 :   aPrincipal->Equals(doc->NodePrincipal(), &equals);
    3274           0 :   if (!equals) {
    3275           0 :     return clientInfo;
    3276             :   }
    3277             : 
    3278           0 :   if (!IsFromAuthenticatedOrigin(doc)) {
    3279           0 :     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    3280           0 :     return clientInfo;
    3281             :   }
    3282             : 
    3283           0 :   clientInfo.reset(new ServiceWorkerClientInfo(doc));
    3284           0 :   return clientInfo;
    3285             : }
    3286             : 
    3287             : void
    3288           0 : ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal,
    3289             :                                     const nsCString& aScope,
    3290             :                                     uint64_t aServiceWorkerID,
    3291             :                                     bool aIncludeUncontrolled,
    3292             :                                     nsTArray<ServiceWorkerClientInfo>& aDocuments)
    3293             : {
    3294           0 :   MOZ_ASSERT(aPrincipal);
    3295             : 
    3296             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    3297           0 :     GetRegistration(aPrincipal, aScope);
    3298             : 
    3299           0 :   if (!registration) {
    3300             :     // The registration was removed, leave the array empty.
    3301           0 :     return;
    3302             :   }
    3303             : 
    3304           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    3305           0 :   if (NS_WARN_IF(!obs)) {
    3306           0 :     return;
    3307             :   }
    3308             : 
    3309           0 :   nsCOMPtr<nsISimpleEnumerator> enumerator;
    3310           0 :   nsresult rv = obs->EnumerateObservers("service-worker-get-client",
    3311           0 :                                         getter_AddRefs(enumerator));
    3312           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3313           0 :     return;
    3314             :   }
    3315             : 
    3316             :   // Get a list of Client documents out of the observer service
    3317           0 :   AutoTArray<nsCOMPtr<nsIDocument>, 32> docList;
    3318           0 :   bool loop = true;
    3319           0 :   while (NS_SUCCEEDED(enumerator->HasMoreElements(&loop)) && loop) {
    3320           0 :     nsCOMPtr<nsISupports> ptr;
    3321           0 :     rv = enumerator->GetNext(getter_AddRefs(ptr));
    3322           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    3323           0 :       continue;
    3324             :     }
    3325             : 
    3326           0 :     nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
    3327           0 :     if (!doc || !doc->GetWindow()) {
    3328           0 :       continue;
    3329             :     }
    3330             : 
    3331           0 :     bool equals = false;
    3332           0 :     Unused << aPrincipal->Equals(doc->NodePrincipal(), &equals);
    3333           0 :     if (!equals) {
    3334           0 :       continue;
    3335             :     }
    3336             : 
    3337             :     // Treat http windows with devtools opened as secure if the correct devtools
    3338             :     // setting is enabled.
    3339           0 :     if (!doc->GetWindow()->GetServiceWorkersTestingEnabled() &&
    3340           0 :         !Preferences::GetBool("dom.serviceWorkers.testing.enabled") &&
    3341           0 :         !IsFromAuthenticatedOrigin(doc)) {
    3342           0 :       continue;
    3343             :     }
    3344             : 
    3345             :     // If we are only returning controlled Clients then skip any documents
    3346             :     // that are for different registrations.  We also skip service workers
    3347             :     // that don't match the ID of our calling service worker.  We should
    3348             :     // only return Clients controlled by that precise service worker.
    3349           0 :     if (!aIncludeUncontrolled) {
    3350           0 :       ServiceWorkerRegistrationInfo* reg = mControlledDocuments.GetWeak(doc);
    3351           0 :       if (!reg || reg->mScope != aScope || !reg->GetActive() ||
    3352           0 :           reg->GetActive()->ID() != aServiceWorkerID) {
    3353           0 :         continue;
    3354             :       }
    3355             :     }
    3356             : 
    3357           0 :     if (!aIncludeUncontrolled && !mControlledDocuments.Contains(doc)) {
    3358           0 :       continue;
    3359             :     }
    3360             : 
    3361           0 :     docList.AppendElement(doc.forget());
    3362             :   }
    3363             : 
    3364             :   // The observer service gives us the list in reverse creation order.
    3365             :   // We need to maintain creation order, so reverse the list before
    3366             :   // processing.
    3367           0 :   docList.Reverse();
    3368             : 
    3369             :   // Finally convert to the list of ServiceWorkerClientInfo objects.
    3370           0 :   uint32_t ordinal = 0;
    3371           0 :   for (uint32_t i = 0; i < docList.Length(); ++i) {
    3372           0 :     aDocuments.AppendElement(ServiceWorkerClientInfo(docList[i], ordinal));
    3373           0 :     ordinal += 1;
    3374             :   }
    3375             : 
    3376           0 :   aDocuments.Sort();
    3377             : }
    3378             : 
    3379             : void
    3380           0 : ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
    3381             :                                        ServiceWorkerRegistrationInfo* aWorkerRegistration)
    3382             : {
    3383           0 :   MOZ_ASSERT(aWorkerRegistration);
    3384           0 :   MOZ_ASSERT(aWorkerRegistration->GetActive());
    3385             : 
    3386             :   // Same origin check
    3387           0 :   if (!aWorkerRegistration->mPrincipal->Equals(aDocument->NodePrincipal())) {
    3388           0 :     return;
    3389             :   }
    3390             : 
    3391             :   // The registration that should be controlling the client
    3392             :   RefPtr<ServiceWorkerRegistrationInfo> matchingRegistration =
    3393           0 :     GetServiceWorkerRegistrationInfo(aDocument);
    3394             : 
    3395             :   // The registration currently controlling the client
    3396           0 :   RefPtr<ServiceWorkerRegistrationInfo> controllingRegistration;
    3397           0 :   GetDocumentRegistration(aDocument, getter_AddRefs(controllingRegistration));
    3398             : 
    3399           0 :   if (aWorkerRegistration != matchingRegistration ||
    3400           0 :         aWorkerRegistration == controllingRegistration) {
    3401           0 :     return;
    3402             :   }
    3403             : 
    3404           0 :   if (controllingRegistration) {
    3405           0 :     StopControllingADocument(controllingRegistration);
    3406             :   }
    3407             : 
    3408           0 :   StartControllingADocument(aWorkerRegistration, aDocument, NS_LITERAL_STRING(""));
    3409           0 :   FireControllerChangeOnDocument(aDocument);
    3410             : }
    3411             : 
    3412             : nsresult
    3413           0 : ServiceWorkerManager::ClaimClients(nsIPrincipal* aPrincipal,
    3414             :                                    const nsCString& aScope, uint64_t aId)
    3415             : {
    3416             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    3417           0 :     GetRegistration(aPrincipal, aScope);
    3418             : 
    3419           0 :   if (!registration || !registration->GetActive() ||
    3420           0 :       !(registration->GetActive()->ID() == aId)) {
    3421             :     // The worker is not active.
    3422           0 :     return NS_ERROR_DOM_INVALID_STATE_ERR;
    3423             :   }
    3424             : 
    3425           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    3426           0 :   if (NS_WARN_IF(!obs)) {
    3427           0 :     return NS_ERROR_FAILURE;
    3428             :   }
    3429             : 
    3430           0 :   nsCOMPtr<nsISimpleEnumerator> enumerator;
    3431           0 :   nsresult rv = obs->EnumerateObservers("service-worker-get-client",
    3432           0 :                                         getter_AddRefs(enumerator));
    3433           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3434           0 :     return rv;
    3435             :   }
    3436             : 
    3437           0 :   bool loop = true;
    3438           0 :   while (NS_SUCCEEDED(enumerator->HasMoreElements(&loop)) && loop) {
    3439           0 :     nsCOMPtr<nsISupports> ptr;
    3440           0 :     rv = enumerator->GetNext(getter_AddRefs(ptr));
    3441           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    3442           0 :       continue;
    3443             :     }
    3444             : 
    3445           0 :     nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
    3446           0 :     MaybeClaimClient(doc, registration);
    3447             :   }
    3448             : 
    3449           0 :   return NS_OK;
    3450             : }
    3451             : 
    3452             : void
    3453           0 : ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal,
    3454             :                                          const nsCString& aScope,
    3455             :                                          uint64_t aServiceWorkerID)
    3456             : {
    3457             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    3458           0 :     GetRegistration(aPrincipal, aScope);
    3459           0 :   if (NS_WARN_IF(!registration)) {
    3460           0 :     return;
    3461             :   }
    3462             : 
    3463             :   RefPtr<ServiceWorkerInfo> worker =
    3464           0 :     registration->GetServiceWorkerInfoById(aServiceWorkerID);
    3465             : 
    3466           0 :   if (NS_WARN_IF(!worker)) {
    3467           0 :     return;
    3468             :   }
    3469             : 
    3470           0 :   worker->SetSkipWaitingFlag();
    3471             : 
    3472           0 :   if (worker->State() == ServiceWorkerState::Installed) {
    3473           0 :     registration->TryToActivateAsync();
    3474             :   }
    3475             : }
    3476             : 
    3477             : void
    3478           0 : ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
    3479             : {
    3480           0 :   AssertIsOnMainThread();
    3481           0 :   for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
    3482           0 :     if (iter.UserData() != aRegistration) {
    3483           0 :       continue;
    3484             :     }
    3485             : 
    3486           0 :     nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
    3487           0 :     if (NS_WARN_IF(!doc)) {
    3488           0 :       continue;
    3489             :     }
    3490             : 
    3491           0 :     FireControllerChangeOnDocument(doc);
    3492             :   }
    3493           0 : }
    3494             : 
    3495             : already_AddRefed<ServiceWorkerRegistrationInfo>
    3496           0 : ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal,
    3497             :                                       const nsACString& aScope) const
    3498             : {
    3499           0 :   MOZ_ASSERT(aPrincipal);
    3500             : 
    3501           0 :   nsAutoCString scopeKey;
    3502           0 :   nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
    3503           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3504           0 :     return nullptr;
    3505             :   }
    3506             : 
    3507           0 :   return GetRegistration(scopeKey, aScope);
    3508             : }
    3509             : 
    3510             : NS_IMETHODIMP
    3511           0 : ServiceWorkerManager::GetRegistrationByPrincipal(nsIPrincipal* aPrincipal,
    3512             :                                                  const nsAString& aScope,
    3513             :                                                  nsIServiceWorkerRegistrationInfo** aInfo)
    3514             : {
    3515           0 :   MOZ_ASSERT(aPrincipal);
    3516           0 :   MOZ_ASSERT(aInfo);
    3517             : 
    3518           0 :   nsCOMPtr<nsIURI> scopeURI;
    3519           0 :   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
    3520           0 :   if (NS_FAILED(rv)) {
    3521           0 :     return NS_ERROR_FAILURE;
    3522             :   }
    3523             : 
    3524             :   RefPtr<ServiceWorkerRegistrationInfo> info =
    3525           0 :     GetServiceWorkerRegistrationInfo(aPrincipal, scopeURI);
    3526           0 :   if (!info) {
    3527           0 :     return NS_ERROR_FAILURE;
    3528             :   }
    3529           0 :   info.forget(aInfo);
    3530             : 
    3531           0 :   return NS_OK;
    3532             : }
    3533             : 
    3534             : already_AddRefed<ServiceWorkerRegistrationInfo>
    3535           0 : ServiceWorkerManager::GetRegistration(const nsACString& aScopeKey,
    3536             :                                       const nsACString& aScope) const
    3537             : {
    3538           0 :   RefPtr<ServiceWorkerRegistrationInfo> reg;
    3539             : 
    3540             :   RegistrationDataPerPrincipal* data;
    3541           0 :   if (!mRegistrationInfos.Get(aScopeKey, &data)) {
    3542           0 :     return reg.forget();
    3543             :   }
    3544             : 
    3545           0 :   data->mInfos.Get(aScope, getter_AddRefs(reg));
    3546           0 :   return reg.forget();
    3547             : }
    3548             : 
    3549             : already_AddRefed<ServiceWorkerRegistrationInfo>
    3550           0 : ServiceWorkerManager::CreateNewRegistration(const nsCString& aScope,
    3551             :                                             nsIPrincipal* aPrincipal,
    3552             :                                             nsLoadFlags aLoadFlags)
    3553             : {
    3554             : #ifdef DEBUG
    3555           0 :   AssertIsOnMainThread();
    3556           0 :   nsCOMPtr<nsIURI> scopeURI;
    3557           0 :   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
    3558           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    3559             : 
    3560             :   RefPtr<ServiceWorkerRegistrationInfo> tmp =
    3561           0 :     GetRegistration(aPrincipal, aScope);
    3562           0 :   MOZ_ASSERT(!tmp);
    3563             : #endif
    3564             : 
    3565             :   RefPtr<ServiceWorkerRegistrationInfo> registration =
    3566           0 :     new ServiceWorkerRegistrationInfo(aScope, aPrincipal, aLoadFlags);
    3567             :   // From now on ownership of registration is with
    3568             :   // mServiceWorkerRegistrationInfos.
    3569           0 :   AddScopeAndRegistration(aScope, registration);
    3570           0 :   return registration.forget();
    3571             : }
    3572             : 
    3573             : void
    3574           0 : ServiceWorkerManager::MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
    3575             : {
    3576           0 :   MOZ_ASSERT(aRegistration);
    3577           0 :   RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
    3578           0 :   if (!newest && HasScope(aRegistration->mPrincipal, aRegistration->mScope)) {
    3579           0 :     RemoveRegistration(aRegistration);
    3580             :   }
    3581           0 : }
    3582             : 
    3583             : void
    3584           0 : ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
    3585             : {
    3586             :   // Note, we do not need to call mActor->SendUnregister() here.  There are a few
    3587             :   // ways we can get here:
    3588             :   // 1) Through a normal unregister which calls SendUnregister() in the unregister
    3589             :   //    job Start() method.
    3590             :   // 2) Through origin storage being purged.  These result in ForceUnregister()
    3591             :   //    starting unregister jobs which in turn call SendUnregister().
    3592             :   // 3) Through the failure to install a new service worker.  Since we don't store
    3593             :   //    the registration until install succeeds, we do not need to call
    3594             :   //    SendUnregister here.
    3595             :   // Assert these conditions by testing for pending uninstall (cases 1 and 2) or
    3596             :   // null workers (case 3).
    3597             : #ifdef DEBUG
    3598           0 :   RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
    3599           0 :   MOZ_ASSERT(aRegistration->mPendingUninstall || !newest);
    3600             : #endif
    3601             : 
    3602           0 :   MOZ_ASSERT(HasScope(aRegistration->mPrincipal, aRegistration->mScope));
    3603             : 
    3604             :   // When a registration is removed, we must clear its contents since the DOM
    3605             :   // object may be held by content script.
    3606           0 :   aRegistration->Clear();
    3607             : 
    3608           0 :   RemoveScopeAndRegistration(aRegistration);
    3609           0 : }
    3610             : 
    3611             : namespace {
    3612             : /**
    3613             :  * See toolkit/modules/sessionstore/Utils.jsm function hasRootDomain().
    3614             :  *
    3615             :  * Returns true if the |url| passed in is part of the given root |domain|.
    3616             :  * For example, if |url| is "www.mozilla.org", and we pass in |domain| as
    3617             :  * "mozilla.org", this will return true. It would return false the other way
    3618             :  * around.
    3619             :  */
    3620             : bool
    3621           0 : HasRootDomain(nsIURI* aURI, const nsACString& aDomain)
    3622             : {
    3623           0 :   AssertIsOnMainThread();
    3624           0 :   MOZ_ASSERT(aURI);
    3625             : 
    3626           0 :   nsAutoCString host;
    3627           0 :   nsresult rv = aURI->GetHost(host);
    3628           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    3629           0 :     return false;
    3630             :   }
    3631             : 
    3632           0 :   nsACString::const_iterator start, end;
    3633           0 :   host.BeginReading(start);
    3634           0 :   host.EndReading(end);
    3635           0 :   if (!FindInReadable(aDomain, start, end)) {
    3636           0 :     return false;
    3637             :   }
    3638             : 
    3639           0 :   if (host.Equals(aDomain)) {
    3640           0 :     return true;
    3641             :   }
    3642             : 
    3643             :   // Beginning of the string matches, can't look at the previous char.
    3644           0 :   if (start.get() == host.BeginReading()) {
    3645             :     // Equals failed so this is fine.
    3646           0 :     return false;
    3647             :   }
    3648             : 
    3649           0 :   char prevChar = *(--start);
    3650           0 :   return prevChar == '.';
    3651             : }
    3652             : 
    3653             : } // namespace
    3654             : 
    3655             : NS_IMETHODIMP
    3656           0 : ServiceWorkerManager::GetAllRegistrations(nsIArray** aResult)
    3657             : {
    3658           0 :   AssertIsOnMainThread();
    3659             : 
    3660           0 :   nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID));
    3661           0 :   if (!array) {
    3662           0 :     return NS_ERROR_OUT_OF_MEMORY;
    3663             :   }
    3664             : 
    3665           0 :   for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
    3666           0 :     for (auto it2 = it1.UserData()->mInfos.Iter(); !it2.Done(); it2.Next()) {
    3667           0 :       ServiceWorkerRegistrationInfo* reg = it2.UserData();
    3668           0 :       MOZ_ASSERT(reg);
    3669             : 
    3670           0 :       if (reg->mPendingUninstall) {
    3671           0 :         continue;
    3672             :       }
    3673             : 
    3674           0 :       array->AppendElement(reg, false);
    3675             :     }
    3676             :   }
    3677             : 
    3678           0 :   array.forget(aResult);
    3679           0 :   return NS_OK;
    3680             : }
    3681             : 
    3682             : // MUST ONLY BE CALLED FROM Remove(), RemoveAll() and RemoveAllRegistrations()!
    3683             : void
    3684           0 : ServiceWorkerManager::ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
    3685             :                                       ServiceWorkerRegistrationInfo* aRegistration)
    3686             : {
    3687           0 :   MOZ_ASSERT(aRegistrationData);
    3688           0 :   MOZ_ASSERT(aRegistration);
    3689             : 
    3690           0 :   RefPtr<ServiceWorkerJobQueue> queue;
    3691           0 :   aRegistrationData->mJobQueues.Get(aRegistration->mScope, getter_AddRefs(queue));
    3692           0 :   if (queue) {
    3693           0 :     queue->CancelAll();
    3694             :   }
    3695             : 
    3696           0 :   if (auto entry = aRegistrationData->mUpdateTimers.Lookup(aRegistration->mScope)) {
    3697           0 :     entry.Data()->Cancel();
    3698           0 :     entry.Remove();
    3699             :   }
    3700             : 
    3701             :   // Since Unregister is async, it is ok to call it in an enumeration.
    3702           0 :   Unregister(aRegistration->mPrincipal, nullptr, NS_ConvertUTF8toUTF16(aRegistration->mScope));
    3703           0 : }
    3704             : 
    3705             : NS_IMETHODIMP
    3706           0 : ServiceWorkerManager::RemoveAndPropagate(const nsACString& aHost)
    3707             : {
    3708           0 :   Remove(aHost);
    3709           0 :   PropagateRemove(aHost);
    3710           0 :   return NS_OK;
    3711             : }
    3712             : 
    3713             : void
    3714           0 : ServiceWorkerManager::Remove(const nsACString& aHost)
    3715             : {
    3716           0 :   AssertIsOnMainThread();
    3717             : 
    3718             :   // We need to postpone this operation in case we don't have an actor because
    3719             :   // this is needed by the ForceUnregister.
    3720           0 :   if (!mActor) {
    3721           0 :     RefPtr<nsIRunnable> runnable = new RemoveRunnable(aHost);
    3722           0 :     AppendPendingOperation(runnable);
    3723           0 :     return;
    3724             :   }
    3725             : 
    3726           0 :   for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
    3727           0 :     ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
    3728           0 :     for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
    3729           0 :       ServiceWorkerRegistrationInfo* reg = it2.UserData();
    3730           0 :       nsCOMPtr<nsIURI> scopeURI;
    3731           0 :       nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), it2.Key(),
    3732           0 :                               nullptr, nullptr);
    3733             :       // This way subdomains are also cleared.
    3734           0 :       if (NS_SUCCEEDED(rv) && HasRootDomain(scopeURI, aHost)) {
    3735           0 :         ForceUnregister(data, reg);
    3736             :       }
    3737             :     }
    3738             :   }
    3739             : }
    3740             : 
    3741             : void
    3742           0 : ServiceWorkerManager::PropagateRemove(const nsACString& aHost)
    3743             : {
    3744           0 :   AssertIsOnMainThread();
    3745             : 
    3746           0 :   if (!mActor) {
    3747           0 :     RefPtr<nsIRunnable> runnable = new PropagateRemoveRunnable(aHost);
    3748           0 :     AppendPendingOperation(runnable);
    3749           0 :     return;
    3750             :   }
    3751             : 
    3752           0 :   mActor->SendPropagateRemove(nsCString(aHost));
    3753             : }
    3754             : 
    3755             : void
    3756           0 : ServiceWorkerManager::RemoveAll()
    3757             : {
    3758           0 :   AssertIsOnMainThread();
    3759             : 
    3760           0 :   for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
    3761           0 :     ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
    3762           0 :     for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
    3763           0 :       ServiceWorkerRegistrationInfo* reg = it2.UserData();
    3764           0 :       ForceUnregister(data, reg);
    3765             :     }
    3766             :   }
    3767           0 : }
    3768             : 
    3769             : void
    3770           0 : ServiceWorkerManager::PropagateRemoveAll()
    3771             : {
    3772           0 :   AssertIsOnMainThread();
    3773           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    3774             : 
    3775           0 :   if (!mActor) {
    3776           0 :     RefPtr<nsIRunnable> runnable = new PropagateRemoveAllRunnable();
    3777           0 :     AppendPendingOperation(runnable);
    3778           0 :     return;
    3779             :   }
    3780             : 
    3781           0 :   mActor->SendPropagateRemoveAll();
    3782             : }
    3783             : 
    3784             : void
    3785           0 : ServiceWorkerManager::RemoveAllRegistrations(OriginAttributesPattern* aPattern)
    3786             : {
    3787           0 :   AssertIsOnMainThread();
    3788             : 
    3789           0 :   MOZ_ASSERT(aPattern);
    3790             : 
    3791           0 :   for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
    3792           0 :     ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
    3793             : 
    3794             :     // We can use iteration because ForceUnregister (and Unregister) are
    3795             :     // async. Otherwise doing some R/W operations on an hashtable during
    3796             :     // iteration will crash.
    3797           0 :     for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
    3798           0 :       ServiceWorkerRegistrationInfo* reg = it2.UserData();
    3799             : 
    3800           0 :       MOZ_ASSERT(reg);
    3801           0 :       MOZ_ASSERT(reg->mPrincipal);
    3802             : 
    3803             :       bool matches =
    3804           0 :         aPattern->Matches(reg->mPrincipal->OriginAttributesRef());
    3805           0 :       if (!matches) {
    3806           0 :         continue;
    3807             :       }
    3808             : 
    3809           0 :       ForceUnregister(data, reg);
    3810             :     }
    3811             :   }
    3812           0 : }
    3813             : 
    3814             : NS_IMETHODIMP
    3815           0 : ServiceWorkerManager::AddListener(nsIServiceWorkerManagerListener* aListener)
    3816             : {
    3817           0 :   AssertIsOnMainThread();
    3818             : 
    3819           0 :   if (!aListener || mListeners.Contains(aListener)) {
    3820           0 :     return NS_ERROR_INVALID_ARG;
    3821             :   }
    3822             : 
    3823           0 :   mListeners.AppendElement(aListener);
    3824             : 
    3825           0 :   return NS_OK;
    3826             : }
    3827             : 
    3828             : NS_IMETHODIMP
    3829           0 : ServiceWorkerManager::RemoveListener(nsIServiceWorkerManagerListener* aListener)
    3830             : {
    3831           0 :   AssertIsOnMainThread();
    3832             : 
    3833           0 :   if (!aListener || !mListeners.Contains(aListener)) {
    3834           0 :     return NS_ERROR_INVALID_ARG;
    3835             :   }
    3836             : 
    3837           0 :   mListeners.RemoveElement(aListener);
    3838             : 
    3839           0 :   return NS_OK;
    3840             : }
    3841             : 
    3842             : NS_IMETHODIMP
    3843           0 : ServiceWorkerManager::ShouldReportToWindow(mozIDOMWindowProxy* aWindow,
    3844             :                                            const nsACString& aScope,
    3845             :                                            bool* aResult)
    3846             : {
    3847           0 :   AssertIsOnMainThread();
    3848           0 :   MOZ_ASSERT(aResult);
    3849             : 
    3850           0 :   *aResult = false;
    3851             : 
    3852             :   // Get the inner window ID to compare to our document windows below.
    3853           0 :   nsCOMPtr<nsPIDOMWindowOuter> targetWin = nsPIDOMWindowOuter::From(aWindow);
    3854           0 :   if (NS_WARN_IF(!targetWin)) {
    3855           0 :     return NS_OK;
    3856             :   }
    3857             : 
    3858           0 :   targetWin = targetWin->GetScriptableTop();
    3859           0 :   uint64_t winId = targetWin->WindowID();
    3860             : 
    3861             :   // Check our weak registering document references first.  This way we clear
    3862             :   // out as many dead weak references as possible when this method is called.
    3863           0 :   WeakDocumentList* list = mRegisteringDocuments.Get(aScope);
    3864           0 :   if (list) {
    3865           0 :     for (int32_t i = list->Length() - 1; i >= 0; --i) {
    3866           0 :       nsCOMPtr<nsIDocument> doc = do_QueryReferent(list->ElementAt(i));
    3867           0 :       if (!doc) {
    3868           0 :         list->RemoveElementAt(i);
    3869           0 :         continue;
    3870             :       }
    3871             : 
    3872           0 :       if (!doc->IsCurrentActiveDocument()) {
    3873           0 :         continue;
    3874             :       }
    3875             : 
    3876           0 :       nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
    3877           0 :       if (!win) {
    3878           0 :         continue;
    3879             :       }
    3880             : 
    3881           0 :       win = win->GetScriptableTop();
    3882             : 
    3883             :       // Match.  We should report to this window.
    3884           0 :       if (win && winId == win->WindowID()) {
    3885           0 :         *aResult = true;
    3886           0 :         return NS_OK;
    3887             :       }
    3888             :     }
    3889             : 
    3890           0 :     if (list->IsEmpty()) {
    3891           0 :       list = nullptr;
    3892           0 :       mRegisteringDocuments.Remove(aScope);
    3893             :     }
    3894             :   }
    3895             : 
    3896             :   // Examine any windows performing a navigation that we are currently
    3897             :   // intercepting.
    3898           0 :   InterceptionList* intList = mNavigationInterceptions.Get(aScope);
    3899           0 :   if (intList) {
    3900           0 :     for (uint32_t i = 0; i < intList->Length(); ++i) {
    3901           0 :       nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
    3902             : 
    3903           0 :       nsCOMPtr<nsIChannel> inner;
    3904           0 :       nsresult rv = channel->GetChannel(getter_AddRefs(inner));
    3905           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    3906           0 :         continue;
    3907             :       }
    3908             : 
    3909           0 :       uint64_t id = nsContentUtils::GetInnerWindowID(inner);
    3910           0 :       if (id == 0) {
    3911           0 :         continue;
    3912             :       }
    3913             : 
    3914           0 :       nsCOMPtr<nsPIDOMWindowInner> win = nsGlobalWindow::GetInnerWindowWithId(id)->AsInner();
    3915           0 :       if (!win) {
    3916           0 :         continue;
    3917             :       }
    3918             : 
    3919           0 :       nsCOMPtr<nsPIDOMWindowOuter> outer = win->GetScriptableTop();
    3920             : 
    3921             :       // Match.  We should report to this window.
    3922           0 :       if (outer && winId == outer->WindowID()) {
    3923           0 :         *aResult = true;
    3924           0 :         return NS_OK;
    3925             :       }
    3926             :     }
    3927             :   }
    3928             : 
    3929             :   // Next examine controlled documents to see if the windows match.
    3930           0 :   for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
    3931           0 :     ServiceWorkerRegistrationInfo* reg = iter.UserData();
    3932           0 :     MOZ_ASSERT(reg);
    3933           0 :     if (!reg->mScope.Equals(aScope)) {
    3934           0 :       continue;
    3935             :     }
    3936             : 
    3937           0 :     nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
    3938           0 :     if (!doc || !doc->IsCurrentActiveDocument()) {
    3939           0 :       continue;
    3940             :     }
    3941             : 
    3942           0 :     nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
    3943           0 :     if (!win) {
    3944           0 :       continue;
    3945             :     }
    3946             : 
    3947           0 :     win = win->GetScriptableTop();
    3948             : 
    3949             :     // Match.  We should report to this window.
    3950           0 :     if (win && winId == win->WindowID()) {
    3951           0 :       *aResult = true;
    3952           0 :       return NS_OK;
    3953             :     }
    3954             :   }
    3955             : 
    3956             :   // No match.  We should not report to this window.
    3957           0 :   return NS_OK;
    3958             : }
    3959             : 
    3960             : NS_IMETHODIMP
    3961           0 : ServiceWorkerManager::Observe(nsISupports* aSubject,
    3962             :                               const char* aTopic,
    3963             :                               const char16_t* aData)
    3964             : {
    3965           0 :   if (strcmp(aTopic, PURGE_SESSION_HISTORY) == 0) {
    3966           0 :     MOZ_ASSERT(XRE_IsParentProcess());
    3967           0 :     RemoveAll();
    3968           0 :     PropagateRemoveAll();
    3969           0 :     return NS_OK;
    3970             :   }
    3971             : 
    3972           0 :   if (strcmp(aTopic, PURGE_DOMAIN_DATA) == 0) {
    3973           0 :     MOZ_ASSERT(XRE_IsParentProcess());
    3974           0 :     nsAutoString domain(aData);
    3975           0 :     RemoveAndPropagate(NS_ConvertUTF16toUTF8(domain));
    3976           0 :     return NS_OK;
    3977             :   }
    3978             : 
    3979           0 :   if (strcmp(aTopic, CLEAR_ORIGIN_DATA) == 0) {
    3980           0 :     MOZ_ASSERT(XRE_IsParentProcess());
    3981           0 :     OriginAttributesPattern pattern;
    3982           0 :     MOZ_ALWAYS_TRUE(pattern.Init(nsAutoString(aData)));
    3983             : 
    3984           0 :     RemoveAllRegistrations(&pattern);
    3985           0 :     return NS_OK;
    3986             :   }
    3987             : 
    3988           0 :   if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
    3989           0 :     MaybeStartShutdown();
    3990           0 :     return NS_OK;
    3991             :   }
    3992             : 
    3993           0 :   MOZ_CRASH("Received message we aren't supposed to be registered for!");
    3994             :   return NS_OK;
    3995             : }
    3996             : 
    3997             : NS_IMETHODIMP
    3998           0 : ServiceWorkerManager::PropagateSoftUpdate(JS::Handle<JS::Value> aOriginAttributes,
    3999             :                                           const nsAString& aScope,
    4000             :                                           JSContext* aCx)
    4001             : {
    4002           0 :   AssertIsOnMainThread();
    4003             : 
    4004           0 :   OriginAttributes attrs;
    4005           0 :   if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
    4006           0 :     return NS_ERROR_INVALID_ARG;
    4007             :   }
    4008             : 
    4009           0 :   PropagateSoftUpdate(attrs, aScope);
    4010           0 :   return NS_OK;
    4011             : }
    4012             : 
    4013             : void
    4014           0 : ServiceWorkerManager::PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
    4015             :                                           const nsAString& aScope)
    4016             : {
    4017           0 :   AssertIsOnMainThread();
    4018             : 
    4019           0 :   if (!mActor) {
    4020             :     RefPtr<nsIRunnable> runnable =
    4021           0 :       new PropagateSoftUpdateRunnable(aOriginAttributes, aScope);
    4022           0 :     AppendPendingOperation(runnable);
    4023           0 :     return;
    4024             :   }
    4025             : 
    4026           0 :   mActor->SendPropagateSoftUpdate(aOriginAttributes, nsString(aScope));
    4027             : }
    4028             : 
    4029             : NS_IMETHODIMP
    4030           0 : ServiceWorkerManager::PropagateUnregister(nsIPrincipal* aPrincipal,
    4031             :                                           nsIServiceWorkerUnregisterCallback* aCallback,
    4032             :                                           const nsAString& aScope)
    4033             : {
    4034           0 :   AssertIsOnMainThread();
    4035           0 :   MOZ_ASSERT(aPrincipal);
    4036             : 
    4037           0 :   if (!mActor) {
    4038             :     RefPtr<nsIRunnable> runnable =
    4039           0 :       new PropagateUnregisterRunnable(aPrincipal, aCallback, aScope);
    4040           0 :     AppendPendingOperation(runnable);
    4041           0 :     return NS_OK;
    4042             :   }
    4043             : 
    4044           0 :   PrincipalInfo principalInfo;
    4045           0 :   if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
    4046             :                                                     &principalInfo)))) {
    4047           0 :     return NS_ERROR_FAILURE;
    4048             :   }
    4049             : 
    4050           0 :   mActor->SendPropagateUnregister(principalInfo, nsString(aScope));
    4051             : 
    4052           0 :   nsresult rv = Unregister(aPrincipal, aCallback, aScope);
    4053           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    4054           0 :     return rv;
    4055             :   }
    4056             : 
    4057           0 :   return NS_OK;
    4058             : }
    4059             : 
    4060             : void
    4061           0 : ServiceWorkerManager::NotifyListenersOnRegister(
    4062             :                                         nsIServiceWorkerRegistrationInfo* aInfo)
    4063             : {
    4064           0 :   nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners);
    4065           0 :   for (size_t index = 0; index < listeners.Length(); ++index) {
    4066           0 :     listeners[index]->OnRegister(aInfo);
    4067             :   }
    4068           0 : }
    4069             : 
    4070             : void
    4071           0 : ServiceWorkerManager::NotifyListenersOnUnregister(
    4072             :                                         nsIServiceWorkerRegistrationInfo* aInfo)
    4073             : {
    4074           0 :   nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners);
    4075           0 :   for (size_t index = 0; index < listeners.Length(); ++index) {
    4076           0 :     listeners[index]->OnUnregister(aInfo);
    4077             :   }
    4078           0 : }
    4079             : 
    4080             : void
    4081           0 : ServiceWorkerManager::AddRegisteringDocument(const nsACString& aScope,
    4082             :                                              nsIDocument* aDoc)
    4083             : {
    4084           0 :   AssertIsOnMainThread();
    4085           0 :   MOZ_ASSERT(!aScope.IsEmpty());
    4086           0 :   MOZ_ASSERT(aDoc);
    4087             : 
    4088           0 :   WeakDocumentList* list = mRegisteringDocuments.LookupOrAdd(aScope);
    4089           0 :   MOZ_ASSERT(list);
    4090             : 
    4091           0 :   for (int32_t i = list->Length() - 1; i >= 0; --i) {
    4092           0 :     nsCOMPtr<nsIDocument> existing = do_QueryReferent(list->ElementAt(i));
    4093           0 :     if (!existing) {
    4094           0 :       list->RemoveElementAt(i);
    4095           0 :       continue;
    4096             :     }
    4097           0 :     if (existing == aDoc) {
    4098           0 :       return;
    4099             :     }
    4100             :   }
    4101             : 
    4102           0 :   list->AppendElement(do_GetWeakReference(aDoc));
    4103             : }
    4104             : 
    4105             : class ServiceWorkerManager::InterceptionReleaseHandle final : public nsISupports
    4106             : {
    4107             :   const nsCString mScope;
    4108             : 
    4109             :   // Weak reference to channel is safe, because the channel holds a
    4110             :   // reference to this object.  Also, the pointer is only used for
    4111             :   // comparison purposes.
    4112             :   nsIInterceptedChannel* mChannel;
    4113             : 
    4114           0 :   ~InterceptionReleaseHandle()
    4115           0 :   {
    4116           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    4117           0 :     if (swm) {
    4118           0 :       swm->RemoveNavigationInterception(mScope, mChannel);
    4119             :     }
    4120           0 :   }
    4121             : 
    4122             : public:
    4123           0 :   InterceptionReleaseHandle(const nsACString& aScope,
    4124             :                             nsIInterceptedChannel* aChannel)
    4125           0 :     : mScope(aScope)
    4126           0 :     , mChannel(aChannel)
    4127             :   {
    4128           0 :     AssertIsOnMainThread();
    4129           0 :     MOZ_ASSERT(!aScope.IsEmpty());
    4130           0 :     MOZ_ASSERT(mChannel);
    4131           0 :   }
    4132             : 
    4133             :   NS_DECL_ISUPPORTS
    4134             : };
    4135             : 
    4136           0 : NS_IMPL_ISUPPORTS0(ServiceWorkerManager::InterceptionReleaseHandle);
    4137             : 
    4138             : void
    4139           0 : ServiceWorkerManager::AddNavigationInterception(const nsACString& aScope,
    4140             :                                                 nsIInterceptedChannel* aChannel)
    4141             : {
    4142           0 :   AssertIsOnMainThread();
    4143           0 :   MOZ_ASSERT(!aScope.IsEmpty());
    4144           0 :   MOZ_ASSERT(aChannel);
    4145             : 
    4146             :   InterceptionList* list =
    4147           0 :     mNavigationInterceptions.LookupOrAdd(aScope);
    4148           0 :   MOZ_ASSERT(list);
    4149           0 :   MOZ_ASSERT(!list->Contains(aChannel));
    4150             : 
    4151             :   nsCOMPtr<nsISupports> releaseHandle =
    4152           0 :     new InterceptionReleaseHandle(aScope, aChannel);
    4153           0 :   aChannel->SetReleaseHandle(releaseHandle);
    4154             : 
    4155           0 :   list->AppendElement(aChannel);
    4156           0 : }
    4157             : 
    4158             : void
    4159           0 : ServiceWorkerManager::RemoveNavigationInterception(const nsACString& aScope,
    4160             :                                                    nsIInterceptedChannel* aChannel)
    4161             : {
    4162           0 :   AssertIsOnMainThread();
    4163           0 :   MOZ_ASSERT(aChannel);
    4164             :   InterceptionList* list =
    4165           0 :     mNavigationInterceptions.Get(aScope);
    4166           0 :   if (list) {
    4167           0 :     MOZ_ALWAYS_TRUE(list->RemoveElement(aChannel));
    4168           0 :     MOZ_ASSERT(!list->Contains(aChannel));
    4169           0 :     if (list->IsEmpty()) {
    4170           0 :       list = nullptr;
    4171           0 :       mNavigationInterceptions.Remove(aScope);
    4172             :     }
    4173             :   }
    4174           0 : }
    4175             : 
    4176             : class UpdateTimerCallback final : public nsITimerCallback
    4177             : {
    4178             :   nsCOMPtr<nsIPrincipal> mPrincipal;
    4179             :   const nsCString mScope;
    4180             : 
    4181           0 :   ~UpdateTimerCallback()
    4182           0 :   {
    4183           0 :   }
    4184             : 
    4185             : public:
    4186           0 :   UpdateTimerCallback(nsIPrincipal* aPrincipal, const nsACString& aScope)
    4187           0 :     : mPrincipal(aPrincipal)
    4188           0 :     , mScope(aScope)
    4189             :   {
    4190           0 :     AssertIsOnMainThread();
    4191           0 :     MOZ_ASSERT(mPrincipal);
    4192           0 :     MOZ_ASSERT(!mScope.IsEmpty());
    4193           0 :   }
    4194             : 
    4195             :   NS_IMETHOD
    4196           0 :   Notify(nsITimer* aTimer) override
    4197             :   {
    4198           0 :     AssertIsOnMainThread();
    4199             : 
    4200           0 :     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    4201           0 :     if (!swm) {
    4202             :       // shutting down, do nothing
    4203           0 :       return NS_OK;
    4204             :     }
    4205             : 
    4206           0 :     swm->UpdateTimerFired(mPrincipal, mScope);
    4207           0 :     return NS_OK;
    4208             :   }
    4209             : 
    4210             :   NS_DECL_ISUPPORTS
    4211             : };
    4212             : 
    4213           0 : NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback)
    4214             : 
    4215             : bool
    4216           0 : ServiceWorkerManager::MayHaveActiveServiceWorkerInstance(ContentParent* aContent,
    4217             :                                                          nsIPrincipal* aPrincipal)
    4218             : {
    4219           0 :   AssertIsOnMainThread();
    4220           0 :   MOZ_ASSERT(aPrincipal);
    4221             : 
    4222           0 :   if (mShuttingDown) {
    4223           0 :     return false;
    4224             :   }
    4225             : 
    4226           0 :   nsAutoCString scopeKey;
    4227           0 :   nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
    4228           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    4229           0 :     return false;
    4230             :   }
    4231             : 
    4232             :   RegistrationDataPerPrincipal* data;
    4233           0 :   if (!mRegistrationInfos.Get(scopeKey, &data)) {
    4234           0 :     return false;
    4235             :   }
    4236             : 
    4237           0 :   return true;
    4238             : }
    4239             : 
    4240             : void
    4241           0 : ServiceWorkerManager::ScheduleUpdateTimer(nsIPrincipal* aPrincipal,
    4242             :                                           const nsACString& aScope)
    4243             : {
    4244           0 :   AssertIsOnMainThread();
    4245           0 :   MOZ_ASSERT(aPrincipal);
    4246           0 :   MOZ_ASSERT(!aScope.IsEmpty());
    4247             : 
    4248           0 :   if (mShuttingDown) {
    4249           0 :     return;
    4250             :   }
    4251             : 
    4252           0 :   nsAutoCString scopeKey;
    4253           0 :   nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
    4254           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    4255           0 :     return;
    4256             :   }
    4257             : 
    4258             :   RegistrationDataPerPrincipal* data;
    4259           0 :   if (!mRegistrationInfos.Get(scopeKey, &data)) {
    4260           0 :     return;
    4261             :   }
    4262             : 
    4263           0 :   nsCOMPtr<nsITimer>& timer = data->mUpdateTimers.GetOrInsert(aScope);
    4264           0 :   if (timer) {
    4265             :     // There is already a timer scheduled.  In this case just use the original
    4266             :     // schedule time.  We don't want to push it out to a later time since that
    4267             :     // could allow updates to be starved forever if events are continuously
    4268             :     // fired.
    4269           0 :     return;
    4270             :   }
    4271             : 
    4272           0 :   timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
    4273           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    4274           0 :     data->mUpdateTimers.Remove(aScope); // another lookup, but very rare
    4275           0 :     return;
    4276             :   }
    4277             : 
    4278             :   nsCOMPtr<nsITimerCallback> callback = new UpdateTimerCallback(aPrincipal,
    4279           0 :                                                                 aScope);
    4280             : 
    4281           0 :   const uint32_t UPDATE_DELAY_MS = 1000;
    4282             : 
    4283           0 :   rv = timer->InitWithCallback(callback, UPDATE_DELAY_MS,
    4284           0 :                                nsITimer::TYPE_ONE_SHOT);
    4285           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    4286           0 :     data->mUpdateTimers.Remove(aScope); // another lookup, but very rare
    4287           0 :     return;
    4288             :   }
    4289             : }
    4290             : 
    4291             : void
    4292           0 : ServiceWorkerManager::UpdateTimerFired(nsIPrincipal* aPrincipal,
    4293             :                                        const nsACString& aScope)
    4294             : {
    4295           0 :   AssertIsOnMainThread();
    4296           0 :   MOZ_ASSERT(aPrincipal);
    4297           0 :   MOZ_ASSERT(!aScope.IsEmpty());
    4298             : 
    4299           0 :   if (mShuttingDown) {
    4300           0 :     return;
    4301             :   }
    4302             : 
    4303             :   // First cleanup the timer.
    4304           0 :   nsAutoCString scopeKey;
    4305           0 :   nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
    4306           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    4307           0 :     return;
    4308             :   }
    4309             : 
    4310             :   RegistrationDataPerPrincipal* data;
    4311           0 :   if (!mRegistrationInfos.Get(scopeKey, &data)) {
    4312           0 :     return;
    4313             :   }
    4314             : 
    4315           0 :   if (auto entry = data->mUpdateTimers.Lookup(aScope)) {
    4316           0 :     entry.Data()->Cancel();
    4317           0 :     entry.Remove();
    4318             :   }
    4319             : 
    4320           0 :   RefPtr<ServiceWorkerRegistrationInfo> registration;
    4321           0 :   data->mInfos.Get(aScope, getter_AddRefs(registration));
    4322           0 :   if (!registration) {
    4323           0 :     return;
    4324             :   }
    4325             : 
    4326           0 :   if (!registration->CheckAndClearIfUpdateNeeded()) {
    4327           0 :     return;
    4328             :   }
    4329             : 
    4330           0 :   OriginAttributes attrs = aPrincipal->OriginAttributesRef();
    4331             : 
    4332           0 :   SoftUpdate(attrs, aScope);
    4333             : }
    4334             : 
    4335             : void
    4336           0 : ServiceWorkerManager::MaybeSendUnregister(nsIPrincipal* aPrincipal,
    4337             :                                           const nsACString& aScope)
    4338             : {
    4339           0 :   AssertIsOnMainThread();
    4340           0 :   MOZ_ASSERT(aPrincipal);
    4341           0 :   MOZ_ASSERT(!aScope.IsEmpty());
    4342             : 
    4343           0 :   if (!mActor) {
    4344           0 :     return;
    4345             :   }
    4346             : 
    4347           0 :   PrincipalInfo principalInfo;
    4348           0 :   nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
    4349           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    4350           0 :     return;
    4351             :   }
    4352             : 
    4353           0 :   Unused << mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(aScope));
    4354             : }
    4355             : 
    4356             : END_WORKERS_NAMESPACE

Generated by: LCOV version 1.13