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
|