LCOV - code coverage report
Current view: top level - dom/workers - ServiceWorkerScriptCache.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 633 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 62 0.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 "ServiceWorkerScriptCache.h"
       8             : #include "mozilla/SystemGroup.h"
       9             : #include "mozilla/Unused.h"
      10             : #include "mozilla/dom/CacheBinding.h"
      11             : #include "mozilla/dom/cache/CacheStorage.h"
      12             : #include "mozilla/dom/cache/Cache.h"
      13             : #include "mozilla/dom/Promise.h"
      14             : #include "mozilla/dom/PromiseWorkerProxy.h"
      15             : #include "mozilla/dom/ScriptLoader.h"
      16             : #include "mozilla/ipc/BackgroundUtils.h"
      17             : #include "mozilla/ipc/PBackgroundSharedTypes.h"
      18             : #include "nsICacheInfoChannel.h"
      19             : #include "nsIHttpChannelInternal.h"
      20             : #include "nsIStreamLoader.h"
      21             : #include "nsIThreadRetargetableRequest.h"
      22             : 
      23             : #include "nsIInputStreamPump.h"
      24             : #include "nsIPrincipal.h"
      25             : #include "nsIScriptError.h"
      26             : #include "nsIScriptSecurityManager.h"
      27             : #include "nsContentUtils.h"
      28             : #include "nsNetUtil.h"
      29             : #include "ServiceWorkerManager.h"
      30             : #include "Workers.h"
      31             : #include "nsStringStream.h"
      32             : 
      33             : using mozilla::dom::cache::Cache;
      34             : using mozilla::dom::cache::CacheStorage;
      35             : using mozilla::ipc::PrincipalInfo;
      36             : 
      37             : BEGIN_WORKERS_NAMESPACE
      38             : 
      39             : namespace serviceWorkerScriptCache {
      40             : 
      41             : namespace {
      42             : 
      43             : // XXX A sandbox nsIGlobalObject does not preserve its reflector, so |aSandbox|
      44             : // must be kept alive as long as the CacheStorage if you want to ensure that
      45             : // the CacheStorage will continue to work. Failures will manifest as errors
      46             : // like "JavaScript error: , line 0: TypeError: The expression cannot be
      47             : // converted to return the specified type."
      48             : already_AddRefed<CacheStorage>
      49           0 : CreateCacheStorage(JSContext* aCx, nsIPrincipal* aPrincipal, ErrorResult& aRv,
      50             :                    JS::MutableHandle<JSObject*> aSandbox)
      51             : {
      52           0 :   AssertIsOnMainThread();
      53           0 :   MOZ_ASSERT(aPrincipal);
      54             : 
      55           0 :   nsIXPConnect* xpc = nsContentUtils::XPConnect();
      56           0 :   MOZ_ASSERT(xpc, "This should never be null!");
      57           0 :   aRv = xpc->CreateSandbox(aCx, aPrincipal, aSandbox.address());
      58           0 :   if (NS_WARN_IF(aRv.Failed())) {
      59           0 :     return nullptr;
      60             :   }
      61             : 
      62           0 :   nsCOMPtr<nsIGlobalObject> sandboxGlobalObject = xpc::NativeGlobal(aSandbox);
      63           0 :   if (!sandboxGlobalObject) {
      64           0 :     aRv.Throw(NS_ERROR_FAILURE);
      65           0 :     return nullptr;
      66             :   }
      67             : 
      68             :   // We assume private browsing is not enabled here.  The ScriptLoader
      69             :   // explicitly fails for private browsing so there should never be
      70             :   // a service worker running in private browsing mode.  Therefore if
      71             :   // we are purging scripts or running a comparison algorithm we cannot
      72             :   // be in private browing.
      73             :   //
      74             :   // Also, bypass the CacheStorage trusted origin checks.  The ServiceWorker
      75             :   // has validated the origin prior to this point.  All the information
      76             :   // to revalidate is not available now.
      77             :   return CacheStorage::CreateOnMainThread(cache::CHROME_ONLY_NAMESPACE,
      78             :                                           sandboxGlobalObject, aPrincipal,
      79             :                                           false /* private browsing */,
      80             :                                           true /* force trusted origin */,
      81           0 :                                           aRv);
      82             : }
      83             : 
      84             : class CompareManager;
      85             : class CompareCache;
      86             : 
      87             : // This class downloads a URL from the network, compare the downloaded script
      88             : // with an existing cache if provided, and report to CompareManager via calling
      89             : // ComparisonFinished().
      90             : class CompareNetwork final : public nsIStreamLoaderObserver,
      91             :                              public nsIRequestObserver
      92             : {
      93             : public:
      94             :   NS_DECL_ISUPPORTS
      95             :   NS_DECL_NSISTREAMLOADEROBSERVER
      96             :   NS_DECL_NSIREQUESTOBSERVER
      97             : 
      98           0 :   CompareNetwork(CompareManager* aManager,
      99             :                  ServiceWorkerRegistrationInfo* aRegistration,
     100             :                  bool aIsMainScript)
     101           0 :     : mManager(aManager)
     102             :     , mRegistration(aRegistration)
     103             :     , mIsMainScript(aIsMainScript)
     104           0 :     , mInternalHeaders(new InternalHeaders())
     105             :     , mLoadFlags(nsIChannel::LOAD_BYPASS_SERVICE_WORKER)
     106             :     , mState(WaitingForInitialization)
     107             :     , mNetworkResult(NS_OK)
     108           0 :     , mCacheResult(NS_OK)
     109             :   {
     110           0 :     MOZ_ASSERT(aManager);
     111           0 :     AssertIsOnMainThread();
     112           0 :   }
     113             : 
     114             :   nsresult
     115             :   Initialize(nsIPrincipal* aPrincipal,
     116             :              const nsAString& aURL,
     117             :              nsILoadGroup* aLoadGroup,
     118             :              Cache* const aCache);
     119             : 
     120             :   void
     121             :   Abort();
     122             : 
     123             :   void
     124             :   NetworkFinish(nsresult aRv);
     125             : 
     126             :   void
     127             :   CacheFinish(nsresult aRv);
     128             : 
     129           0 :   const nsString& URL() const
     130             :   {
     131           0 :     AssertIsOnMainThread();
     132           0 :     return mURL;
     133             :   }
     134             : 
     135           0 :   const nsString& Buffer() const
     136             :   {
     137           0 :     AssertIsOnMainThread();
     138           0 :     return mBuffer;
     139             :   }
     140             : 
     141             :   const ChannelInfo&
     142           0 :   GetChannelInfo() const
     143             :   {
     144           0 :     return mChannelInfo;
     145             :   }
     146             : 
     147             :   already_AddRefed<InternalHeaders>
     148           0 :   GetInternalHeaders() const
     149             :   {
     150           0 :     RefPtr<InternalHeaders> internalHeaders = mInternalHeaders;
     151           0 :     return internalHeaders.forget();
     152             :   }
     153             : 
     154             :   UniquePtr<PrincipalInfo>
     155           0 :   TakePrincipalInfo()
     156             :   {
     157           0 :     return Move(mPrincipalInfo);
     158             :   }
     159             : 
     160             :   bool
     161           0 :   Succeeded() const
     162             :   {
     163           0 :     return NS_SUCCEEDED(mNetworkResult);
     164             :   }
     165             : 
     166             : private:
     167           0 :   ~CompareNetwork()
     168           0 :   {
     169           0 :     AssertIsOnMainThread();
     170           0 :     MOZ_ASSERT(!mCC);
     171           0 :   }
     172             : 
     173             :   void
     174             :   Finish();
     175             : 
     176             :   nsresult
     177             :   SetPrincipalInfo(nsIChannel* aChannel);
     178             : 
     179             :   RefPtr<CompareManager> mManager;
     180             :   RefPtr<CompareCache> mCC;
     181             :   RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
     182             : 
     183             :   bool mIsMainScript;
     184             : 
     185             :   nsCOMPtr<nsIChannel> mChannel;
     186             :   nsString mBuffer;
     187             :   nsString mURL;
     188             :   ChannelInfo mChannelInfo;
     189             :   RefPtr<InternalHeaders> mInternalHeaders;
     190             :   UniquePtr<PrincipalInfo> mPrincipalInfo;
     191             : 
     192             :   nsCString mMaxScope;
     193             :   nsLoadFlags mLoadFlags;
     194             : 
     195             :   enum {
     196             :     WaitingForInitialization,
     197             :     WaitingForBothFinished,
     198             :     WaitingForNetworkFinished,
     199             :     WaitingForCacheFinished,
     200             :     Finished
     201             :   } mState;
     202             : 
     203             :   nsresult mNetworkResult;
     204             :   nsresult mCacheResult;
     205             : };
     206             : 
     207           0 : NS_IMPL_ISUPPORTS(CompareNetwork, nsIStreamLoaderObserver,
     208             :                   nsIRequestObserver)
     209             : 
     210             : // This class gets a cached Response from the CacheStorage and then it calls
     211             : // CacheFinish() in the CompareNetwork.
     212             : class CompareCache final : public PromiseNativeHandler
     213             :                          , public nsIStreamLoaderObserver
     214             : {
     215             : public:
     216             :   NS_DECL_ISUPPORTS
     217             :   NS_DECL_NSISTREAMLOADEROBSERVER
     218             : 
     219           0 :   explicit CompareCache(CompareNetwork* aCN)
     220           0 :     : mCN(aCN)
     221             :     , mState(WaitingForInitialization)
     222           0 :     , mInCache(false)
     223             :   {
     224           0 :     MOZ_ASSERT(aCN);
     225           0 :     AssertIsOnMainThread();
     226           0 :   }
     227             : 
     228             :   nsresult
     229             :   Initialize(Cache* const aCache, const nsAString& aURL);
     230             : 
     231             :   void
     232             :   Finish(nsresult aStatus, bool aInCache);
     233             : 
     234             :   void
     235             :   Abort();
     236             : 
     237             :   virtual void
     238             :   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
     239             : 
     240             :   virtual void
     241             :   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
     242             : 
     243           0 :   const nsString& Buffer() const
     244             :   {
     245           0 :     AssertIsOnMainThread();
     246           0 :     return mBuffer;
     247             :   }
     248             : 
     249             :   bool
     250           0 :   InCache()
     251             :   {
     252           0 :     return mInCache;
     253             :   }
     254             : 
     255             : private:
     256           0 :   ~CompareCache()
     257           0 :   {
     258           0 :     AssertIsOnMainThread();
     259           0 :   }
     260             : 
     261             :   void
     262             :   ManageValueResult(JSContext* aCx, JS::Handle<JS::Value> aValue);
     263             : 
     264             :   RefPtr<CompareNetwork> mCN;
     265             :   nsCOMPtr<nsIInputStreamPump> mPump;
     266             : 
     267             :   nsString mURL;
     268             :   nsString mBuffer;
     269             : 
     270             :   enum {
     271             :     WaitingForInitialization,
     272             :     WaitingForScript,
     273             :     Finished,
     274             :   } mState;
     275             : 
     276             :   bool mInCache;
     277             : };
     278             : 
     279           0 : NS_IMPL_ISUPPORTS(CompareCache, nsIStreamLoaderObserver)
     280             : 
     281             : class CompareManager final : public PromiseNativeHandler
     282             : {
     283             : public:
     284             :   NS_DECL_ISUPPORTS
     285             : 
     286           0 :   explicit CompareManager(ServiceWorkerRegistrationInfo* aRegistration,
     287             :                           CompareCallback* aCallback)
     288           0 :     : mRegistration(aRegistration)
     289             :     , mCallback(aCallback)
     290             :     , mLoadFlags(nsIChannel::LOAD_BYPASS_SERVICE_WORKER)
     291             :     , mState(WaitingForInitialization)
     292             :     , mPendingCount(0)
     293           0 :     , mAreScriptsEqual(true)
     294             :   {
     295           0 :     AssertIsOnMainThread();
     296           0 :     MOZ_ASSERT(aRegistration);
     297           0 :   }
     298             : 
     299             :   nsresult
     300             :   Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL,
     301             :              const nsAString& aCacheName, nsILoadGroup* aLoadGroup);
     302             : 
     303             :   void
     304             :   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
     305             : 
     306             :   void
     307             :   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
     308             : 
     309             :   CacheStorage*
     310             :   CacheStorage_()
     311             :   {
     312             :     AssertIsOnMainThread();
     313             :     MOZ_ASSERT(mCacheStorage);
     314             :     return mCacheStorage;
     315             :   }
     316             : 
     317             :   void
     318           0 :   ComparisonFinished(nsresult aStatus,
     319             :                      bool aIsMainScript,
     320             :                      bool aIsEqual,
     321             :                      const nsACString& aMaxScope,
     322             :                      nsLoadFlags aLoadFlags)
     323             :   {
     324           0 :     AssertIsOnMainThread();
     325           0 :     if (mState == Finished) {
     326           0 :       return;
     327             :     }
     328             : 
     329           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForScriptOrComparisonResult);
     330             : 
     331           0 :     if (NS_WARN_IF(NS_FAILED(aStatus))) {
     332           0 :       Fail(aStatus);
     333           0 :       return;
     334             :     }
     335             : 
     336           0 :     mAreScriptsEqual = mAreScriptsEqual && aIsEqual;
     337             : 
     338           0 :     if (aIsMainScript) {
     339           0 :       mMaxScope = aMaxScope;
     340           0 :       mLoadFlags = aLoadFlags;
     341             :     }
     342             : 
     343             :     // Check whether all CompareNetworks finished their jobs.
     344           0 :     MOZ_DIAGNOSTIC_ASSERT(mPendingCount > 0);
     345           0 :     if (--mPendingCount) {
     346           0 :       return;
     347             :     }
     348             : 
     349           0 :     if (mAreScriptsEqual) {
     350           0 :       MOZ_ASSERT(mCallback);
     351           0 :       mCallback->ComparisonResult(aStatus,
     352             :                                   true /* aSameScripts */,
     353           0 :                                   EmptyString(),
     354             :                                   mMaxScope,
     355           0 :                                   mLoadFlags);
     356           0 :       Cleanup();
     357           0 :       return;
     358             :     }
     359             : 
     360             :     // Write to Cache so ScriptLoader reads succeed.
     361           0 :     WriteNetworkBufferToNewCache();
     362             :   }
     363             : 
     364             : private:
     365           0 :   ~CompareManager()
     366           0 :   {
     367           0 :     AssertIsOnMainThread();
     368           0 :     MOZ_ASSERT(mCNList.Length() == 0);
     369           0 :   }
     370             : 
     371             :   void
     372             :   Fail(nsresult aStatus);
     373             : 
     374             :   void
     375             :   Cleanup();
     376             : 
     377             :   nsresult
     378           0 :   FetchScript(const nsAString& aURL,
     379             :               bool aIsMainScript,
     380             :               Cache* const aCache = nullptr)
     381             :   {
     382           0 :     AssertIsOnMainThread();
     383             : 
     384           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForInitialization ||
     385             :                           mState == WaitingForScriptOrComparisonResult);
     386             : 
     387             :     RefPtr<CompareNetwork> cn = new CompareNetwork(this,
     388             :                                                    mRegistration,
     389           0 :                                                    aIsMainScript);
     390           0 :     mCNList.AppendElement(cn);
     391           0 :     mPendingCount += 1;
     392             : 
     393           0 :     nsresult rv = cn->Initialize(mPrincipal, aURL, mLoadGroup, aCache);
     394           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     395           0 :       return rv;
     396             :     }
     397             : 
     398           0 :     return NS_OK;
     399             :   }
     400             : 
     401             :   void
     402           0 :   ManageOldCache(JSContext* aCx, JS::Handle<JS::Value> aValue)
     403             :   {
     404           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForExistingOpen);
     405             : 
     406             :     // RAII Cleanup when fails.
     407           0 :     nsresult rv = NS_ERROR_FAILURE;
     408           0 :     auto guard = MakeScopeExit([&] {
     409           0 :         Fail(rv);
     410           0 :     });
     411             : 
     412           0 :     if (NS_WARN_IF(!aValue.isObject())) {
     413           0 :       return;
     414             :     }
     415             : 
     416           0 :     MOZ_ASSERT(!mOldCache);
     417           0 :     JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
     418           0 :     if (NS_WARN_IF(!obj) ||
     419           0 :         NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Cache, obj, mOldCache)))) {
     420           0 :       return;
     421             :     }
     422             : 
     423           0 :     Optional<RequestOrUSVString> request;
     424           0 :     CacheQueryOptions options;
     425           0 :     ErrorResult error;
     426           0 :     RefPtr<Promise> promise = mOldCache->Keys(request, options, error);
     427           0 :     if (NS_WARN_IF(error.Failed())) {
     428           0 :       rv = error.StealNSResult();
     429           0 :       return;
     430             :     }
     431             : 
     432           0 :     mState = WaitingForExistingKeys;
     433           0 :     promise->AppendNativeHandler(this);
     434           0 :     guard.release();
     435             :   }
     436             : 
     437             :   void
     438           0 :   ManageOldKeys(JSContext* aCx, JS::Handle<JS::Value> aValue)
     439             :   {
     440           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForExistingKeys);
     441             : 
     442             :     // RAII Cleanup when fails.
     443           0 :     nsresult rv = NS_ERROR_FAILURE;
     444           0 :     auto guard = MakeScopeExit([&] {
     445           0 :         Fail(rv);
     446           0 :     });
     447             : 
     448           0 :     if (NS_WARN_IF(!aValue.isObject())) {
     449           0 :       return;
     450             :     }
     451             : 
     452           0 :     JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
     453           0 :     if (NS_WARN_IF(!obj)) {
     454           0 :       return;
     455             :     }
     456             : 
     457           0 :     uint32_t len = 0;
     458           0 :     if (!JS_GetArrayLength(aCx, obj, &len)) {
     459           0 :       return;
     460             :     }
     461             : 
     462             :     // Fetch and compare the source scripts.
     463           0 :     MOZ_ASSERT(mPendingCount == 0);
     464             : 
     465           0 :     mState = WaitingForScriptOrComparisonResult;
     466             : 
     467           0 :     for (uint32_t i = 0; i < len; ++i) {
     468           0 :       JS::Rooted<JS::Value> val(aCx);
     469           0 :       if (NS_WARN_IF(!JS_GetElement(aCx, obj, i, &val)) ||
     470           0 :           NS_WARN_IF(!val.isObject())) {
     471           0 :         return;
     472             :       }
     473             : 
     474             :       Request* request;
     475           0 :       JS::Rooted<JSObject*> requestObj(aCx, &val.toObject());
     476           0 :       if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Request, &requestObj, request)))) {
     477           0 :         return;
     478             :       };
     479             : 
     480           0 :       nsString URL;
     481           0 :       request->GetUrl(URL);
     482             : 
     483           0 :       rv = FetchScript(URL, mURL == URL /* aIsMainScript */, mOldCache);
     484           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
     485           0 :         return;
     486             :       }
     487             :     }
     488             : 
     489           0 :     guard.release();
     490             :   }
     491             : 
     492             :   void
     493           0 :   ManageNewCache(JSContext* aCx, JS::Handle<JS::Value> aValue)
     494             :   {
     495           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForOpen);
     496             : 
     497             :     // RAII Cleanup when fails.
     498           0 :     nsresult rv = NS_ERROR_FAILURE;
     499           0 :     auto guard = MakeScopeExit([&] {
     500           0 :         Fail(rv);
     501           0 :     });
     502             : 
     503           0 :     if (NS_WARN_IF(!aValue.isObject())) {
     504           0 :       return;
     505             :     }
     506             : 
     507           0 :     JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
     508           0 :     if (NS_WARN_IF(!obj)) {
     509           0 :       return;
     510             :     }
     511             : 
     512           0 :     Cache* cache = nullptr;
     513           0 :     rv = UNWRAP_OBJECT(Cache, &obj, cache);
     514           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     515           0 :       return;
     516             :     }
     517             : 
     518             :     // Just to be safe.
     519           0 :     RefPtr<Cache> kungfuDeathGrip = cache;
     520             : 
     521           0 :     MOZ_ASSERT(mPendingCount == 0);
     522           0 :     for (uint32_t i = 0; i < mCNList.Length(); ++i) {
     523             :       // We bail out immediately when something goes wrong.
     524           0 :       rv = WriteToCache(cache, mCNList[i]);
     525           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
     526           0 :         return;
     527             :       }
     528             :     }
     529             : 
     530           0 :     mState = WaitingForPut;
     531           0 :     guard.release();
     532             :   }
     533             : 
     534             :   void
     535           0 :   WriteNetworkBufferToNewCache()
     536             :   {
     537           0 :     AssertIsOnMainThread();
     538           0 :     MOZ_ASSERT(mCNList.Length() != 0);
     539           0 :     MOZ_ASSERT(mCacheStorage);
     540           0 :     MOZ_ASSERT(mNewCacheName.IsEmpty());
     541             : 
     542           0 :     ErrorResult result;
     543           0 :     result = serviceWorkerScriptCache::GenerateCacheName(mNewCacheName);
     544           0 :     if (NS_WARN_IF(result.Failed())) {
     545           0 :       MOZ_ASSERT(!result.IsErrorWithMessage());
     546           0 :       Fail(result.StealNSResult());
     547           0 :       return;
     548             :     }
     549             : 
     550           0 :     RefPtr<Promise> cacheOpenPromise = mCacheStorage->Open(mNewCacheName, result);
     551           0 :     if (NS_WARN_IF(result.Failed())) {
     552           0 :       MOZ_ASSERT(!result.IsErrorWithMessage());
     553           0 :       Fail(result.StealNSResult());
     554           0 :       return;
     555             :     }
     556             : 
     557           0 :     mState = WaitingForOpen;
     558           0 :     cacheOpenPromise->AppendNativeHandler(this);
     559             :   }
     560             : 
     561             :   nsresult
     562           0 :   WriteToCache(Cache* aCache, CompareNetwork* aCN)
     563             :   {
     564           0 :     AssertIsOnMainThread();
     565           0 :     MOZ_ASSERT(aCache);
     566           0 :     MOZ_ASSERT(aCN);
     567           0 :     MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForOpen);
     568             : 
     569             :     // We don't have to save any information from a failed CompareNetwork.
     570           0 :     if (!aCN->Succeeded()) {
     571           0 :       return NS_OK;
     572             :     }
     573             : 
     574           0 :     ErrorResult result;
     575           0 :     nsCOMPtr<nsIInputStream> body;
     576           0 :     result = NS_NewCStringInputStream(getter_AddRefs(body),
     577           0 :                                       NS_ConvertUTF16toUTF8(aCN->Buffer()));
     578           0 :     if (NS_WARN_IF(result.Failed())) {
     579           0 :       MOZ_ASSERT(!result.IsErrorWithMessage());
     580           0 :       return result.StealNSResult();
     581             :     }
     582             : 
     583             :     RefPtr<InternalResponse> ir =
     584           0 :       new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
     585           0 :     ir->SetBody(body, aCN->Buffer().Length());
     586             : 
     587           0 :     ir->InitChannelInfo(aCN->GetChannelInfo());
     588           0 :     UniquePtr<PrincipalInfo> principalInfo = aCN->TakePrincipalInfo();
     589           0 :     if (principalInfo) {
     590           0 :       ir->SetPrincipalInfo(Move(principalInfo));
     591             :     }
     592             : 
     593           0 :     IgnoredErrorResult ignored;
     594           0 :     RefPtr<InternalHeaders> internalHeaders = aCN->GetInternalHeaders();
     595           0 :     ir->Headers()->Fill(*(internalHeaders.get()), ignored);
     596             : 
     597           0 :     RefPtr<Response> response = new Response(aCache->GetGlobalObject(), ir);
     598             : 
     599           0 :     RequestOrUSVString request;
     600           0 :     request.SetAsUSVString().Rebind(aCN->URL().Data(), aCN->URL().Length());
     601             : 
     602             :     // For now we have to wait until the Put Promise is fulfilled before we can
     603             :     // continue since Cache does not yet support starting a read that is being
     604             :     // written to.
     605           0 :     RefPtr<Promise> cachePromise = aCache->Put(request, *response, result);
     606           0 :     if (NS_WARN_IF(result.Failed())) {
     607           0 :       MOZ_ASSERT(!result.IsErrorWithMessage());
     608           0 :       return result.StealNSResult();
     609             :     }
     610             : 
     611           0 :     mPendingCount += 1;
     612           0 :     cachePromise->AppendNativeHandler(this);
     613           0 :     return NS_OK;
     614             :   }
     615             : 
     616             :   RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
     617             :   RefPtr<CompareCallback> mCallback;
     618             :   JS::PersistentRooted<JSObject*> mSandbox;
     619             :   RefPtr<CacheStorage> mCacheStorage;
     620             : 
     621             :   nsTArray<RefPtr<CompareNetwork>> mCNList;
     622             : 
     623             :   nsString mURL;
     624             :   RefPtr<nsIPrincipal> mPrincipal;
     625             :   RefPtr<nsILoadGroup> mLoadGroup;
     626             : 
     627             :   // Used for the old cache where saves the old source scripts.
     628             :   RefPtr<Cache> mOldCache;
     629             : 
     630             :   // Only used if the network script has changed and needs to be cached.
     631             :   nsString mNewCacheName;
     632             : 
     633             :   nsCString mMaxScope;
     634             :   nsLoadFlags mLoadFlags;
     635             : 
     636             :   enum {
     637             :     WaitingForInitialization,
     638             :     WaitingForExistingOpen,
     639             :     WaitingForExistingKeys,
     640             :     WaitingForScriptOrComparisonResult,
     641             :     WaitingForOpen,
     642             :     WaitingForPut,
     643             :     Finished
     644             :   } mState;
     645             : 
     646             :   uint32_t mPendingCount;
     647             :   bool mAreScriptsEqual;
     648             : };
     649             : 
     650           0 : NS_IMPL_ISUPPORTS0(CompareManager)
     651             : 
     652             : nsresult
     653           0 : CompareNetwork::Initialize(nsIPrincipal* aPrincipal,
     654             :                            const nsAString& aURL,
     655             :                            nsILoadGroup* aLoadGroup,
     656             :                            Cache* const aCache)
     657             : {
     658           0 :   MOZ_ASSERT(aPrincipal);
     659           0 :   AssertIsOnMainThread();
     660             : 
     661           0 :   nsCOMPtr<nsIURI> uri;
     662           0 :   nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, nullptr);
     663           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     664           0 :     return rv;
     665             :   }
     666             : 
     667           0 :   mURL = aURL;
     668             : 
     669           0 :   nsCOMPtr<nsILoadGroup> loadGroup;
     670           0 :   rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), aPrincipal);
     671           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     672           0 :     return rv;
     673             :   }
     674             : 
     675             :   // Update LoadFlags for propagating to ServiceWorkerInfo.
     676           0 :   mLoadFlags |= mRegistration->GetLoadFlags();
     677           0 :   if (mRegistration->IsLastUpdateCheckTimeOverOneDay()) {
     678           0 :     mLoadFlags |= nsIRequest::LOAD_BYPASS_CACHE;
     679             :   }
     680             : 
     681             :   // Different settings are needed for fetching imported scripts, since they
     682             :   // might be cross-origin scripts.
     683             :   uint32_t secFlags =
     684           0 :       mIsMainScript ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
     685           0 :                     : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
     686             : 
     687             :   nsContentPolicyType contentPolicyType =
     688           0 :       mIsMainScript ? nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER
     689           0 :                     : nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS;
     690             : 
     691             :   // Note that because there is no "serviceworker" RequestContext type, we can
     692             :   // use the TYPE_INTERNAL_SCRIPT content policy types when loading a service
     693             :   // worker.
     694           0 :   rv = NS_NewChannel(getter_AddRefs(mChannel), uri, aPrincipal, secFlags,
     695             :                      contentPolicyType, loadGroup, nullptr /* aCallbacks */,
     696           0 :                      mLoadFlags);
     697           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     698           0 :     return rv;
     699             :   }
     700             : 
     701           0 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
     702           0 :   if (httpChannel) {
     703             :     // Spec says no redirects allowed for SW scripts.
     704           0 :     rv = httpChannel->SetRedirectionLimit(0);
     705           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     706             : 
     707           0 :     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Service-Worker"),
     708           0 :                                        NS_LITERAL_CSTRING("script"),
     709           0 :                                        /* merge */ false);
     710           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     711             :   }
     712             : 
     713           0 :   nsCOMPtr<nsIStreamLoader> loader;
     714           0 :   rv = NS_NewStreamLoader(getter_AddRefs(loader), this, this);
     715           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     716           0 :     return rv;
     717             :   }
     718             : 
     719           0 :   rv = mChannel->AsyncOpen2(loader);
     720           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     721           0 :     return rv;
     722             :   }
     723             : 
     724             :   // If we do have an existing cache to compare with.
     725           0 :   if (aCache) {
     726           0 :     mCC = new CompareCache(this);
     727           0 :     rv = mCC->Initialize(aCache, aURL);
     728           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     729           0 :       Abort();
     730           0 :       return rv;
     731             :     }
     732             : 
     733           0 :     mState = WaitingForBothFinished;
     734           0 :     return NS_OK;
     735             :   }
     736             : 
     737           0 :   mState = WaitingForNetworkFinished;
     738           0 :   return NS_OK;
     739             : }
     740             : 
     741             :  void
     742           0 : CompareNetwork::Finish()
     743             : {
     744           0 :   if (mState == Finished) {
     745           0 :     return;
     746             :   }
     747             : 
     748           0 :   bool same = true;
     749           0 :   nsresult rv = NS_OK;
     750             : 
     751             :   // mNetworkResult is prior to mCacheResult, since it's needed for reporting
     752             :   // various errors to web contenet.
     753           0 :   if (NS_FAILED(mNetworkResult)) {
     754             :     // An imported script could become offline, since it might no longer be
     755             :     // needed by the new importing script. In that case, the importing script
     756             :     // must be different, and thus, it's okay to report same script found here.
     757           0 :     rv = mIsMainScript ? mNetworkResult : NS_OK;
     758           0 :     same = true;
     759           0 :   } else if (mCC && NS_FAILED(mCacheResult)) {
     760           0 :     rv = mCacheResult;
     761             :   } else { // Both passed.
     762           0 :     same = mCC &&
     763           0 :            mCC->InCache() &&
     764           0 :            mCC->Buffer().Equals(mBuffer);
     765             :   }
     766             : 
     767           0 :   mManager->ComparisonFinished(rv, mIsMainScript, same, mMaxScope, mLoadFlags);
     768             : 
     769             :   // We have done with the CompareCache.
     770           0 :   mCC = nullptr;
     771             : }
     772             : 
     773             : void
     774           0 : CompareNetwork::NetworkFinish(nsresult aRv)
     775             : {
     776           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForBothFinished ||
     777             :                         mState == WaitingForNetworkFinished);
     778             : 
     779           0 :   mNetworkResult = aRv;
     780             : 
     781           0 :   if (mState == WaitingForBothFinished) {
     782           0 :     mState = WaitingForCacheFinished;
     783           0 :     return;
     784             :   }
     785             : 
     786           0 :   if (mState == WaitingForNetworkFinished) {
     787           0 :     Finish();
     788           0 :     return;
     789             :   }
     790             : }
     791             : 
     792             : void
     793           0 : CompareNetwork::CacheFinish(nsresult aRv)
     794             : {
     795           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForBothFinished ||
     796             :                         mState == WaitingForCacheFinished);
     797             : 
     798           0 :   mCacheResult = aRv;
     799             : 
     800           0 :   if (mState == WaitingForBothFinished) {
     801           0 :     mState = WaitingForNetworkFinished;
     802           0 :     return;
     803             :   }
     804             : 
     805           0 :   if (mState == WaitingForCacheFinished) {
     806           0 :     Finish();
     807           0 :     return;
     808             :   }
     809             : }
     810             : 
     811             : void
     812           0 : CompareNetwork::Abort()
     813             : {
     814           0 :   AssertIsOnMainThread();
     815             : 
     816           0 :   if (mState != Finished) {
     817           0 :     mState = Finished;
     818             : 
     819           0 :     MOZ_ASSERT(mChannel);
     820           0 :     mChannel->Cancel(NS_BINDING_ABORTED);
     821           0 :     mChannel = nullptr;
     822             : 
     823           0 :     if (mCC) {
     824           0 :       mCC->Abort();
     825           0 :       mCC = nullptr;
     826             :     }
     827             :   }
     828           0 : }
     829             : 
     830             : NS_IMETHODIMP
     831           0 : CompareNetwork::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
     832             : {
     833           0 :   AssertIsOnMainThread();
     834             : 
     835           0 :   if (mState == Finished) {
     836           0 :     return NS_OK;
     837             :   }
     838             : 
     839             : #ifdef DEBUG
     840           0 :   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     841           0 :   MOZ_ASSERT(channel == mChannel);
     842             : #endif
     843             : 
     844           0 :   MOZ_ASSERT(!mChannelInfo.IsInitialized());
     845           0 :   mChannelInfo.InitFromChannel(mChannel);
     846             : 
     847           0 :   nsresult rv = SetPrincipalInfo(mChannel);
     848           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     849           0 :     return rv;
     850             :   }
     851             : 
     852           0 :   mInternalHeaders->FillResponseHeaders(mChannel);
     853           0 :   return NS_OK;
     854             : }
     855             : 
     856             : nsresult
     857           0 : CompareNetwork::SetPrincipalInfo(nsIChannel* aChannel)
     858             : {
     859           0 :   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
     860           0 :   if (!ssm) {
     861           0 :     return NS_ERROR_FAILURE;
     862             :   }
     863             : 
     864           0 :   nsCOMPtr<nsIPrincipal> channelPrincipal;
     865           0 :   nsresult rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(channelPrincipal));
     866           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     867           0 :     return rv;
     868             :   }
     869             : 
     870           0 :   UniquePtr<PrincipalInfo> principalInfo = MakeUnique<PrincipalInfo>();
     871           0 :   rv = PrincipalToPrincipalInfo(channelPrincipal, principalInfo.get());
     872             : 
     873           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     874           0 :     return rv;
     875             :   }
     876             : 
     877           0 :   mPrincipalInfo = Move(principalInfo);
     878           0 :   return NS_OK;
     879             : }
     880             : 
     881             : NS_IMETHODIMP
     882           0 : CompareNetwork::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
     883             :                               nsresult aStatusCode)
     884             : {
     885             :   // Nothing to do here!
     886           0 :   return NS_OK;
     887             : }
     888             : 
     889             : NS_IMETHODIMP
     890           0 : CompareNetwork::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
     891             :                                  nsresult aStatus, uint32_t aLen,
     892             :                                  const uint8_t* aString)
     893             : {
     894           0 :   AssertIsOnMainThread();
     895             : 
     896           0 :   if (mState == Finished) {
     897           0 :     return NS_OK;
     898             :   }
     899             : 
     900           0 :   nsresult rv = NS_ERROR_FAILURE;
     901           0 :   auto guard = MakeScopeExit([&] {
     902           0 :     NetworkFinish(rv);
     903           0 :   });
     904             : 
     905           0 :   if (NS_WARN_IF(NS_FAILED(aStatus))) {
     906           0 :     rv = (aStatus == NS_ERROR_REDIRECT_LOOP) ? NS_ERROR_DOM_SECURITY_ERR
     907             :                                              : aStatus;
     908           0 :     return NS_OK;
     909             :   }
     910             : 
     911           0 :   nsCOMPtr<nsIRequest> request;
     912           0 :   rv = aLoader->GetRequest(getter_AddRefs(request));
     913           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     914           0 :     return NS_OK;
     915             :   }
     916             : 
     917           0 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
     918           0 :   MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
     919             : 
     920             :   bool requestSucceeded;
     921           0 :   rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
     922           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     923           0 :     return NS_OK;
     924             :   }
     925             : 
     926           0 :   if (NS_WARN_IF(!requestSucceeded)) {
     927             :     // Get the stringified numeric status code, not statusText which could be
     928             :     // something misleading like OK for a 404.
     929           0 :     uint32_t status = 0;
     930           0 :     Unused << httpChannel->GetResponseStatus(&status); // don't care if this fails, use 0.
     931           0 :     nsAutoString statusAsText;
     932           0 :     statusAsText.AppendInt(status);
     933             : 
     934           0 :     ServiceWorkerManager::LocalizeAndReportToAllClients(
     935           0 :       mRegistration->mScope, "ServiceWorkerRegisterNetworkError",
     936           0 :       nsTArray<nsString> { NS_ConvertUTF8toUTF16(mRegistration->mScope),
     937           0 :         statusAsText, mURL });
     938             : 
     939           0 :     rv = NS_ERROR_FAILURE;
     940           0 :     return NS_OK;
     941             :   }
     942             : 
     943             :   // Note: we explicitly don't check for the return value here, because the
     944             :   // absence of the header is not an error condition.
     945           0 :   Unused << httpChannel->GetResponseHeader(
     946           0 :       NS_LITERAL_CSTRING("Service-Worker-Allowed"),
     947           0 :       mMaxScope);
     948             : 
     949           0 :   bool isFromCache = false;
     950           0 :   nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(httpChannel));
     951           0 :   if (cacheChannel) {
     952           0 :     cacheChannel->IsFromCache(&isFromCache);
     953             :   }
     954             : 
     955             :   // [9.2 Update]4.13, If response's cache state is not "local",
     956             :   // set registration's last update check time to the current time
     957           0 :   if (!isFromCache) {
     958           0 :     mRegistration->RefreshLastUpdateCheckTime();
     959             :   }
     960             : 
     961           0 :   nsAutoCString mimeType;
     962           0 :   nsresult rv2 = httpChannel->GetContentType(mimeType);
     963           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     964             :     // We should only end up here if !mResponseHead in the channel.  If headers
     965             :     // were received but no content type was specified, we'll be given
     966             :     // UNKNOWN_CONTENT_TYPE "application/x-unknown-content-type" and so fall
     967             :     // into the next case with its better error message.
     968           0 :     rv = NS_ERROR_DOM_SECURITY_ERR;
     969           0 :     return rv2;
     970             :   }
     971             : 
     972           0 :   if (!mimeType.LowerCaseEqualsLiteral("text/javascript") &&
     973           0 :       !mimeType.LowerCaseEqualsLiteral("application/x-javascript") &&
     974           0 :       !mimeType.LowerCaseEqualsLiteral("application/javascript")) {
     975           0 :     ServiceWorkerManager::LocalizeAndReportToAllClients(
     976           0 :       mRegistration->mScope, "ServiceWorkerRegisterMimeTypeError",
     977           0 :       nsTArray<nsString> { NS_ConvertUTF8toUTF16(mRegistration->mScope),
     978           0 :         NS_ConvertUTF8toUTF16(mimeType), mURL });
     979           0 :     rv = NS_ERROR_DOM_SECURITY_ERR;
     980           0 :     return rv2;
     981             :   }
     982             : 
     983           0 :   char16_t* buffer = nullptr;
     984           0 :   size_t len = 0;
     985             : 
     986           0 :   rv = ScriptLoader::ConvertToUTF16(httpChannel, aString, aLen,
     987           0 :                                     NS_LITERAL_STRING("UTF-8"), nullptr,
     988           0 :                                     buffer, len);
     989           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     990           0 :     return rv;
     991             :   }
     992             : 
     993           0 :   mBuffer.Adopt(buffer, len);
     994             : 
     995           0 :   rv = NS_OK;
     996           0 :   return NS_OK;
     997             : }
     998             : 
     999             : nsresult
    1000           0 : CompareCache::Initialize(Cache* const aCache, const nsAString& aURL)
    1001             : {
    1002           0 :   AssertIsOnMainThread();
    1003           0 :   MOZ_ASSERT(aCache);
    1004           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForInitialization);
    1005             : 
    1006           0 :   RequestOrUSVString request;
    1007           0 :   request.SetAsUSVString().Rebind(aURL.Data(), aURL.Length());
    1008           0 :   ErrorResult error;
    1009           0 :   CacheQueryOptions params;
    1010           0 :   RefPtr<Promise> promise = aCache->Match(request, params, error);
    1011           0 :   if (NS_WARN_IF(error.Failed())) {
    1012           0 :     mState = Finished;
    1013           0 :     return error.StealNSResult();
    1014             :   }
    1015             : 
    1016             :   // Retrieve the script from aCache.
    1017           0 :   mState = WaitingForScript;
    1018           0 :   promise->AppendNativeHandler(this);
    1019           0 :   return NS_OK;
    1020             : }
    1021             : 
    1022             : void
    1023           0 : CompareCache::Finish(nsresult aStatus, bool aInCache)
    1024             : {
    1025           0 :   if (mState != Finished) {
    1026           0 :     mState = Finished;
    1027           0 :     mInCache = aInCache;
    1028           0 :     mCN->CacheFinish(aStatus);
    1029             :   }
    1030           0 : }
    1031             : 
    1032             : void
    1033           0 : CompareCache::Abort()
    1034             : {
    1035           0 :   AssertIsOnMainThread();
    1036             : 
    1037           0 :   if (mState != Finished) {
    1038           0 :     mState = Finished;
    1039             : 
    1040           0 :     if (mPump) {
    1041           0 :       mPump->Cancel(NS_BINDING_ABORTED);
    1042           0 :       mPump = nullptr;
    1043             :     }
    1044             :   }
    1045           0 : }
    1046             : 
    1047             : NS_IMETHODIMP
    1048           0 : CompareCache::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
    1049             :                                nsresult aStatus, uint32_t aLen,
    1050             :                                const uint8_t* aString)
    1051             : {
    1052           0 :   AssertIsOnMainThread();
    1053             : 
    1054           0 :   if (mState == Finished) {
    1055           0 :     return aStatus;
    1056             :   }
    1057             : 
    1058           0 :   if (NS_WARN_IF(NS_FAILED(aStatus))) {
    1059           0 :     Finish(aStatus, false);
    1060           0 :     return aStatus;
    1061             :   }
    1062             : 
    1063           0 :   char16_t* buffer = nullptr;
    1064           0 :   size_t len = 0;
    1065             : 
    1066           0 :   nsresult rv = ScriptLoader::ConvertToUTF16(nullptr, aString, aLen,
    1067           0 :                                              NS_LITERAL_STRING("UTF-8"),
    1068           0 :                                              nullptr, buffer, len);
    1069           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1070           0 :     Finish(rv, false);
    1071           0 :     return rv;
    1072             :   }
    1073             : 
    1074           0 :   mBuffer.Adopt(buffer, len);
    1075             : 
    1076           0 :   Finish(NS_OK, true);
    1077           0 :   return NS_OK;
    1078             : }
    1079             : 
    1080             : void
    1081           0 : CompareCache::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
    1082             : {
    1083           0 :   AssertIsOnMainThread();
    1084             : 
    1085           0 :   switch (mState) {
    1086             :     case Finished:
    1087           0 :       return;
    1088             :     case WaitingForScript:
    1089           0 :       ManageValueResult(aCx, aValue);
    1090           0 :       return;
    1091             :     default:
    1092           0 :       MOZ_CRASH("Unacceptable state.");
    1093             :   }
    1094             : }
    1095             : 
    1096             : void
    1097           0 : CompareCache::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
    1098             : {
    1099           0 :   AssertIsOnMainThread();
    1100             : 
    1101           0 :   if (mState != Finished) {
    1102           0 :     Finish(NS_ERROR_FAILURE, false);
    1103           0 :     return;
    1104             :   }
    1105             : }
    1106             : 
    1107             : void
    1108           0 : CompareCache::ManageValueResult(JSContext* aCx, JS::Handle<JS::Value> aValue)
    1109             : {
    1110           0 :   AssertIsOnMainThread();
    1111             : 
    1112             :   // The cache returns undefined if the object is not stored.
    1113           0 :   if (aValue.isUndefined()) {
    1114           0 :     Finish(NS_OK, false);
    1115           0 :     return;
    1116             :   }
    1117             : 
    1118           0 :   MOZ_ASSERT(aValue.isObject());
    1119             : 
    1120           0 :   JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
    1121           0 :   if (NS_WARN_IF(!obj)) {
    1122           0 :     Finish(NS_ERROR_FAILURE, false);
    1123           0 :     return;
    1124             :   }
    1125             : 
    1126           0 :   Response* response = nullptr;
    1127           0 :   nsresult rv = UNWRAP_OBJECT(Response, &obj, response);
    1128           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1129           0 :     Finish(rv, false);
    1130           0 :     return;
    1131             :   }
    1132             : 
    1133           0 :   MOZ_ASSERT(response->Ok());
    1134             : 
    1135           0 :   nsCOMPtr<nsIInputStream> inputStream;
    1136           0 :   response->GetBody(getter_AddRefs(inputStream));
    1137           0 :   MOZ_ASSERT(inputStream);
    1138             : 
    1139           0 :   MOZ_ASSERT(!mPump);
    1140           0 :   rv = NS_NewInputStreamPump(getter_AddRefs(mPump),
    1141             :                              inputStream,
    1142             :                              -1, /* default streamPos */
    1143             :                              -1, /* default streamLen */
    1144             :                              0, /* default segsize */
    1145             :                              0, /* default segcount */
    1146             :                              false, /* default closeWhenDone */
    1147           0 :                              SystemGroup::EventTargetFor(TaskCategory::Other));
    1148           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1149           0 :     Finish(rv, false);
    1150           0 :     return;
    1151             :   }
    1152             : 
    1153           0 :   nsCOMPtr<nsIStreamLoader> loader;
    1154           0 :   rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
    1155           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1156           0 :     Finish(rv, false);
    1157           0 :     return;
    1158             :   }
    1159             : 
    1160           0 :   rv = mPump->AsyncRead(loader, nullptr);
    1161           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1162           0 :     mPump = nullptr;
    1163           0 :     Finish(rv, false);
    1164           0 :     return;
    1165             :   }
    1166             : 
    1167           0 :   nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(mPump);
    1168           0 :   if (rr) {
    1169             :     nsCOMPtr<nsIEventTarget> sts =
    1170           0 :       do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
    1171           0 :     rv = rr->RetargetDeliveryTo(sts);
    1172           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1173           0 :       mPump = nullptr;
    1174           0 :       Finish(rv, false);
    1175           0 :       return;
    1176             :     }
    1177             :   }
    1178             : }
    1179             : 
    1180             : nsresult
    1181           0 : CompareManager::Initialize(nsIPrincipal* aPrincipal,
    1182             :                            const nsAString& aURL,
    1183             :                            const nsAString& aCacheName,
    1184             :                            nsILoadGroup* aLoadGroup)
    1185             : {
    1186           0 :   AssertIsOnMainThread();
    1187           0 :   MOZ_ASSERT(aPrincipal);
    1188           0 :   MOZ_ASSERT(mPendingCount == 0);
    1189           0 :   MOZ_DIAGNOSTIC_ASSERT(mState == WaitingForInitialization);
    1190             : 
    1191             :   // RAII Cleanup when fails.
    1192           0 :   auto guard = MakeScopeExit([&] { Cleanup(); });
    1193             : 
    1194           0 :   mURL = aURL;
    1195           0 :   mPrincipal = aPrincipal;
    1196           0 :   mLoadGroup = aLoadGroup;
    1197             : 
    1198             :   // Always create a CacheStorage since we want to write the network entry to
    1199             :   // the cache even if there isn't an existing one.
    1200           0 :   AutoJSAPI jsapi;
    1201           0 :   jsapi.Init();
    1202           0 :   ErrorResult result;
    1203           0 :   mSandbox.init(jsapi.cx());
    1204           0 :   mCacheStorage = CreateCacheStorage(jsapi.cx(), aPrincipal, result, &mSandbox);
    1205           0 :   if (NS_WARN_IF(result.Failed())) {
    1206           0 :     MOZ_ASSERT(!result.IsErrorWithMessage());
    1207           0 :     return result.StealNSResult();
    1208             :   }
    1209             : 
    1210             :   // If there is no existing cache, proceed to fetch the script directly.
    1211           0 :   if (aCacheName.IsEmpty()) {
    1212           0 :     mState = WaitingForScriptOrComparisonResult;
    1213           0 :     nsresult rv = FetchScript(aURL, true /* aIsMainScript */);
    1214           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1215           0 :       return rv;
    1216             :     }
    1217             : 
    1218           0 :     guard.release();
    1219           0 :     return NS_OK;
    1220             :   }
    1221             : 
    1222             :   // Open the cache saving the old source scripts.
    1223           0 :   RefPtr<Promise> promise = mCacheStorage->Open(aCacheName, result);
    1224           0 :   if (NS_WARN_IF(result.Failed())) {
    1225           0 :     MOZ_ASSERT(!result.IsErrorWithMessage());
    1226           0 :     return result.StealNSResult();
    1227             :   }
    1228             : 
    1229           0 :   mState = WaitingForExistingOpen;
    1230           0 :   promise->AppendNativeHandler(this);
    1231             : 
    1232           0 :   guard.release();
    1233           0 :   return NS_OK;
    1234             : }
    1235             : 
    1236             : // This class manages 4 promises if needed:
    1237             : // 1. Retrieve the Cache object by a given CacheName of OldCache.
    1238             : // 2. Retrieve the URLs saved in OldCache.
    1239             : // 3. Retrieve the Cache object of the NewCache for the newly created SW.
    1240             : // 4. Put the value in the cache.
    1241             : // For this reason we have mState to know what callback we are handling.
    1242             : void
    1243           0 : CompareManager::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
    1244             : {
    1245           0 :   AssertIsOnMainThread();
    1246           0 :   MOZ_ASSERT(mCallback);
    1247             : 
    1248           0 :   switch (mState) {
    1249             :     case Finished:
    1250           0 :       return;
    1251             :     case WaitingForExistingOpen:
    1252           0 :       ManageOldCache(aCx, aValue);
    1253           0 :       return;
    1254             :     case WaitingForExistingKeys:
    1255           0 :       ManageOldKeys(aCx, aValue);
    1256           0 :       return;
    1257             :     case WaitingForOpen:
    1258           0 :       ManageNewCache(aCx, aValue);
    1259           0 :       return;
    1260             :     case WaitingForPut:
    1261           0 :       MOZ_DIAGNOSTIC_ASSERT(mPendingCount > 0);
    1262           0 :       if (--mPendingCount == 0) {
    1263           0 :         mCallback->ComparisonResult(NS_OK,
    1264             :                                     false /* aIsEqual */,
    1265             :                                     mNewCacheName,
    1266             :                                     mMaxScope,
    1267           0 :                                     mLoadFlags);
    1268           0 :         Cleanup();
    1269             :       }
    1270           0 :       return;
    1271             :     default:
    1272           0 :       MOZ_DIAGNOSTIC_ASSERT(false);
    1273             :   }
    1274             : }
    1275             : 
    1276             : void
    1277           0 : CompareManager::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
    1278             : {
    1279           0 :   AssertIsOnMainThread();
    1280           0 :   switch (mState) {
    1281             :     case Finished:
    1282           0 :       return;
    1283             :     case WaitingForExistingOpen:
    1284           0 :       NS_WARNING("Could not open the existing cache.");
    1285           0 :       break;
    1286             :     case WaitingForExistingKeys:
    1287           0 :       NS_WARNING("Could not get the existing URLs.");
    1288           0 :       break;
    1289             :     case WaitingForOpen:
    1290           0 :       NS_WARNING("Could not open cache.");
    1291           0 :       break;
    1292             :     case WaitingForPut:
    1293           0 :       NS_WARNING("Could not write to cache.");
    1294           0 :       break;
    1295             :     default:
    1296           0 :       MOZ_DIAGNOSTIC_ASSERT(false);
    1297             :   }
    1298             : 
    1299           0 :   Fail(NS_ERROR_FAILURE);
    1300             : }
    1301             : 
    1302             : void
    1303           0 : CompareManager::Fail(nsresult aStatus)
    1304             : {
    1305           0 :   AssertIsOnMainThread();
    1306           0 :   mCallback->ComparisonResult(aStatus, false /* aIsEqual */,
    1307           0 :                               EmptyString(), EmptyCString(), mLoadFlags);
    1308           0 :   Cleanup();
    1309           0 : }
    1310             : 
    1311             : void
    1312           0 : CompareManager::Cleanup()
    1313             : {
    1314           0 :   AssertIsOnMainThread();
    1315             : 
    1316           0 :   if (mState != Finished) {
    1317           0 :     mState = Finished;
    1318             : 
    1319           0 :     MOZ_ASSERT(mCallback);
    1320           0 :     mCallback = nullptr;
    1321             : 
    1322             :     // Abort and release CompareNetworks.
    1323           0 :     for (uint32_t i = 0; i < mCNList.Length(); ++i) {
    1324           0 :       mCNList[i]->Abort();
    1325             :     }
    1326           0 :     mCNList.Clear();
    1327             :   }
    1328           0 : }
    1329             : 
    1330             : } // namespace
    1331             : 
    1332             : nsresult
    1333           0 : PurgeCache(nsIPrincipal* aPrincipal, const nsAString& aCacheName)
    1334             : {
    1335           0 :   AssertIsOnMainThread();
    1336           0 :   MOZ_ASSERT(aPrincipal);
    1337             : 
    1338           0 :   if (aCacheName.IsEmpty()) {
    1339           0 :     return NS_OK;
    1340             :   }
    1341             : 
    1342           0 :   AutoJSAPI jsapi;
    1343           0 :   jsapi.Init();
    1344           0 :   ErrorResult rv;
    1345           0 :   JS::Rooted<JSObject*> sandboxObject(jsapi.cx());
    1346           0 :   RefPtr<CacheStorage> cacheStorage = CreateCacheStorage(jsapi.cx(), aPrincipal, rv, &sandboxObject);
    1347           0 :   if (NS_WARN_IF(rv.Failed())) {
    1348           0 :     return rv.StealNSResult();
    1349             :   }
    1350             : 
    1351             :   // We use the ServiceWorker scope as key for the cacheStorage.
    1352             :   RefPtr<Promise> promise =
    1353           0 :     cacheStorage->Delete(aCacheName, rv);
    1354           0 :   if (NS_WARN_IF(rv.Failed())) {
    1355           0 :     return rv.StealNSResult();
    1356             :   }
    1357             : 
    1358             :   // We don't actually care about the result of the delete operation.
    1359           0 :   return NS_OK;
    1360             : }
    1361             : 
    1362             : nsresult
    1363           0 : GenerateCacheName(nsAString& aName)
    1364             : {
    1365             :   nsresult rv;
    1366             :   nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
    1367           0 :     do_GetService("@mozilla.org/uuid-generator;1", &rv);
    1368           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1369           0 :     return rv;
    1370             :   }
    1371             : 
    1372             :   nsID id;
    1373           0 :   rv = uuidGenerator->GenerateUUIDInPlace(&id);
    1374           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1375           0 :     return rv;
    1376             :   }
    1377             : 
    1378             :   char chars[NSID_LENGTH];
    1379           0 :   id.ToProvidedString(chars);
    1380             : 
    1381             :   // NSID_LENGTH counts the null terminator.
    1382           0 :   aName.AssignASCII(chars, NSID_LENGTH - 1);
    1383             : 
    1384           0 :   return NS_OK;
    1385             : }
    1386             : 
    1387             : nsresult
    1388           0 : Compare(ServiceWorkerRegistrationInfo* aRegistration,
    1389             :         nsIPrincipal* aPrincipal, const nsAString& aCacheName,
    1390             :         const nsAString& aURL, CompareCallback* aCallback,
    1391             :         nsILoadGroup* aLoadGroup)
    1392             : {
    1393           0 :   AssertIsOnMainThread();
    1394           0 :   MOZ_ASSERT(aRegistration);
    1395           0 :   MOZ_ASSERT(aPrincipal);
    1396           0 :   MOZ_ASSERT(!aURL.IsEmpty());
    1397           0 :   MOZ_ASSERT(aCallback);
    1398             : 
    1399           0 :   RefPtr<CompareManager> cm = new CompareManager(aRegistration, aCallback);
    1400             : 
    1401           0 :   nsresult rv = cm->Initialize(aPrincipal, aURL, aCacheName, aLoadGroup);
    1402           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1403           0 :     return rv;
    1404             :   }
    1405             : 
    1406           0 :   return NS_OK;
    1407             : }
    1408             : 
    1409             : } // namespace serviceWorkerScriptCache
    1410             : 
    1411             : END_WORKERS_NAMESPACE

Generated by: LCOV version 1.13