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 "ServiceWorkerManager.h"
8
9
#include "nsAutoPtr.h"
10
#include "nsIEffectiveTLDService.h"
11
#include "nsIHttpChannel.h"
12
#include "nsIHttpChannelInternal.h"
13
#include "nsINamed.h"
14
#include "nsINetworkInterceptController.h"
15
#include "nsIMutableArray.h"
16
#include "nsITimer.h"
17
#include "nsIUploadChannel2.h"
18
#include "nsServiceManagerUtils.h"
19
#include "nsDebug.h"
20
#include "nsIPermissionManager.h"
21
#include "nsXULAppAPI.h"
22
23
#include "jsapi.h"
24
25
#include "mozilla/BasePrincipal.h"
26
#include "mozilla/ClearOnShutdown.h"
27
#include "mozilla/ErrorNames.h"
28
#include "mozilla/LoadContext.h"
29
#include "mozilla/SystemGroup.h"
30
#include "mozilla/Telemetry.h"
31
#include "mozilla/dom/BindingUtils.h"
32
#include "mozilla/dom/ClientHandle.h"
33
#include "mozilla/dom/ClientManager.h"
34
#include "mozilla/dom/ClientSource.h"
35
#include "mozilla/dom/ConsoleUtils.h"
36
#include "mozilla/dom/ContentParent.h"
37
#include "mozilla/dom/ErrorEvent.h"
38
#include "mozilla/dom/Headers.h"
39
#include "mozilla/dom/InternalHeaders.h"
40
#include "mozilla/dom/Navigator.h"
41
#include "mozilla/dom/NotificationEvent.h"
42
#include "mozilla/dom/PromiseNativeHandler.h"
43
#include "mozilla/dom/Request.h"
44
#include "mozilla/dom/RootedDictionary.h"
45
#include "mozilla/dom/TypedArray.h"
46
#include "mozilla/dom/SharedWorker.h"
47
#include "mozilla/dom/WorkerPrivate.h"
48
#include "mozilla/dom/WorkerRunnable.h"
49
#include "mozilla/dom/WorkerScope.h"
50
#include "mozilla/ipc/BackgroundChild.h"
51
#include "mozilla/ipc/PBackgroundChild.h"
52
#include "mozilla/ipc/PBackgroundSharedTypes.h"
53
#include "mozilla/dom/ScriptLoader.h"
54
#include "mozilla/Unused.h"
55
#include "mozilla/EnumSet.h"
56
57
#include "nsContentUtils.h"
58
#include "nsNetUtil.h"
59
#include "nsPermissionManager.h"
60
#include "nsProxyRelease.h"
61
#include "nsQueryObject.h"
62
#include "nsTArray.h"
63
64
#include "ServiceWorker.h"
65
#include "ServiceWorkerContainer.h"
66
#include "ServiceWorkerInfo.h"
67
#include "ServiceWorkerJobQueue.h"
68
#include "ServiceWorkerManagerChild.h"
69
#include "ServiceWorkerPrivate.h"
70
#include "ServiceWorkerRegisterJob.h"
71
#include "ServiceWorkerRegistrar.h"
72
#include "ServiceWorkerRegistration.h"
73
#include "ServiceWorkerScriptCache.h"
74
#include "ServiceWorkerShutdownBlocker.h"
75
#include "ServiceWorkerEvents.h"
76
#include "ServiceWorkerUnregisterJob.h"
77
#include "ServiceWorkerUpdateJob.h"
78
#include "ServiceWorkerUpdaterChild.h"
79
#include "ServiceWorkerUtils.h"
80
81
#ifdef PostMessage
82
# undef PostMessage
83
#endif
84
85
using namespace mozilla;
86
using namespace mozilla::dom;
87
using namespace mozilla::ipc;
88
89
namespace mozilla {
90
namespace dom {
91
92
#define CLEAR_ORIGIN_DATA "clear-origin-attributes-data"
93
94
static_assert(
95
nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN ==
96
static_cast<uint32_t>(RequestMode::Same_origin),
97
"RequestMode enumeration value should match Necko CORS mode value.");
98
static_assert(
99
nsIHttpChannelInternal::CORS_MODE_NO_CORS ==
100
static_cast<uint32_t>(RequestMode::No_cors),
101
"RequestMode enumeration value should match Necko CORS mode value.");
102
static_assert(
103
nsIHttpChannelInternal::CORS_MODE_CORS ==
104
static_cast<uint32_t>(RequestMode::Cors),
105
"RequestMode enumeration value should match Necko CORS mode value.");
106
static_assert(
107
nsIHttpChannelInternal::CORS_MODE_NAVIGATE ==
108
static_cast<uint32_t>(RequestMode::Navigate),
109
"RequestMode enumeration value should match Necko CORS mode value.");
110
111
static_assert(
112
nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW ==
113
static_cast<uint32_t>(RequestRedirect::Follow),
114
"RequestRedirect enumeration value should make Necko Redirect mode value.");
115
static_assert(
116
nsIHttpChannelInternal::REDIRECT_MODE_ERROR ==
117
static_cast<uint32_t>(RequestRedirect::Error),
118
"RequestRedirect enumeration value should make Necko Redirect mode value.");
119
static_assert(
120
nsIHttpChannelInternal::REDIRECT_MODE_MANUAL ==
121
static_cast<uint32_t>(RequestRedirect::Manual),
122
"RequestRedirect enumeration value should make Necko Redirect mode value.");
123
static_assert(
124
3 == RequestRedirectValues::Count,
125
"RequestRedirect enumeration value should make Necko Redirect mode value.");
126
127
static_assert(
128
nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT ==
129
static_cast<uint32_t>(RequestCache::Default),
130
"RequestCache enumeration value should match Necko Cache mode value.");
131
static_assert(
132
nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE ==
133
static_cast<uint32_t>(RequestCache::No_store),
134
"RequestCache enumeration value should match Necko Cache mode value.");
135
static_assert(
136
nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD ==
137
static_cast<uint32_t>(RequestCache::Reload),
138
"RequestCache enumeration value should match Necko Cache mode value.");
139
static_assert(
140
nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE ==
141
static_cast<uint32_t>(RequestCache::No_cache),
142
"RequestCache enumeration value should match Necko Cache mode value.");
143
static_assert(
144
nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE ==
145
static_cast<uint32_t>(RequestCache::Force_cache),
146
"RequestCache enumeration value should match Necko Cache mode value.");
147
static_assert(
148
nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED ==
149
static_cast<uint32_t>(RequestCache::Only_if_cached),
150
"RequestCache enumeration value should match Necko Cache mode value.");
151
static_assert(
152
6 == RequestCacheValues::Count,
153
"RequestCache enumeration value should match Necko Cache mode value.");
154
155
static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::Imports) ==
156
nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS,
157
"nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*"
158
" should match ServiceWorkerUpdateViaCache enumeration.");
159
static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::All) ==
160
nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL,
161
"nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*"
162
" should match ServiceWorkerUpdateViaCache enumeration.");
163
static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::None) ==
164
nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE,
165
"nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*"
166
" should match ServiceWorkerUpdateViaCache enumeration.");
167
168
static StaticRefPtr<ServiceWorkerManager> gInstance;
169
170
struct ServiceWorkerManager::RegistrationDataPerPrincipal final {
171
// Ordered list of scopes for glob matching.
172
// Each entry is an absolute URL representing the scope.
173
// Each value of the hash table is an array of an absolute URLs representing
174
// the scopes.
175
//
176
// An array is used for now since the number of controlled scopes per
177
// domain is expected to be relatively low. If that assumption was proved
178
// wrong this should be replaced with a better structure to avoid the
179
// memmoves associated with inserting stuff in the middle of the array.
180
nsTArray<nsCString> mOrderedScopes;
181
182
// Scope to registration.
183
// The scope should be a fully qualified valid URL.
184
nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mInfos;
185
186
// Maps scopes to job queues.
187
nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues;
188
189
// Map scopes to scheduled update timers.
190
nsInterfaceHashtable<nsCStringHashKey, nsITimer> mUpdateTimers;
191
};
192
193
namespace {
194
195
nsresult PopulateRegistrationData(
196
nsIPrincipal* aPrincipal,
197
const ServiceWorkerRegistrationInfo* aRegistration,
198
ServiceWorkerRegistrationData& aData) {
199
MOZ_ASSERT(aPrincipal);
200
MOZ_ASSERT(aRegistration);
201
202
if (NS_WARN_IF(!BasePrincipal::Cast(aPrincipal)->IsContentPrincipal())) {
203
return NS_ERROR_FAILURE;
204
}
205
206
nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aData.principal());
207
if (NS_WARN_IF(NS_FAILED(rv))) {
208
return rv;
209
}
210
211
aData.scope() = aRegistration->Scope();
212
213
// TODO: When bug 1426401 is implemented we will need to handle more
214
// than just the active worker here.
215
RefPtr<ServiceWorkerInfo> active = aRegistration->GetActive();
216
MOZ_ASSERT(active);
217
if (NS_WARN_IF(!active)) {
218
return NS_ERROR_FAILURE;
219
}
220
221
aData.currentWorkerURL() = active->ScriptSpec();
222
aData.cacheName() = active->CacheName();
223
aData.currentWorkerHandlesFetch() = active->HandlesFetch();
224
225
aData.currentWorkerInstalledTime() = active->GetInstalledTime();
226
aData.currentWorkerActivatedTime() = active->GetActivatedTime();
227
228
aData.updateViaCache() =
229
static_cast<uint32_t>(aRegistration->GetUpdateViaCache());
230
231
aData.lastUpdateTime() = aRegistration->GetLastUpdateTime();
232
233
MOZ_ASSERT(ServiceWorkerRegistrationDataIsValid(aData));
234
235
return NS_OK;
236
}
237
238
class TeardownRunnable final : public Runnable {
239
public:
240
explicit TeardownRunnable(ServiceWorkerManagerChild* aActor)
241
: Runnable("dom::ServiceWorkerManager::TeardownRunnable"),
242
mActor(aActor) {
243
MOZ_ASSERT(mActor);
244
}
245
246
NS_IMETHOD Run() override {
247
MOZ_ASSERT(mActor);
248
mActor->SendShutdown();
249
return NS_OK;
250
}
251
252
private:
253
~TeardownRunnable() {}
254
255
RefPtr<ServiceWorkerManagerChild> mActor;
256
};
257
258
bool ServiceWorkersAreCrossProcess() {
259
return ServiceWorkerParentInterceptEnabled() && XRE_IsE10sParentProcess();
260
}
261
262
const char* GetXPCOMShutdownTopic() {
263
if (ServiceWorkersAreCrossProcess()) {
264
return "profile-change-teardown";
265
}
266
267
return NS_XPCOM_SHUTDOWN_OBSERVER_ID;
268
}
269
270
already_AddRefed<nsIAsyncShutdownClient> GetAsyncShutdownBarrier() {
271
AssertIsOnMainThread();
272
273
if (!ServiceWorkersAreCrossProcess()) {
274
return nullptr;
275
}
276
277
nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
278
MOZ_ASSERT(svc);
279
280
nsCOMPtr<nsIAsyncShutdownClient> barrier;
281
DebugOnly<nsresult> rv =
282
svc->GetProfileChangeTeardown(getter_AddRefs(barrier));
283
MOZ_ASSERT(NS_SUCCEEDED(rv));
284
285
return barrier.forget();
286
}
287
288
} // namespace
289
290
//////////////////////////
291
// ServiceWorkerManager //
292
//////////////////////////
293
294
NS_IMPL_ADDREF(ServiceWorkerManager)
295
NS_IMPL_RELEASE(ServiceWorkerManager)
296
297
NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
298
NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager)
299
NS_INTERFACE_MAP_ENTRY(nsIObserver)
300
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager)
301
NS_INTERFACE_MAP_END
302
303
ServiceWorkerManager::ServiceWorkerManager()
304
: mActor(nullptr), mShuttingDown(false) {}
305
306
ServiceWorkerManager::~ServiceWorkerManager() {
307
// The map will assert if it is not empty when destroyed.
308
mRegistrationInfos.Clear();
309
310
if (!ServiceWorkersAreCrossProcess()) {
311
MOZ_ASSERT(!mActor);
312
}
313
314
// This can happen if the browser is started up in ProfileManager mode, in
315
// which case XPCOM will startup and shutdown, but there won't be any
316
// profile-* topic notifications. The shutdown blocker expects to be in a
317
// NotAcceptingPromises state when it's destroyed, and this transition
318
// normally happens in the "profile-change-teardown" notification callback
319
// (which won't be called in ProfileManager mode).
320
if (!mShuttingDown && mShutdownBlocker) {
321
mShutdownBlocker->StopAcceptingPromises();
322
}
323
}
324
325
void ServiceWorkerManager::BlockShutdownOn(
326
GenericNonExclusivePromise* aPromise) {
327
AssertIsOnMainThread();
328
329
// This may be called when in non-e10s mode with parent-intercept enabled.
330
if (!ServiceWorkersAreCrossProcess()) {
331
return;
332
}
333
334
MOZ_ASSERT(mShutdownBlocker);
335
MOZ_ASSERT(aPromise);
336
337
mShutdownBlocker->WaitOnPromise(aPromise);
338
}
339
340
void ServiceWorkerManager::Init(ServiceWorkerRegistrar* aRegistrar) {
341
nsCOMPtr<nsIAsyncShutdownClient> shutdownBarrier = GetAsyncShutdownBarrier();
342
343
if (shutdownBarrier) {
344
mShutdownBlocker =
345
ServiceWorkerShutdownBlocker::CreateAndRegisterOn(shutdownBarrier);
346
MOZ_ASSERT(mShutdownBlocker);
347
}
348
349
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
350
if (obs) {
351
DebugOnly<nsresult> rv;
352
rv = obs->AddObserver(this, GetXPCOMShutdownTopic(), false /* ownsWeak */);
353
MOZ_ASSERT(NS_SUCCEEDED(rv));
354
}
355
356
if (XRE_IsParentProcess()) {
357
MOZ_DIAGNOSTIC_ASSERT(aRegistrar);
358
359
nsTArray<ServiceWorkerRegistrationData> data;
360
aRegistrar->GetRegistrations(data);
361
LoadRegistrations(data);
362
363
if (obs) {
364
DebugOnly<nsresult> rv;
365
rv = obs->AddObserver(this, CLEAR_ORIGIN_DATA, false /* ownsWeak */);
366
MOZ_ASSERT(NS_SUCCEEDED(rv));
367
}
368
}
369
370
PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
371
if (NS_WARN_IF(!actorChild)) {
372
MaybeStartShutdown();
373
return;
374
}
375
376
PServiceWorkerManagerChild* actor =
377
actorChild->SendPServiceWorkerManagerConstructor();
378
if (!actor) {
379
MaybeStartShutdown();
380
return;
381
}
382
383
mActor = static_cast<ServiceWorkerManagerChild*>(actor);
384
}
385
386
RefPtr<GenericPromise> ServiceWorkerManager::StartControllingClient(
387
const ClientInfo& aClientInfo,
388
ServiceWorkerRegistrationInfo* aRegistrationInfo,
389
bool aControlClientHandle) {
390
MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive());
391
392
RefPtr<GenericPromise> promise;
393
RefPtr<ServiceWorkerManager> self(this);
394
395
const ServiceWorkerDescriptor& active =
396
aRegistrationInfo->GetActive()->Descriptor();
397
398
auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
399
if (entry) {
400
RefPtr<ServiceWorkerRegistrationInfo> old =
401
entry.Data()->mRegistrationInfo.forget();
402
403
if (aControlClientHandle) {
404
promise = entry.Data()->mClientHandle->Control(active);
405
} else {
406
promise = GenericPromise::CreateAndResolve(false, __func__);
407
}
408
409
entry.Data()->mRegistrationInfo = aRegistrationInfo;
410
411
if (old != aRegistrationInfo) {
412
StopControllingRegistration(old);
413
aRegistrationInfo->StartControllingClient();
414
}
415
416
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
417
418
// Always check to see if we failed to actually control the client. In
419
// that case removed the client from our list of controlled clients.
420
return promise->Then(
421
SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
422
[](bool) {
423
// do nothing on success
424
return GenericPromise::CreateAndResolve(true, __func__);
425
},
426
[self, aClientInfo](nsresult aRv) {
427
// failed to control, forget about this client
428
self->StopControllingClient(aClientInfo);
429
return GenericPromise::CreateAndReject(aRv, __func__);
430
});
431
}
432
433
RefPtr<ClientHandle> clientHandle = ClientManager::CreateHandle(
434
aClientInfo, SystemGroup::EventTargetFor(TaskCategory::Other));
435
436
if (aControlClientHandle) {
437
promise = clientHandle->Control(active);
438
} else {
439
promise = GenericPromise::CreateAndResolve(false, __func__);
440
}
441
442
aRegistrationInfo->StartControllingClient();
443
444
entry.OrInsert([&] {
445
return new ControlledClientData(clientHandle, aRegistrationInfo);
446
});
447
448
clientHandle->OnDetach()->Then(
449
SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
450
[self, aClientInfo] { self->StopControllingClient(aClientInfo); });
451
452
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
453
454
// Always check to see if we failed to actually control the client. In
455
// that case removed the client from our list of controlled clients.
456
return promise->Then(
457
SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
458
[](bool) {
459
// do nothing on success
460
return GenericPromise::CreateAndResolve(true, __func__);
461
},
462
[self, aClientInfo](nsresult aRv) {
463
// failed to control, forget about this client
464
self->StopControllingClient(aClientInfo);
465
return GenericPromise::CreateAndReject(aRv, __func__);
466
});
467
}
468
469
void ServiceWorkerManager::StopControllingClient(
470
const ClientInfo& aClientInfo) {
471
auto entry = mControlledClients.Lookup(aClientInfo.Id());
472
if (!entry) {
473
return;
474
}
475
476
RefPtr<ServiceWorkerRegistrationInfo> reg =
477
entry.Data()->mRegistrationInfo.forget();
478
479
entry.Remove();
480
481
StopControllingRegistration(reg);
482
}
483
484
void ServiceWorkerManager::MaybeStartShutdown() {
485
MOZ_ASSERT(NS_IsMainThread());
486
487
if (mShuttingDown) {
488
return;
489
}
490
491
mShuttingDown = true;
492
493
for (auto& entry : mRegistrationInfos) {
494
auto& dataPtr = entry.GetData();
495
496
for (auto& timerEntry : dataPtr->mUpdateTimers) {
497
timerEntry.GetData()->Cancel();
498
}
499
dataPtr->mUpdateTimers.Clear();
500
501
for (auto& queueEntry : dataPtr->mJobQueues) {
502
queueEntry.GetData()->CancelAll();
503
}
504
dataPtr->mJobQueues.Clear();
505
506
for (auto& registrationEntry : dataPtr->mInfos) {
507
registrationEntry.GetData()->ShutdownWorkers();
508
}
509
dataPtr->mInfos.Clear();
510
}
511
512
for (auto& entry : mControlledClients) {
513
entry.GetData()->mRegistrationInfo->ShutdownWorkers();
514
}
515
516
for (auto iter = mOrphanedRegistrations.iter(); !iter.done(); iter.next()) {
517
iter.get()->ShutdownWorkers();
518
}
519
520
if (mShutdownBlocker) {
521
mShutdownBlocker->StopAcceptingPromises();
522
}
523
524
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
525
if (obs) {
526
obs->RemoveObserver(this, GetXPCOMShutdownTopic());
527
528
if (XRE_IsParentProcess()) {
529
obs->RemoveObserver(this, CLEAR_ORIGIN_DATA);
530
}
531
}
532
533
if (!mActor) {
534
return;
535
}
536
537
mActor->ManagerShuttingDown();
538
539
RefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
540
nsresult rv = NS_DispatchToMainThread(runnable);
541
Unused << NS_WARN_IF(NS_FAILED(rv));
542
mActor = nullptr;
543
}
544
545
class ServiceWorkerResolveWindowPromiseOnRegisterCallback final
546
: public ServiceWorkerJob::Callback {
547
RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
548
549
~ServiceWorkerResolveWindowPromiseOnRegisterCallback() {}
550
551
virtual void JobFinished(ServiceWorkerJob* aJob,
552
ErrorResult& aStatus) override {
553
MOZ_ASSERT(NS_IsMainThread());
554
MOZ_ASSERT(aJob);
555
556
if (aStatus.Failed()) {
557
mPromise->Reject(std::move(aStatus), __func__);
558
return;
559
}
560
561
MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Register);
562
RefPtr<ServiceWorkerRegisterJob> registerJob =
563
static_cast<ServiceWorkerRegisterJob*>(aJob);
564
RefPtr<ServiceWorkerRegistrationInfo> reg = registerJob->GetRegistration();
565
566
mPromise->Resolve(reg->Descriptor(), __func__);
567
}
568
569
public:
570
ServiceWorkerResolveWindowPromiseOnRegisterCallback()
571
: mPromise(new ServiceWorkerRegistrationPromise::Private(__func__)) {}
572
573
RefPtr<ServiceWorkerRegistrationPromise> Promise() const { return mPromise; }
574
575
NS_INLINE_DECL_REFCOUNTING(
576
ServiceWorkerResolveWindowPromiseOnRegisterCallback, override)
577
};
578
579
namespace {
580
581
class PropagateSoftUpdateRunnable final : public Runnable {
582
public:
583
PropagateSoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
584
const nsAString& aScope)
585
: Runnable("dom::ServiceWorkerManager::PropagateSoftUpdateRunnable"),
586
mOriginAttributes(aOriginAttributes),
587
mScope(aScope) {}
588
589
NS_IMETHOD Run() override {
590
MOZ_ASSERT(NS_IsMainThread());
591
592
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
593
if (swm) {
594
swm->PropagateSoftUpdate(mOriginAttributes, mScope);
595
}
596
597
return NS_OK;
598
}
599
600
private:
601
~PropagateSoftUpdateRunnable() {}
602
603
const OriginAttributes mOriginAttributes;
604
const nsString mScope;
605
};
606
607
class PromiseResolverCallback final : public ServiceWorkerUpdateFinishCallback {
608
public:
609
PromiseResolverCallback(ServiceWorkerUpdateFinishCallback* aCallback,
610
GenericPromise::Private* aPromise)
611
: mCallback(aCallback), mPromise(aPromise) {
612
MOZ_DIAGNOSTIC_ASSERT(mPromise);
613
}
614
615
void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override {
616
MOZ_DIAGNOSTIC_ASSERT(mPromise);
617
618
if (mCallback) {
619
mCallback->UpdateSucceeded(aInfo);
620
}
621
622
MaybeResolve();
623
}
624
625
void UpdateFailed(ErrorResult& aStatus) override {
626
MOZ_DIAGNOSTIC_ASSERT(mPromise);
627
628
if (mCallback) {
629
mCallback->UpdateFailed(aStatus);
630
}
631
632
MaybeResolve();
633
}
634
635
private:
636
~PromiseResolverCallback() { MaybeResolve(); }
637
638
void MaybeResolve() {
639
if (mPromise) {
640
mPromise->Resolve(true, __func__);
641
mPromise = nullptr;
642
}
643
}
644
645
RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
646
RefPtr<GenericPromise::Private> mPromise;
647
};
648
649
// This runnable is used for 2 different tasks:
650
// - to postpone the SoftUpdate() until the IPC SWM actor is created
651
// (aInternalMethod == false)
652
// - to call the 'real' SoftUpdate when the ServiceWorkerUpdaterChild is
653
// notified by the parent (aInternalMethod == true)
654
class SoftUpdateRunnable final : public CancelableRunnable {
655
public:
656
SoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
657
const nsACString& aScope, bool aInternalMethod,
658
GenericPromise::Private* aPromise)
659
: CancelableRunnable("dom::ServiceWorkerManager::SoftUpdateRunnable"),
660
mAttrs(aOriginAttributes),
661
mScope(aScope),
662
mInternalMethod(aInternalMethod),
663
mPromise(aPromise) {}
664
665
NS_IMETHOD Run() override {
666
MOZ_ASSERT(NS_IsMainThread());
667
668
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
669
if (!swm) {
670
return NS_ERROR_FAILURE;
671
}
672
673
if (mInternalMethod) {
674
RefPtr<PromiseResolverCallback> callback =
675
new PromiseResolverCallback(nullptr, mPromise);
676
mPromise = nullptr;
677
678
swm->SoftUpdateInternal(mAttrs, mScope, callback);
679
} else {
680
swm->SoftUpdate(mAttrs, mScope);
681
}
682
683
return NS_OK;
684
}
685
686
nsresult Cancel() override {
687
mPromise = nullptr;
688
return NS_OK;
689
}
690
691
private:
692
~SoftUpdateRunnable() {
693
if (mPromise) {
694
mPromise->Resolve(true, __func__);
695
}
696
}
697
698
const OriginAttributes mAttrs;
699
const nsCString mScope;
700
bool mInternalMethod;
701
702
RefPtr<GenericPromise::Private> mPromise;
703
};
704
705
// This runnable is used for 3 different tasks:
706
// - to postpone the Update() until the IPC SWM actor is created
707
// (aType == ePostpone)
708
// - to call the 'real' Update when the ServiceWorkerUpdaterChild is
709
// notified by the parent (aType == eSuccess)
710
// - an error must be propagated (aType == eFailure)
711
class UpdateRunnable final : public CancelableRunnable {
712
public:
713
enum Type {
714
ePostpone,
715
eSuccess,
716
eFailure,
717
};
718
719
UpdateRunnable(nsIPrincipal* aPrincipal, const nsACString& aScope,
720
ServiceWorkerUpdateFinishCallback* aCallback, Type aType,
721
GenericPromise::Private* aPromise)
722
: CancelableRunnable("dom::ServiceWorkerManager::UpdateRunnable"),
723
mPrincipal(aPrincipal),
724
mScope(aScope),
725
mCallback(aCallback),
726
mType(aType),
727
mPromise(aPromise) {}
728
729
NS_IMETHOD Run() override {
730
MOZ_ASSERT(NS_IsMainThread());
731
732
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
733
if (!swm) {
734
return NS_ERROR_FAILURE;
735
}
736
737
if (mType == ePostpone) {
738
swm->Update(mPrincipal, mScope, mCallback);
739
return NS_OK;
740
}
741
742
MOZ_ASSERT(mPromise);
743
744
RefPtr<PromiseResolverCallback> callback =
745
new PromiseResolverCallback(mCallback, mPromise);
746
mPromise = nullptr;
747
748
if (mType == eSuccess) {
749
swm->UpdateInternal(mPrincipal, mScope, callback);
750
return NS_OK;
751
}
752
753
ErrorResult error(NS_ERROR_DOM_ABORT_ERR);
754
callback->UpdateFailed(error);
755
return NS_OK;
756
}
757
758
nsresult Cancel() override {
759
mPromise = nullptr;
760
return NS_OK;
761
}
762
763
private:
764
~UpdateRunnable() {
765
if (mPromise) {
766
mPromise->Resolve(true, __func__);
767
}
768
}
769
770
nsCOMPtr<nsIPrincipal> mPrincipal;
771
const nsCString mScope;
772
RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
773
Type mType;
774
775
RefPtr<GenericPromise::Private> mPromise;
776
};
777
778
class ResolvePromiseRunnable final : public CancelableRunnable {
779
public:
780
explicit ResolvePromiseRunnable(GenericPromise::Private* aPromise)
781
: CancelableRunnable("dom::ServiceWorkerManager::ResolvePromiseRunnable"),
782
mPromise(aPromise) {}
783
784
NS_IMETHOD
785
Run() override {
786
MaybeResolve();
787
return NS_OK;
788
}
789
790
nsresult Cancel() override {
791
mPromise = nullptr;
792
return NS_OK;
793
}
794
795
private:
796
~ResolvePromiseRunnable() { MaybeResolve(); }
797
798
void MaybeResolve() {
799
if (mPromise) {
800
mPromise->Resolve(true, __func__);
801
mPromise = nullptr;
802
}
803
}
804
805
RefPtr<GenericPromise::Private> mPromise;
806
};
807
808
} // namespace
809
810
RefPtr<ServiceWorkerRegistrationPromise> ServiceWorkerManager::Register(
811
const ClientInfo& aClientInfo, const nsACString& aScopeURL,
812
const nsACString& aScriptURL, ServiceWorkerUpdateViaCache aUpdateViaCache) {
813
nsCOMPtr<nsIURI> scopeURI;
814
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScopeURL);
815
if (NS_FAILED(rv)) {
816
return ServiceWorkerRegistrationPromise::CreateAndReject(
817
CopyableErrorResult(rv), __func__);
818
}
819
820
nsCOMPtr<nsIURI> scriptURI;
821
rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL);
822
if (NS_FAILED(rv)) {
823
return ServiceWorkerRegistrationPromise::CreateAndReject(
824
CopyableErrorResult(rv), __func__);
825
}
826
827
rv = ServiceWorkerScopeAndScriptAreValid(aClientInfo, scopeURI, scriptURI);
828
if (NS_FAILED(rv)) {
829
return ServiceWorkerRegistrationPromise::CreateAndReject(
830
CopyableErrorResult(rv), __func__);
831
}
832
833
// If the previous validation step passed then we must have a principal.
834
nsCOMPtr<nsIPrincipal> principal = aClientInfo.GetPrincipal();
835
836
nsAutoCString scopeKey;
837
rv = PrincipalToScopeKey(principal, scopeKey);
838
if (NS_WARN_IF(NS_FAILED(rv))) {
839
return ServiceWorkerRegistrationPromise::CreateAndReject(
840
CopyableErrorResult(rv), __func__);
841
}
842
843
RefPtr<ServiceWorkerJobQueue> queue =
844
GetOrCreateJobQueue(scopeKey, aScopeURL);
845
846
RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb =
847
new ServiceWorkerResolveWindowPromiseOnRegisterCallback();
848
849
RefPtr<ServiceWorkerRegisterJob> job = new ServiceWorkerRegisterJob(
850
principal, aScopeURL, aScriptURL,
851
static_cast<ServiceWorkerUpdateViaCache>(aUpdateViaCache));
852
853
job->AppendResultCallback(cb);
854
queue->ScheduleJob(job);
855
856
MOZ_ASSERT(NS_IsMainThread());
857
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REGISTRATIONS, 1);
858
859
return cb->Promise();
860
}
861
862
/*
863
* Implements the async aspects of the getRegistrations algorithm.
864
*/
865
class GetRegistrationsRunnable final : public Runnable {
866
const ClientInfo mClientInfo;
867
RefPtr<ServiceWorkerRegistrationListPromise::Private> mPromise;
868
869
public:
870
explicit GetRegistrationsRunnable(const ClientInfo& aClientInfo)
871
: Runnable("dom::ServiceWorkerManager::GetRegistrationsRunnable"),
872
mClientInfo(aClientInfo),
873
mPromise(new ServiceWorkerRegistrationListPromise::Private(__func__)) {}
874
875
RefPtr<ServiceWorkerRegistrationListPromise> Promise() const {
876
return mPromise;
877
}
878
879
NS_IMETHOD
880
Run() override {
881
auto scopeExit = MakeScopeExit(
882
[&] { mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); });
883
884
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
885
if (!swm) {
886
return NS_OK;
887
}
888
889
nsCOMPtr<nsIPrincipal> principal = mClientInfo.GetPrincipal();
890
if (!principal) {
891
return NS_OK;
892
}
893
894
nsTArray<ServiceWorkerRegistrationDescriptor> array;
895
896
if (NS_WARN_IF(!BasePrincipal::Cast(principal)->IsContentPrincipal())) {
897
return NS_OK;
898
}
899
900
nsAutoCString scopeKey;
901
nsresult rv = swm->PrincipalToScopeKey(principal, scopeKey);
902
if (NS_WARN_IF(NS_FAILED(rv))) {
903
return rv;
904
}
905
906
ServiceWorkerManager::RegistrationDataPerPrincipal* data;
907
if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
908
scopeExit.release();
909
mPromise->Resolve(array, __func__);
910
return NS_OK;
911
}
912
913
for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) {
914
RefPtr<ServiceWorkerRegistrationInfo> info =
915
data->mInfos.GetWeak(data->mOrderedScopes[i]);
916
917
NS_ConvertUTF8toUTF16 scope(data->mOrderedScopes[i]);
918
919
nsCOMPtr<nsIURI> scopeURI;
920
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope);
921
if (NS_WARN_IF(NS_FAILED(rv))) {
922
break;
923
}
924
925
rv = principal->CheckMayLoad(scopeURI, true /* report */,
926
false /* allowIfInheritsPrincipal */);
927
if (NS_WARN_IF(NS_FAILED(rv))) {
928
continue;
929
}
930
931
array.AppendElement(info->Descriptor());
932
}
933
934
scopeExit.release();
935
mPromise->Resolve(array, __func__);
936
937
return NS_OK;
938
}
939
};
940
941
RefPtr<ServiceWorkerRegistrationListPromise>
942
ServiceWorkerManager::GetRegistrations(const ClientInfo& aClientInfo) const {
943
RefPtr<GetRegistrationsRunnable> runnable =
944
new GetRegistrationsRunnable(aClientInfo);
945
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable));
946
return runnable->Promise();
947
;
948
}
949
950
/*
951
* Implements the async aspects of the getRegistration algorithm.
952
*/
953
class GetRegistrationRunnable final : public Runnable {
954
const ClientInfo mClientInfo;
955
RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
956
nsCString mURL;
957
958
public:
959
GetRegistrationRunnable(const ClientInfo& aClientInfo, const nsACString& aURL)
960
: Runnable("dom::ServiceWorkerManager::GetRegistrationRunnable"),
961
mClientInfo(aClientInfo),
962
mPromise(new ServiceWorkerRegistrationPromise::Private(__func__)),
963
mURL(aURL) {}
964
965
RefPtr<ServiceWorkerRegistrationPromise> Promise() const { return mPromise; }
966
967
NS_IMETHOD
968
Run() override {
969
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
970
if (!swm) {
971
mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
972
return NS_OK;
973
}
974
975
nsCOMPtr<nsIPrincipal> principal = mClientInfo.GetPrincipal();
976
if (!principal) {
977
mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
978
return NS_OK;
979
}
980
981
nsCOMPtr<nsIURI> uri;
982
nsresult rv = NS_NewURI(getter_AddRefs(uri), mURL);
983
if (NS_WARN_IF(NS_FAILED(rv))) {
984
mPromise->Reject(rv, __func__);
985
return NS_OK;
986
}
987
988
rv = principal->CheckMayLoad(uri, true /* report */,
989
false /* allowIfInheritsPrinciple */);
990
if (NS_FAILED(rv)) {
991
mPromise->Reject(NS_ERROR_DOM_SECURITY_ERR, __func__);
992
return NS_OK;
993
}
994
995
RefPtr<ServiceWorkerRegistrationInfo> registration =
996
swm->GetServiceWorkerRegistrationInfo(principal, uri);
997
998
if (!registration) {
999
// Reject with NS_OK means "not found".
1000
mPromise->Reject(NS_OK, __func__);
1001
return NS_OK;
1002
}
1003
1004
mPromise->Resolve(registration->Descriptor(), __func__);
1005
1006
return NS_OK;
1007
}
1008
};
1009
1010
RefPtr<ServiceWorkerRegistrationPromise> ServiceWorkerManager::GetRegistration(
1011
const ClientInfo& aClientInfo, const nsACString& aURL) const {
1012
MOZ_ASSERT(NS_IsMainThread());
1013
1014
RefPtr<GetRegistrationRunnable> runnable =
1015
new GetRegistrationRunnable(aClientInfo, aURL);
1016
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable));
1017
1018
return runnable->Promise();
1019
}
1020
1021
NS_IMETHODIMP
1022
ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes,
1023
const nsACString& aScope,
1024
const nsTArray<uint8_t>& aDataBytes,
1025
uint8_t optional_argc) {
1026
if (optional_argc == 1) {
1027
// This does one copy here (while constructing the Maybe) and another when
1028
// we end up copying into the SendPushEventRunnable. We could fix that to
1029
// only do one copy by making things between here and there take
1030
// Maybe<nsTArray<uint8_t>>&&, but then we'd need to copy before we know
1031
// whether we really need to in PushMessageDispatcher::NotifyWorkers. Since
1032
// in practice this only affects JS callers that pass data, and we don't
1033
// have any right now, let's not worry about it.
1034
return SendPushEvent(aOriginAttributes, aScope, EmptyString(),
1035
Some(aDataBytes));
1036
}
1037
MOZ_ASSERT(optional_argc == 0);
1038
return SendPushEvent(aOriginAttributes, aScope, EmptyString(), Nothing());
1039
}
1040
1041
nsresult ServiceWorkerManager::SendPushEvent(
1042
const nsACString& aOriginAttributes, const nsACString& aScope,
1043
const nsAString& aMessageId, const Maybe<nsTArray<uint8_t>>& aData) {
1044
OriginAttributes attrs;
1045
if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
1046
return NS_ERROR_INVALID_ARG;
1047
}
1048
1049
ServiceWorkerInfo* serviceWorker = GetActiveWorkerInfoForScope(attrs, aScope);
1050
if (NS_WARN_IF(!serviceWorker)) {
1051
return NS_ERROR_FAILURE;
1052
}
1053
1054
RefPtr<ServiceWorkerRegistrationInfo> registration =
1055
GetRegistration(serviceWorker->Principal(), aScope);
1056
MOZ_DIAGNOSTIC_ASSERT(registration);
1057
1058
return serviceWorker->WorkerPrivate()->SendPushEvent(aMessageId, aData,
1059
registration);
1060
}
1061
1062
NS_IMETHODIMP
1063
ServiceWorkerManager::SendPushSubscriptionChangeEvent(
1064
const nsACString& aOriginAttributes, const nsACString& aScope) {
1065
OriginAttributes attrs;
1066
if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
1067
return NS_ERROR_INVALID_ARG;
1068
}
1069
1070
ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
1071
if (!info) {
1072
return NS_ERROR_FAILURE;
1073
}
1074
return info->WorkerPrivate()->SendPushSubscriptionChangeEvent();
1075
}
1076
1077
nsresult ServiceWorkerManager::SendNotificationEvent(
1078
const nsAString& aEventName, const nsACString& aOriginSuffix,
1079
const nsACString& aScope, const nsAString& aID, const nsAString& aTitle,
1080
const nsAString& aDir, const nsAString& aLang, const nsAString& aBody,
1081
const nsAString& aTag, const nsAString& aIcon, const nsAString& aData,
1082
const nsAString& aBehavior) {
1083
OriginAttributes attrs;
1084
if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
1085
return NS_ERROR_INVALID_ARG;
1086
}
1087
1088
ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
1089
if (!info) {
1090
return NS_ERROR_FAILURE;
1091
}
1092
1093
ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate();
1094
return workerPrivate->SendNotificationEvent(
1095
aEventName, aID, aTitle, aDir, aLang, aBody, aTag, aIcon, aData,
1096
aBehavior, NS_ConvertUTF8toUTF16(aScope));
1097
}
1098
1099
NS_IMETHODIMP
1100
ServiceWorkerManager::SendNotificationClickEvent(
1101
const nsACString& aOriginSuffix, const nsACString& aScope,
1102
const nsAString& aID, const nsAString& aTitle, const nsAString& aDir,
1103
const nsAString& aLang, const nsAString& aBody, const nsAString& aTag,
1104
const nsAString& aIcon, const nsAString& aData,
1105
const nsAString& aBehavior) {
1106
return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLICK_EVENT_NAME),
1107
aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
1108
aBody, aTag, aIcon, aData, aBehavior);
1109
}
1110
1111
NS_IMETHODIMP
1112
ServiceWorkerManager::SendNotificationCloseEvent(
1113
const nsACString& aOriginSuffix, const nsACString& aScope,
1114
const nsAString& aID, const nsAString& aTitle, const nsAString& aDir,
1115
const nsAString& aLang, const nsAString& aBody, const nsAString& aTag,
1116
const nsAString& aIcon, const nsAString& aData,
1117
const nsAString& aBehavior) {
1118
return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLOSE_EVENT_NAME),
1119
aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
1120
aBody, aTag, aIcon, aData, aBehavior);
1121
}
1122
1123
RefPtr<ServiceWorkerRegistrationPromise> ServiceWorkerManager::WhenReady(
1124
const ClientInfo& aClientInfo) {
1125
AssertIsOnMainThread();
1126
1127
for (auto& prd : mPendingReadyList) {
1128
if (prd->mClientHandle->Info().Id() == aClientInfo.Id() &&
1129
prd->mClientHandle->Info().PrincipalInfo() ==
1130
aClientInfo.PrincipalInfo()) {
1131
return prd->mPromise;
1132
}
1133
}
1134
1135
RefPtr<ServiceWorkerRegistrationInfo> reg =
1136
GetServiceWorkerRegistrationInfo(aClientInfo);
1137
if (reg && reg->GetActive()) {
1138
return ServiceWorkerRegistrationPromise::CreateAndResolve(reg->Descriptor(),
1139
__func__);
1140
}
1141
1142
nsCOMPtr<nsISerialEventTarget> target =
1143
SystemGroup::EventTargetFor(TaskCategory::Other);
1144
1145
RefPtr<ClientHandle> handle =
1146
ClientManager::CreateHandle(aClientInfo, target);
1147
mPendingReadyList.AppendElement(MakeUnique<PendingReadyData>(handle));
1148
1149
RefPtr<ServiceWorkerManager> self(this);
1150
handle->OnDetach()->Then(target, __func__,
1151
[self = std::move(self), aClientInfo] {
1152
self->RemovePendingReadyPromise(aClientInfo);
1153
});
1154
1155
return mPendingReadyList.LastElement()->mPromise;
1156
}
1157
1158
void ServiceWorkerManager::CheckPendingReadyPromises() {
1159
nsTArray<UniquePtr<PendingReadyData>> pendingReadyList;
1160
mPendingReadyList.SwapElements(pendingReadyList);
1161
for (uint32_t i = 0; i < pendingReadyList.Length(); ++i) {
1162
UniquePtr<PendingReadyData> prd(std::move(pendingReadyList[i]));
1163
1164
RefPtr<ServiceWorkerRegistrationInfo> reg =
1165
GetServiceWorkerRegistrationInfo(prd->mClientHandle->Info());
1166
1167
if (reg && reg->GetActive()) {
1168
prd->mPromise->Resolve(reg->Descriptor(), __func__);
1169
} else {
1170
mPendingReadyList.AppendElement(std::move(prd));
1171
}
1172
}
1173
}
1174
1175
void ServiceWorkerManager::RemovePendingReadyPromise(
1176
const ClientInfo& aClientInfo) {
1177
nsTArray<UniquePtr<PendingReadyData>> pendingReadyList;
1178
mPendingReadyList.SwapElements(pendingReadyList);
1179
for (uint32_t i = 0; i < pendingReadyList.Length(); ++i) {
1180
UniquePtr<PendingReadyData> prd(std::move(pendingReadyList[i]));
1181
1182
if (prd->mClientHandle->Info().Id() == aClientInfo.Id() &&
1183
prd->mClientHandle->Info().PrincipalInfo() ==
1184
aClientInfo.PrincipalInfo()) {
1185
prd->mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
1186
} else {
1187
mPendingReadyList.AppendElement(std::move(prd));
1188
}
1189
}
1190
}
1191
1192
void ServiceWorkerManager::NoteInheritedController(
1193
const ClientInfo& aClientInfo, const ServiceWorkerDescriptor& aController) {
1194
MOZ_ASSERT(NS_IsMainThread());
1195
1196
nsCOMPtr<nsIPrincipal> principal =
1197
PrincipalInfoToPrincipal(aController.PrincipalInfo());
1198
NS_ENSURE_TRUE_VOID(principal);
1199
1200
nsCOMPtr<nsIURI> scope;
1201
nsresult rv = NS_NewURI(getter_AddRefs(scope), aController.Scope());
1202
NS_ENSURE_SUCCESS_VOID(rv);
1203
1204
RefPtr<ServiceWorkerRegistrationInfo> registration =
1205
GetServiceWorkerRegistrationInfo(principal, scope);
1206
NS_ENSURE_TRUE_VOID(registration);
1207
NS_ENSURE_TRUE_VOID(registration->GetActive());
1208
1209
StartControllingClient(aClientInfo, registration,
1210
false /* aControlClientHandle */);
1211
}
1212
1213
ServiceWorkerInfo* ServiceWorkerManager::GetActiveWorkerInfoForScope(
1214
const OriginAttributes& aOriginAttributes, const nsACString& aScope) {
1215
MOZ_ASSERT(NS_IsMainThread());
1216
1217
nsCOMPtr<nsIURI> scopeURI;
1218
nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope);
1219
if (NS_FAILED(rv)) {
1220
return nullptr;
1221
}
1222
nsCOMPtr<nsIPrincipal> principal =
1223
BasePrincipal::CreateContentPrincipal(scopeURI, aOriginAttributes);
1224
RefPtr<ServiceWorkerRegistrationInfo> registration =
1225
GetServiceWorkerRegistrationInfo(principal, scopeURI);
1226
if (!registration) {
1227
return nullptr;
1228
}
1229
1230
return registration->GetActive();
1231
}
1232
1233
namespace {
1234
1235
class UnregisterJobCallback final : public ServiceWorkerJob::Callback {
1236
nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
1237
1238
~UnregisterJobCallback() {}
1239
1240
public:
1241
explicit UnregisterJobCallback(nsIServiceWorkerUnregisterCallback* aCallback)
1242
: mCallback(aCallback) {
1243
MOZ_ASSERT(NS_IsMainThread());
1244
MOZ_ASSERT(mCallback);
1245
}
1246
1247
void JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override {
1248
MOZ_ASSERT(NS_IsMainThread());
1249
MOZ_ASSERT(aJob);
1250
1251
if (aStatus.Failed()) {
1252
mCallback->UnregisterFailed();
1253
return;
1254
}
1255
1256
MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Unregister);
1257
RefPtr<ServiceWorkerUnregisterJob> unregisterJob =
1258
static_cast<ServiceWorkerUnregisterJob*>(aJob);
1259
mCallback->UnregisterSucceeded(unregisterJob->GetResult());
1260
}
1261
1262
NS_INLINE_DECL_REFCOUNTING(UnregisterJobCallback, override)
1263
};
1264
1265
} // anonymous namespace
1266
1267
NS_IMETHODIMP
1268
ServiceWorkerManager::Unregister(nsIPrincipal* aPrincipal,
1269
nsIServiceWorkerUnregisterCallback* aCallback,
1270
const nsAString& aScope) {
1271
MOZ_ASSERT(NS_IsMainThread());
1272
1273
if (!aPrincipal) {
1274
return NS_ERROR_FAILURE;
1275
}
1276
1277
nsresult rv;
1278
1279
// This is not accessible by content, and callers should always ensure scope is
1280
// a correct URI, so this is wrapped in DEBUG
1281
#ifdef DEBUG
1282
nsCOMPtr<nsIURI> scopeURI;
1283
rv = NS_NewURI(getter_AddRefs(scopeURI), aScope);
1284
if (NS_WARN_IF(NS_FAILED(rv))) {
1285
return NS_ERROR_DOM_SECURITY_ERR;
1286
}
1287
#endif
1288
1289
nsAutoCString scopeKey;
1290
rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1291
if (NS_WARN_IF(NS_FAILED(rv))) {
1292
return rv;
1293
}
1294
1295
NS_ConvertUTF16toUTF8 scope(aScope);
1296
RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope);
1297
1298
RefPtr<ServiceWorkerUnregisterJob> job = new ServiceWorkerUnregisterJob(
1299
aPrincipal, scope, true /* send to parent */);
1300
1301
if (aCallback) {
1302
RefPtr<UnregisterJobCallback> cb = new UnregisterJobCallback(aCallback);
1303
job->AppendResultCallback(cb);
1304
}
1305
1306
queue->ScheduleJob(job);
1307
return NS_OK;
1308
}
1309
1310
nsresult ServiceWorkerManager::NotifyUnregister(nsIPrincipal* aPrincipal,
1311
const nsAString& aScope) {
1312
MOZ_ASSERT(NS_IsMainThread());
1313
MOZ_ASSERT(aPrincipal);
1314
1315
nsresult rv;
1316
1317
// This is not accessible by content, and callers should always ensure scope is
1318
// a correct URI, so this is wrapped in DEBUG
1319
#ifdef DEBUG
1320
nsCOMPtr<nsIURI> scopeURI;
1321
rv = NS_NewURI(getter_AddRefs(scopeURI), aScope);
1322
if (NS_WARN_IF(NS_FAILED(rv))) {
1323
return rv;
1324
}
1325
#endif
1326
1327
nsAutoCString scopeKey;
1328
rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1329
if (NS_WARN_IF(NS_FAILED(rv))) {
1330
return rv;
1331
}
1332
1333
NS_ConvertUTF16toUTF8 scope(aScope);
1334
RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope);
1335
1336
RefPtr<ServiceWorkerUnregisterJob> job = new ServiceWorkerUnregisterJob(
1337
aPrincipal, scope, false /* send to parent */);
1338
1339
queue->ScheduleJob(job);
1340
return NS_OK;
1341
}
1342
1343
void ServiceWorkerManager::WorkerIsIdle(ServiceWorkerInfo* aWorker) {
1344
MOZ_ASSERT(NS_IsMainThread());
1345
MOZ_DIAGNOSTIC_ASSERT(aWorker);
1346
1347
RefPtr<ServiceWorkerRegistrationInfo> reg =
1348
GetRegistration(aWorker->Principal(), aWorker->Scope());
1349
if (!reg) {
1350
return;
1351
}
1352
1353
if (reg->GetActive() != aWorker) {
1354
return;
1355
}
1356
1357
reg->TryToActivateAsync();
1358
}
1359
1360
already_AddRefed<ServiceWorkerJobQueue>
1361
ServiceWorkerManager::GetOrCreateJobQueue(const nsACString& aKey,
1362
const nsACString& aScope) {
1363
MOZ_ASSERT(!aKey.IsEmpty());
1364
ServiceWorkerManager::RegistrationDataPerPrincipal* data;
1365
// XXX we could use LookupForAdd here to avoid a hashtable lookup, except that
1366
// leads to a false positive assertion, see bug 1370674 comment 7.
1367
if (!mRegistrationInfos.Get(aKey, &data)) {
1368
data = new RegistrationDataPerPrincipal();
1369
mRegistrationInfos.Put(aKey, data);
1370
}
1371
1372
RefPtr<ServiceWorkerJobQueue> queue =
1373
data->mJobQueues.LookupForAdd(aScope).OrInsert(
1374
[]() { return new ServiceWorkerJobQueue(); });
1375
1376
return queue.forget();
1377
}
1378
1379
/* static */
1380
already_AddRefed<ServiceWorkerManager> ServiceWorkerManager::GetInstance() {
1381
// Note: We don't simply check gInstance for null-ness here, since otherwise
1382
// this can resurrect the ServiceWorkerManager pretty late during shutdown.
1383
static bool firstTime = true;
1384
if (firstTime) {
1385
RefPtr<ServiceWorkerRegistrar> swr;
1386
1387
// Don't create the ServiceWorkerManager until the ServiceWorkerRegistrar is
1388
// initialized.
1389
if (XRE_IsParentProcess()) {
1390
swr = ServiceWorkerRegistrar::Get();
1391
if (!swr) {
1392
return nullptr;
1393
}
1394
}
1395
1396
firstTime = false;
1397
1398
MOZ_ASSERT(NS_IsMainThread());
1399
1400
gInstance = new ServiceWorkerManager();
1401
gInstance->Init(swr);
1402
ClearOnShutdown(&gInstance);
1403
}
1404
RefPtr<ServiceWorkerManager> copy = gInstance.get();
1405
return copy.forget();
1406
}
1407
1408
void ServiceWorkerManager::FinishFetch(
1409
ServiceWorkerRegistrationInfo* aRegistration) {}
1410
1411
void ServiceWorkerManager::ReportToAllClients(
1412
const nsCString& aScope, const nsString& aMessage,
1413
const nsString& aFilename, const nsString& aLine, uint32_t aLineNumber,
1414
uint32_t aColumnNumber, uint32_t aFlags) {
1415
ConsoleUtils::ReportForServiceWorkerScope(
1416
NS_ConvertUTF8toUTF16(aScope), aMessage, aFilename, aLineNumber,
1417
aColumnNumber, ConsoleUtils::eError);
1418
}
1419
1420
/* static */
1421
void ServiceWorkerManager::LocalizeAndReportToAllClients(
1422
const nsCString& aScope, const char* aStringKey,
1423
const nsTArray<nsString>& aParamArray, uint32_t aFlags,
1424
const nsString& aFilename, const nsString& aLine, uint32_t aLineNumber,
1425
uint32_t aColumnNumber) {
1426
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1427
if (!swm) {
1428
return;
1429
}
1430
1431
nsresult rv;
1432
nsAutoString message;
1433
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1434
aStringKey, aParamArray, message);
1435
if (NS_SUCCEEDED(rv)) {
1436
swm->ReportToAllClients(aScope, message, aFilename, aLine, aLineNumber,
1437
aColumnNumber, aFlags);
1438
} else {
1439
NS_WARNING("Failed to format and therefore report localized error.");
1440
}
1441
}
1442
1443
void ServiceWorkerManager::HandleError(
1444
JSContext* aCx, nsIPrincipal* aPrincipal, const nsCString& aScope,
1445
const nsString& aWorkerURL, const nsString& aMessage,
1446
const nsString& aFilename, const nsString& aLine, uint32_t aLineNumber,
1447
uint32_t aColumnNumber, uint32_t aFlags, JSExnType aExnType) {
1448
MOZ_ASSERT(NS_IsMainThread());
1449
MOZ_ASSERT(aPrincipal);
1450
1451
nsAutoCString scopeKey;
1452
nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1453
if (NS_WARN_IF(NS_FAILED(rv))) {
1454
return;
1455
}
1456
1457
ServiceWorkerManager::RegistrationDataPerPrincipal* data;
1458
if (NS_WARN_IF(!mRegistrationInfos.Get(scopeKey, &data))) {
1459
return;
1460
}
1461
1462
// Always report any uncaught exceptions or errors to the console of
1463
// each client.
1464
ReportToAllClients(aScope, aMessage, aFilename, aLine, aLineNumber,
1465
aColumnNumber, aFlags);
1466
}
1467
1468
void ServiceWorkerManager::LoadRegistration(
1469
const ServiceWorkerRegistrationData& aRegistration) {
1470
MOZ_ASSERT(NS_IsMainThread());
1471
1472
nsCOMPtr<nsIPrincipal> principal =
1473
PrincipalInfoToPrincipal(aRegistration.principal());
1474
if (!principal) {
1475
return;
1476
}
1477
1478
RefPtr<ServiceWorkerRegistrationInfo> registration =
1479
GetRegistration(principal, aRegistration.scope());
1480
if (!registration) {
1481
registration =
1482
CreateNewRegistration(aRegistration.scope(), principal,
1483
static_cast<ServiceWorkerUpdateViaCache>(
1484
aRegistration.updateViaCache()));
1485
} else {
1486
// If active worker script matches our expectations for a "current worker",
1487
// then we are done. Since scripts with the same URL might have different
1488
// contents such as updated scripts or scripts with different LoadFlags, we
1489
// use the CacheName to judje whether the two scripts are identical, where
1490
// the CacheName is an UUID generated when a new script is found.
1491
if (registration->GetActive() &&
1492
registration->GetActive()->CacheName() == aRegistration.cacheName()) {
1493
// No needs for updates.
1494
return;
1495
}
1496
}
1497
1498
registration->SetLastUpdateTime(aRegistration.lastUpdateTime());
1499
1500
nsLoadFlags importsLoadFlags = nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
1501
importsLoadFlags |=
1502
aRegistration.updateViaCache() ==
1503
static_cast<uint16_t>(ServiceWorkerUpdateViaCache::None)
1504
? nsIRequest::LOAD_NORMAL
1505
: nsIRequest::VALIDATE_ALWAYS;
1506
1507
const nsCString& currentWorkerURL = aRegistration.currentWorkerURL();
1508
if (!currentWorkerURL.IsEmpty()) {
1509
registration->SetActive(new ServiceWorkerInfo(
1510
registration->Principal(), registration->Scope(), registration->Id(),
1511
registration->Version(), currentWorkerURL, aRegistration.cacheName(),
1512
importsLoadFlags));
1513
registration->GetActive()->SetHandlesFetch(
1514
aRegistration.currentWorkerHandlesFetch());
1515
registration->GetActive()->SetInstalledTime(
1516
aRegistration.currentWorkerInstalledTime());
1517
registration->GetActive()->SetActivatedTime(
1518
aRegistration.currentWorkerActivatedTime());
1519
}
1520
}
1521
1522
void ServiceWorkerManager::LoadRegistrations(
1523
const nsTArray<ServiceWorkerRegistrationData>& aRegistrations) {
1524
MOZ_ASSERT(NS_IsMainThread());
1525
1526
for (uint32_t i = 0, len = aRegistrations.Length(); i < len; ++i) {
1527
LoadRegistration(aRegistrations[i]);
1528
}
1529
}
1530
1531
void ServiceWorkerManager::StoreRegistration(
1532
nsIPrincipal* aPrincipal, ServiceWorkerRegistrationInfo* aRegistration) {
1533
MOZ_ASSERT(aPrincipal);
1534
MOZ_ASSERT(aRegistration);
1535
1536
if (mShuttingDown) {
1537
return;
1538
}
1539
1540
ServiceWorkerRegistrationData data;
1541
nsresult rv = PopulateRegistrationData(aPrincipal, aRegistration, data);
1542
if (NS_WARN_IF(NS_FAILED(rv))) {
1543
return;
1544
}
1545
1546
PrincipalInfo principalInfo;
1547
if (NS_WARN_IF(
1548
NS_FAILED(PrincipalToPrincipalInfo(aPrincipal, &principalInfo)))) {
1549
return;
1550
}
1551
1552
mActor->SendRegister(data);
1553
}
1554
1555
already_AddRefed<ServiceWorkerRegistrationInfo>
1556
ServiceWorkerManager::GetServiceWorkerRegistrationInfo(
1557
const ClientInfo& aClientInfo) const {
1558
nsCOMPtr<nsIPrincipal> principal = aClientInfo.GetPrincipal();
1559
NS_ENSURE_TRUE(principal, nullptr);
1560
1561
nsCOMPtr<nsIURI> uri;
1562
nsresult rv = NS_NewURI(getter_AddRefs(uri), aClientInfo.URL());
1563
NS_ENSURE_SUCCESS(rv, nullptr);
1564
1565
return GetServiceWorkerRegistrationInfo(principal, uri);
1566
}
1567
1568
already_AddRefed<ServiceWorkerRegistrationInfo>
1569
ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal,
1570
nsIURI* aURI) const {
1571
MOZ_ASSERT(aPrincipal);
1572
MOZ_ASSERT(aURI);
1573
1574
nsAutoCString scopeKey;
1575
nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1576
if (NS_FAILED(rv)) {
1577
return nullptr;
1578
}
1579
1580
return GetServiceWorkerRegistrationInfo(scopeKey, aURI);
1581
}
1582
1583
already_AddRefed<ServiceWorkerRegistrationInfo>
1584
ServiceWorkerManager::GetServiceWorkerRegistrationInfo(
1585
const nsACString& aScopeKey, nsIURI* aURI) const {
1586
MOZ_ASSERT(aURI);
1587
1588
nsAutoCString spec;
1589
nsresult rv = aURI->GetSpec(spec);
1590
if (NS_WARN_IF(NS_FAILED(rv))) {
1591
return nullptr;
1592
}
1593
1594
nsAutoCString scope;
1595
RegistrationDataPerPrincipal* data;
1596
if (!FindScopeForPath(aScopeKey, spec, &data, scope)) {
1597
return nullptr;
1598
}
1599
1600
MOZ_ASSERT(data);
1601
1602
RefPtr<ServiceWorkerRegistrationInfo> registration;
1603
data->mInfos.Get(scope, getter_AddRefs(registration));
1604
// ordered scopes and registrations better be in sync.
1605
MOZ_ASSERT(registration);
1606
1607
#ifdef DEBUG
1608
nsAutoCString origin;
1609
rv = registration->Principal()->GetOrigin(origin);
1610
MOZ_ASSERT(NS_SUCCEEDED(rv));
1611
MOZ_ASSERT(origin.Equals(aScopeKey));
1612
#endif
1613
1614
return registration.forget();
1615
}
1616
1617
/* static */
1618
nsresult ServiceWorkerManager::PrincipalToScopeKey(nsIPrincipal* aPrincipal,
1619
nsACString& aKey) {
1620
MOZ_ASSERT(aPrincipal);
1621
1622
if (!BasePrincipal::Cast(aPrincipal)->IsContentPrincipal()) {
1623
return NS_ERROR_FAILURE;
1624
}
1625
1626
nsresult rv = aPrincipal->GetOrigin(aKey);
1627
if (NS_WARN_IF(NS_FAILED(rv))) {
1628
return rv;
1629
}
1630
1631
return NS_OK;
1632
}
1633
1634
/* static */
1635
nsresult ServiceWorkerManager::PrincipalInfoToScopeKey(
1636
const PrincipalInfo& aPrincipalInfo, nsACString& aKey) {
1637
if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo) {
1638
return NS_ERROR_FAILURE;
1639
}
1640
1641
auto content = aPrincipalInfo.get_ContentPrincipalInfo();
1642
1643
nsAutoCString suffix;
1644
content.attrs().CreateSuffix(suffix);
1645
1646
aKey = content.originNoSuffix();
1647
aKey.Append(suffix);
1648
1649
return NS_OK;
1650
}
1651
1652
/* static */
1653
void ServiceWorkerManager::AddScopeAndRegistration(
1654
const nsACString& aScope, ServiceWorkerRegistrationInfo* aInfo) {
1655
MOZ_ASSERT(aInfo);
1656
MOZ_ASSERT(aInfo->Principal());
1657
MOZ_ASSERT(!aInfo->IsUnregistered());
1658
1659
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1660
if (!swm) {
1661
// browser shutdown
1662
return;
1663
}
1664
1665
nsAutoCString scopeKey;
1666
nsresult rv = swm->PrincipalToScopeKey(aInfo->Principal(), scopeKey);
1667
if (NS_WARN_IF(NS_FAILED(rv))) {
1668
return;
1669
}
1670
1671
MOZ_ASSERT(!scopeKey.IsEmpty());
1672
1673
RegistrationDataPerPrincipal* data =
1674
swm->mRegistrationInfos.LookupForAdd(scopeKey).OrInsert(
1675
[]() { return new RegistrationDataPerPrincipal(); });
1676
1677
for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) {
1678
const nsCString& current = data->mOrderedScopes[i];
1679
1680
// Perfect match!
1681
if (aScope.Equals(current)) {
1682
data->mInfos.Put(aScope, aInfo);
1683
swm->NotifyListenersOnRegister(aInfo);
1684
return;
1685
}
1686
1687
// Sort by length, with longest match first.
1688
// /foo/bar should be before /foo/
1689
// Similarly /foo/b is between the two.
1690
if (StringBeginsWith(aScope, current)) {
1691
data->mOrderedScopes.InsertElementAt(i, aScope);
1692
data->mInfos.Put(aScope, aInfo);
1693
swm->NotifyListenersOnRegister(aInfo);
1694
return;
1695
}
1696
}
1697
1698
data->mOrderedScopes.AppendElement(aScope);
1699
data->mInfos.Put(aScope, aInfo);
1700
swm->NotifyListenersOnRegister(aInfo);
1701
}
1702
1703
/* static */
1704
bool ServiceWorkerManager::FindScopeForPath(
1705
const nsACString& aScopeKey, const nsACString& aPath,
1706
RegistrationDataPerPrincipal** aData, nsACString& aMatch) {
1707
MOZ_ASSERT(aData);
1708
1709
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1710
1711
if (!swm || !swm->mRegistrationInfos.Get(aScopeKey, aData)) {
1712
return false;
1713
}
1714
1715
for (uint32_t i = 0; i < (*aData)->mOrderedScopes.Length(); ++i) {
1716
const nsCString& current = (*aData)->mOrderedScopes[i];
1717
if (StringBeginsWith(aPath, current)) {
1718
aMatch = current;
1719
return true;
1720
}
1721
}
1722
1723
return false;
1724
}
1725
1726
/* static */
1727
bool ServiceWorkerManager::HasScope(nsIPrincipal* aPrincipal,
1728
const nsACString& aScope) {
1729
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1730
if (!swm) {
1731
return false;
1732
}
1733
1734
nsAutoCString scopeKey;
1735
nsresult