Source code

Revision control

Other Tools

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