Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/serviceworkers/ServiceWorkerManager.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "ServiceWorkerManager.h"
8
9
#include "nsAutoPtr.h"
10
#include "nsIConsoleService.h"
11
#include "nsIEffectiveTLDService.h"
12
#include "nsIScriptSecurityManager.h"
13
#include "nsIStreamLoader.h"
14
#include "nsIHttpChannel.h"
15
#include "nsIHttpChannelInternal.h"
16
#include "nsIHttpHeaderVisitor.h"
17
#include "nsINamed.h"
18
#include "nsINetworkInterceptController.h"
19
#include "nsIMutableArray.h"
20
#include "nsIScriptError.h"
21
#include "nsISimpleEnumerator.h"
22
#include "nsITimer.h"
23
#include "nsIUploadChannel2.h"
24
#include "nsServiceManagerUtils.h"
25
#include "nsDebug.h"
26
#include "nsISupportsPrimitives.h"
27
#include "nsIPermissionManager.h"
28
29
#include "jsapi.h"
30
31
#include "mozilla/BasePrincipal.h"
32
#include "mozilla/ClearOnShutdown.h"
33
#include "mozilla/ErrorNames.h"
34
#include "mozilla/LoadContext.h"
35
#include "mozilla/SystemGroup.h"
36
#include "mozilla/Telemetry.h"
37
#include "mozilla/dom/BindingUtils.h"
38
#include "mozilla/dom/ClientHandle.h"
39
#include "mozilla/dom/ClientManager.h"
40
#include "mozilla/dom/ClientSource.h"
41
#include "mozilla/dom/ConsoleUtils.h"
42
#include "mozilla/dom/ContentParent.h"
43
#include "mozilla/dom/DOMPrefs.h"
44
#include "mozilla/dom/ErrorEvent.h"
45
#include "mozilla/dom/Headers.h"
46
#include "mozilla/dom/InternalHeaders.h"
47
#include "mozilla/dom/Navigator.h"
48
#include "mozilla/dom/NotificationEvent.h"
49
#include "mozilla/dom/PromiseNativeHandler.h"
50
#include "mozilla/dom/Request.h"
51
#include "mozilla/dom/RootedDictionary.h"
52
#include "mozilla/dom/TypedArray.h"
53
#include "mozilla/dom/SharedWorker.h"
54
#include "mozilla/dom/WorkerPrivate.h"
55
#include "mozilla/dom/WorkerRunnable.h"
56
#include "mozilla/dom/WorkerScope.h"
57
#include "mozilla/ipc/BackgroundChild.h"
58
#include "mozilla/ipc/PBackgroundChild.h"
59
#include "mozilla/ipc/PBackgroundSharedTypes.h"
60
#include "mozilla/dom/ScriptLoader.h"
61
#include "mozilla/Unused.h"
62
#include "mozilla/EnumSet.h"
63
64
#include "nsContentUtils.h"
65
#include "nsNetUtil.h"
66
#include "nsProxyRelease.h"
67
#include "nsQueryObject.h"
68
#include "nsTArray.h"
69
70
#include "ServiceWorker.h"
71
#include "ServiceWorkerContainer.h"
72
#include "ServiceWorkerInfo.h"
73
#include "ServiceWorkerJobQueue.h"
74
#include "ServiceWorkerManagerChild.h"
75
#include "ServiceWorkerPrivate.h"
76
#include "ServiceWorkerRegisterJob.h"
77
#include "ServiceWorkerRegistrar.h"
78
#include "ServiceWorkerRegistration.h"
79
#include "ServiceWorkerScriptCache.h"
80
#include "ServiceWorkerEvents.h"
81
#include "ServiceWorkerUnregisterJob.h"
82
#include "ServiceWorkerUpdateJob.h"
83
#include "ServiceWorkerUpdaterChild.h"
84
#include "ServiceWorkerUtils.h"
85
86
#ifdef PostMessage
87
#undef PostMessage
88
#endif
89
90
using namespace mozilla;
91
using namespace mozilla::dom;
92
using namespace mozilla::ipc;
93
94
namespace mozilla {
95
namespace dom {
96
97
0
#define CLEAR_ORIGIN_DATA "clear-origin-attributes-data"
98
99
static_assert(nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN == static_cast<uint32_t>(RequestMode::Same_origin),
100
              "RequestMode enumeration value should match Necko CORS mode value.");
101
static_assert(nsIHttpChannelInternal::CORS_MODE_NO_CORS == static_cast<uint32_t>(RequestMode::No_cors),
102
              "RequestMode enumeration value should match Necko CORS mode value.");
103
static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(RequestMode::Cors),
104
              "RequestMode enumeration value should match Necko CORS mode value.");
105
static_assert(nsIHttpChannelInternal::CORS_MODE_NAVIGATE == static_cast<uint32_t>(RequestMode::Navigate),
106
              "RequestMode enumeration value should match Necko CORS mode value.");
107
108
static_assert(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW == static_cast<uint32_t>(RequestRedirect::Follow),
109
              "RequestRedirect enumeration value should make Necko Redirect mode value.");
110
static_assert(nsIHttpChannelInternal::REDIRECT_MODE_ERROR == static_cast<uint32_t>(RequestRedirect::Error),
111
              "RequestRedirect enumeration value should make Necko Redirect mode value.");
112
static_assert(nsIHttpChannelInternal::REDIRECT_MODE_MANUAL == static_cast<uint32_t>(RequestRedirect::Manual),
113
              "RequestRedirect enumeration value should make Necko Redirect mode value.");
114
static_assert(3 == static_cast<uint32_t>(RequestRedirect::EndGuard_),
115
              "RequestRedirect enumeration value should make Necko Redirect mode value.");
116
117
static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT == static_cast<uint32_t>(RequestCache::Default),
118
             "RequestCache enumeration value should match Necko Cache mode value.");
119
static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE == static_cast<uint32_t>(RequestCache::No_store),
120
             "RequestCache enumeration value should match Necko Cache mode value.");
121
static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD == static_cast<uint32_t>(RequestCache::Reload),
122
             "RequestCache enumeration value should match Necko Cache mode value.");
123
static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE == static_cast<uint32_t>(RequestCache::No_cache),
124
             "RequestCache enumeration value should match Necko Cache mode value.");
125
static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE == static_cast<uint32_t>(RequestCache::Force_cache),
126
             "RequestCache enumeration value should match Necko Cache mode value.");
127
static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED == static_cast<uint32_t>(RequestCache::Only_if_cached),
128
             "RequestCache enumeration value should match Necko Cache mode value.");
129
static_assert(6 == static_cast<uint32_t>(RequestCache::EndGuard_),
130
             "RequestCache enumeration value should match Necko Cache mode value.");
131
132
static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::Imports) ==
133
              nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS,
134
              "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*"
135
              " should match ServiceWorkerUpdateViaCache enumeration.");
136
static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::All) ==
137
              nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL,
138
              "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*"
139
              " should match ServiceWorkerUpdateViaCache enumeration.");
140
static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::None) ==
141
              nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE,
142
              "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*"
143
              " should match ServiceWorkerUpdateViaCache enumeration.");
144
145
static StaticRefPtr<ServiceWorkerManager> gInstance;
146
147
struct ServiceWorkerManager::RegistrationDataPerPrincipal final
148
{
149
  // Ordered list of scopes for glob matching.
150
  // Each entry is an absolute URL representing the scope.
151
  // Each value of the hash table is an array of an absolute URLs representing
152
  // the scopes.
153
  //
154
  // An array is used for now since the number of controlled scopes per
155
  // domain is expected to be relatively low. If that assumption was proved
156
  // wrong this should be replaced with a better structure to avoid the
157
  // memmoves associated with inserting stuff in the middle of the array.
158
  nsTArray<nsCString> mOrderedScopes;
159
160
  // Scope to registration.
161
  // The scope should be a fully qualified valid URL.
162
  nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mInfos;
163
164
  // Maps scopes to job queues.
165
  nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues;
166
167
  // Map scopes to scheduled update timers.
168
  nsInterfaceHashtable<nsCStringHashKey, nsITimer> mUpdateTimers;
169
};
170
171
namespace {
172
173
nsresult
174
PopulateRegistrationData(nsIPrincipal* aPrincipal,
175
                         const ServiceWorkerRegistrationInfo* aRegistration,
176
                         ServiceWorkerRegistrationData& aData)
177
0
{
178
0
  MOZ_ASSERT(aPrincipal);
179
0
  MOZ_ASSERT(aRegistration);
180
0
181
0
  if (NS_WARN_IF(!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal())) {
182
0
    return NS_ERROR_FAILURE;
183
0
  }
184
0
185
0
  nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aData.principal());
186
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
187
0
    return rv;
188
0
  }
189
0
190
0
  aData.scope() = aRegistration->Scope();
191
0
192
0
  // TODO: When bug 1426401 is implemented we will need to handle more
193
0
  //       than just the active worker here.
194
0
  RefPtr<ServiceWorkerInfo> active = aRegistration->GetActive();
195
0
  MOZ_ASSERT(active);
196
0
  if (NS_WARN_IF(!active)) {
197
0
    return NS_ERROR_FAILURE;
198
0
  }
199
0
200
0
  aData.currentWorkerURL() = active->ScriptSpec();
201
0
  aData.cacheName() = active->CacheName();
202
0
  aData.currentWorkerHandlesFetch() = active->HandlesFetch();
203
0
204
0
  aData.currentWorkerInstalledTime() = active->GetInstalledTime();
205
0
  aData.currentWorkerActivatedTime() = active->GetActivatedTime();
206
0
207
0
  aData.updateViaCache() =
208
0
    static_cast<uint32_t>(aRegistration->GetUpdateViaCache());
209
0
210
0
  aData.lastUpdateTime() = aRegistration->GetLastUpdateTime();
211
0
212
0
  MOZ_ASSERT(ServiceWorkerRegistrationDataIsValid(aData));
213
0
214
0
  return NS_OK;
215
0
}
216
217
class TeardownRunnable final : public Runnable
218
{
219
public:
220
  explicit TeardownRunnable(ServiceWorkerManagerChild* aActor)
221
    : Runnable("dom::ServiceWorkerManager::TeardownRunnable")
222
    , mActor(aActor)
223
0
  {
224
0
    MOZ_ASSERT(mActor);
225
0
  }
226
227
  NS_IMETHOD Run() override
228
0
  {
229
0
    MOZ_ASSERT(mActor);
230
0
    mActor->SendShutdown();
231
0
    return NS_OK;
232
0
  }
233
234
private:
235
0
  ~TeardownRunnable() {}
236
237
  RefPtr<ServiceWorkerManagerChild> mActor;
238
};
239
240
} // namespace
241
242
//////////////////////////
243
// ServiceWorkerManager //
244
//////////////////////////
245
246
NS_IMPL_ADDREF(ServiceWorkerManager)
247
NS_IMPL_RELEASE(ServiceWorkerManager)
248
249
0
NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
250
0
  NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager)
251
0
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
252
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager)
253
0
NS_INTERFACE_MAP_END
254
255
ServiceWorkerManager::ServiceWorkerManager()
256
  : mActor(nullptr)
257
  , mShuttingDown(false)
258
0
{
259
0
}
260
261
ServiceWorkerManager::~ServiceWorkerManager()
262
0
{
263
0
  // The map will assert if it is not empty when destroyed.
264
0
  mRegistrationInfos.Clear();
265
0
  MOZ_ASSERT(!mActor);
266
0
}
267
268
void
269
ServiceWorkerManager::Init(ServiceWorkerRegistrar* aRegistrar)
270
0
{
271
0
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
272
0
  if (obs) {
273
0
    DebugOnly<nsresult> rv;
274
0
    rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false /* ownsWeak */);
275
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
276
0
  }
277
0
278
0
  if (XRE_IsParentProcess()) {
279
0
    MOZ_DIAGNOSTIC_ASSERT(aRegistrar);
280
0
281
0
    nsTArray<ServiceWorkerRegistrationData> data;
282
0
    aRegistrar->GetRegistrations(data);
283
0
    LoadRegistrations(data);
284
0
285
0
    if (obs) {
286
0
      DebugOnly<nsresult> rv;
287
0
      rv = obs->AddObserver(this, CLEAR_ORIGIN_DATA, false /* ownsWeak */);
288
0
      MOZ_ASSERT(NS_SUCCEEDED(rv));
289
0
    }
290
0
  }
291
0
292
0
  PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
293
0
  if (NS_WARN_IF(!actorChild)) {
294
0
    MaybeStartShutdown();
295
0
    return;
296
0
  }
297
0
298
0
  PServiceWorkerManagerChild* actor =
299
0
    actorChild->SendPServiceWorkerManagerConstructor();
300
0
  if (!actor) {
301
0
    MaybeStartShutdown();
302
0
    return;
303
0
  }
304
0
305
0
  mActor = static_cast<ServiceWorkerManagerChild*>(actor);
306
0
}
307
308
RefPtr<GenericPromise>
309
ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo,
310
                                             ServiceWorkerRegistrationInfo* aRegistrationInfo,
311
                                             bool aControlClientHandle)
312
0
{
313
0
  MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive());
314
0
315
0
  RefPtr<GenericPromise> ref;
316
0
  RefPtr<ServiceWorkerManager> self(this);
317
0
318
0
  const ServiceWorkerDescriptor& active =
319
0
    aRegistrationInfo->GetActive()->Descriptor();
320
0
321
0
  auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
322
0
  if (entry) {
323
0
    RefPtr<ServiceWorkerRegistrationInfo> old =
324
0
      entry.Data()->mRegistrationInfo.forget();
325
0
326
0
    if (aControlClientHandle) {
327
0
      ref = entry.Data()->mClientHandle->Control(active);
328
0
    } else {
329
0
      ref = GenericPromise::CreateAndResolve(false, __func__);
330
0
    }
331
0
332
0
    entry.Data()->mRegistrationInfo = aRegistrationInfo;
333
0
334
0
    if (old != aRegistrationInfo) {
335
0
      StopControllingRegistration(old);
336
0
      aRegistrationInfo->StartControllingClient();
337
0
    }
338
0
339
0
    Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
340
0
341
0
    // Always check to see if we failed to actually control the client.  In
342
0
    // that case removed the client from our list of controlled clients.
343
0
    ref->Then(
344
0
      SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
345
0
      [] (bool) {
346
0
        // do nothing on success
347
0
      }, [self, aClientInfo] (nsresult aRv) {
348
0
        // failed to control, forget about this client
349
0
        self->StopControllingClient(aClientInfo);
350
0
      });
351
0
352
0
    return ref;
353
0
  }
354
0
355
0
  RefPtr<ClientHandle> clientHandle =
356
0
    ClientManager::CreateHandle(aClientInfo,
357
0
                                SystemGroup::EventTargetFor(TaskCategory::Other));
358
0
359
0
  if (aControlClientHandle) {
360
0
    ref = clientHandle->Control(active);
361
0
  } else {
362
0
    ref = GenericPromise::CreateAndResolve(false, __func__);
363
0
  }
364
0
365
0
  aRegistrationInfo->StartControllingClient();
366
0
367
0
  entry.OrInsert([&] {
368
0
    return new ControlledClientData(clientHandle, aRegistrationInfo);
369
0
  });
370
0
371
0
  clientHandle->OnDetach()->Then(
372
0
    SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
373
0
    [self, aClientInfo] {
374
0
      self->StopControllingClient(aClientInfo);
375
0
    });
376
0
377
0
  Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
378
0
379
0
  // Always check to see if we failed to actually control the client.  In
380
0
  // that case removed the client from our list of controlled clients.
381
0
  ref->Then(
382
0
    SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
383
0
    [] (bool) {
384
0
      // do nothing on success
385
0
    }, [self, aClientInfo] (nsresult aRv) {
386
0
      // failed to control, forget about this client
387
0
      self->StopControllingClient(aClientInfo);
388
0
    });
389
0
390
0
  return ref;
391
0
}
392
393
void
394
ServiceWorkerManager::StopControllingClient(const ClientInfo& aClientInfo)
395
0
{
396
0
  auto entry = mControlledClients.Lookup(aClientInfo.Id());
397
0
  if (!entry) {
398
0
    return;
399
0
  }
400
0
401
0
  RefPtr<ServiceWorkerRegistrationInfo> reg =
402
0
    entry.Data()->mRegistrationInfo.forget();
403
0
404
0
  entry.Remove();
405
0
406
0
  StopControllingRegistration(reg);
407
0
}
408
409
void
410
ServiceWorkerManager::MaybeStartShutdown()
411
0
{
412
0
  MOZ_ASSERT(NS_IsMainThread());
413
0
414
0
  if (mShuttingDown) {
415
0
    return;
416
0
  }
417
0
418
0
  mShuttingDown = true;
419
0
420
0
  for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
421
0
    for (auto it2 = it1.UserData()->mUpdateTimers.Iter(); !it2.Done(); it2.Next()) {
422
0
      nsCOMPtr<nsITimer> timer = it2.UserData();
423
0
      timer->Cancel();
424
0
    }
425
0
    it1.UserData()->mUpdateTimers.Clear();
426
0
427
0
    for (auto it2 = it1.UserData()->mJobQueues.Iter(); !it2.Done(); it2.Next()) {
428
0
      RefPtr<ServiceWorkerJobQueue> queue = it2.UserData();
429
0
      queue->CancelAll();
430
0
    }
431
0
    it1.UserData()->mJobQueues.Clear();
432
0
  }
433
0
434
0
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
435
0
  if (obs) {
436
0
    obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
437
0
438
0
    if (XRE_IsParentProcess()) {
439
0
      obs->RemoveObserver(this, CLEAR_ORIGIN_DATA);
440
0
    }
441
0
  }
442
0
443
0
  if (!mActor) {
444
0
    return;
445
0
  }
446
0
447
0
  mActor->ManagerShuttingDown();
448
0
449
0
  RefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
450
0
  nsresult rv = NS_DispatchToMainThread(runnable);
451
0
  Unused << NS_WARN_IF(NS_FAILED(rv));
452
0
  mActor = nullptr;
453
0
}
454
455
class ServiceWorkerResolveWindowPromiseOnRegisterCallback final : public ServiceWorkerJob::Callback
456
{
457
  RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
458
459
  ~ServiceWorkerResolveWindowPromiseOnRegisterCallback()
460
0
  {}
461
462
  virtual void
463
  JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override
464
0
  {
465
0
    MOZ_ASSERT(NS_IsMainThread());
466
0
    MOZ_ASSERT(aJob);
467
0
468
0
    if (aStatus.Failed()) {
469
0
      mPromise->Reject(std::move(aStatus), __func__);
470
0
      return;
471
0
    }
472
0
473
0
    MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Register);
474
0
    RefPtr<ServiceWorkerRegisterJob> registerJob =
475
0
      static_cast<ServiceWorkerRegisterJob*>(aJob);
476
0
    RefPtr<ServiceWorkerRegistrationInfo> reg = registerJob->GetRegistration();
477
0
478
0
    mPromise->Resolve(reg->Descriptor(), __func__);
479
0
  }
480
481
public:
482
  ServiceWorkerResolveWindowPromiseOnRegisterCallback()
483
    : mPromise(new ServiceWorkerRegistrationPromise::Private(__func__))
484
0
  {}
485
486
  RefPtr<ServiceWorkerRegistrationPromise>
487
  Promise() const
488
0
  {
489
0
    return mPromise;
490
0
  }
491
492
  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerResolveWindowPromiseOnRegisterCallback, override)
493
};
494
495
namespace {
496
497
class PropagateSoftUpdateRunnable final : public Runnable
498
{
499
public:
500
  PropagateSoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
501
                              const nsAString& aScope)
502
    : Runnable("dom::ServiceWorkerManager::PropagateSoftUpdateRunnable")
503
    , mOriginAttributes(aOriginAttributes)
504
    , mScope(aScope)
505
0
  {}
506
507
  NS_IMETHOD Run() override
508
0
  {
509
0
    MOZ_ASSERT(NS_IsMainThread());
510
0
511
0
    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
512
0
    if (swm) {
513
0
      swm->PropagateSoftUpdate(mOriginAttributes, mScope);
514
0
    }
515
0
516
0
    return NS_OK;
517
0
  }
518
519
private:
520
  ~PropagateSoftUpdateRunnable()
521
0
  {}
522
523
  const OriginAttributes mOriginAttributes;
524
  const nsString mScope;
525
};
526
527
class PromiseResolverCallback final : public ServiceWorkerUpdateFinishCallback
528
{
529
public:
530
  PromiseResolverCallback(ServiceWorkerUpdateFinishCallback* aCallback,
531
                          GenericPromise::Private* aPromise)
532
    : mCallback(aCallback)
533
    , mPromise(aPromise)
534
0
  {
535
0
    MOZ_DIAGNOSTIC_ASSERT(mPromise);
536
0
  }
537
538
  void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override
539
0
  {
540
0
    MOZ_DIAGNOSTIC_ASSERT(mPromise);
541
0
542
0
    if (mCallback) {
543
0
      mCallback->UpdateSucceeded(aInfo);
544
0
    }
545
0
546
0
    MaybeResolve();
547
0
  }
548
549
  void UpdateFailed(ErrorResult& aStatus) override
550
0
  {
551
0
    MOZ_DIAGNOSTIC_ASSERT(mPromise);
552
0
553
0
    if (mCallback) {
554
0
      mCallback->UpdateFailed(aStatus);
555
0
    }
556
0
557
0
    MaybeResolve();
558
0
  }
559
560
private:
561
  ~PromiseResolverCallback()
562
0
  {
563
0
    MaybeResolve();
564
0
  }
565
566
  void
567
  MaybeResolve()
568
0
  {
569
0
    if (mPromise) {
570
0
      mPromise->Resolve(true, __func__);
571
0
      mPromise = nullptr;
572
0
    }
573
0
  }
574
575
  RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
576
  RefPtr<GenericPromise::Private> mPromise;
577
};
578
579
// This runnable is used for 2 different tasks:
580
// - to postpone the SoftUpdate() until the IPC SWM actor is created
581
//   (aInternalMethod == false)
582
// - to call the 'real' SoftUpdate when the ServiceWorkerUpdaterChild is
583
//   notified by the parent (aInternalMethod == true)
584
class SoftUpdateRunnable final : public CancelableRunnable
585
{
586
public:
587
  SoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
588
                     const nsACString& aScope,
589
                     bool aInternalMethod,
590
                     GenericPromise::Private* aPromise)
591
    : CancelableRunnable("dom::ServiceWorkerManager::SoftUpdateRunnable")
592
    , mAttrs(aOriginAttributes)
593
    , mScope(aScope)
594
    , mInternalMethod(aInternalMethod)
595
    , mPromise(aPromise)
596
0
  {}
597
598
  NS_IMETHOD Run() override
599
0
  {
600
0
    MOZ_ASSERT(NS_IsMainThread());
601
0
602
0
    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
603
0
    if (!swm) {
604
0
      return NS_ERROR_FAILURE;
605
0
    }
606
0
607
0
    if (mInternalMethod) {
608
0
      RefPtr<PromiseResolverCallback> callback =
609
0
        new PromiseResolverCallback(nullptr, mPromise);
610
0
      mPromise = nullptr;
611
0
612
0
      swm->SoftUpdateInternal(mAttrs, mScope, callback);
613
0
    } else {
614
0
      swm->SoftUpdate(mAttrs, mScope);
615
0
    }
616
0
617
0
    return NS_OK;
618
0
  }
619
620
  nsresult
621
  Cancel() override
622
0
  {
623
0
    mPromise = nullptr;
624
0
    return NS_OK;
625
0
  }
626
627
private:
628
  ~SoftUpdateRunnable()
629
0
  {
630
0
    if (mPromise) {
631
0
      mPromise->Resolve(true, __func__);
632
0
    }
633
0
  }
634
635
  const OriginAttributes mAttrs;
636
  const nsCString mScope;
637
  bool mInternalMethod;
638
639
  RefPtr<GenericPromise::Private> mPromise;
640
};
641
642
// This runnable is used for 3 different tasks:
643
// - to postpone the Update() until the IPC SWM actor is created
644
//   (aType == ePostpone)
645
// - to call the 'real' Update when the ServiceWorkerUpdaterChild is
646
//   notified by the parent (aType == eSuccess)
647
// - an error must be propagated (aType == eFailure)
648
class UpdateRunnable final : public CancelableRunnable
649
{
650
public:
651
  enum Type {
652
    ePostpone,
653
    eSuccess,
654
    eFailure,
655
  };
656
657
  UpdateRunnable(nsIPrincipal* aPrincipal,
658
                 const nsACString& aScope,
659
                 ServiceWorkerUpdateFinishCallback* aCallback,
660
                 Type aType,
661
                 GenericPromise::Private* aPromise)
662
    : CancelableRunnable("dom::ServiceWorkerManager::UpdateRunnable")
663
    , mPrincipal(aPrincipal)
664
    , mScope(aScope)
665
    , mCallback(aCallback)
666
    , mType(aType)
667
    , mPromise(aPromise)
668
0
  {}
669
670
  NS_IMETHOD Run() override
671
0
  {
672
0
    MOZ_ASSERT(NS_IsMainThread());
673
0
674
0
    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
675
0
    if (!swm) {
676
0
      return NS_ERROR_FAILURE;
677
0
    }
678
0
679
0
    if (mType == ePostpone) {
680
0
      swm->Update(mPrincipal, mScope, mCallback);
681
0
      return NS_OK;
682
0
    }
683
0
684
0
    MOZ_ASSERT(mPromise);
685
0
686
0
    RefPtr<PromiseResolverCallback> callback =
687
0
      new PromiseResolverCallback(mCallback, mPromise);
688
0
    mPromise = nullptr;
689
0
690
0
    if (mType == eSuccess) {
691
0
      swm->UpdateInternal(mPrincipal, mScope, callback);
692
0
      return NS_OK;
693
0
    }
694
0
695
0
    ErrorResult error(NS_ERROR_DOM_ABORT_ERR);
696
0
    callback->UpdateFailed(error);
697
0
    return NS_OK;
698
0
  }
699
700
  nsresult
701
  Cancel() override
702
0
  {
703
0
    mPromise = nullptr;
704
0
    return NS_OK;
705
0
  }
706
707
private:
708
  ~UpdateRunnable()
709
0
  {
710
0
    if (mPromise) {
711
0
      mPromise->Resolve(true, __func__);
712
0
    }
713
0
  }
714
715
  nsCOMPtr<nsIPrincipal> mPrincipal;
716
  const nsCString mScope;
717
  RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
718
  Type mType;
719
720
  RefPtr<GenericPromise::Private> mPromise;
721
};
722
723
class ResolvePromiseRunnable final : public CancelableRunnable
724
{
725
public:
726
  explicit ResolvePromiseRunnable(GenericPromise::Private* aPromise)
727
    : CancelableRunnable("dom::ServiceWorkerManager::ResolvePromiseRunnable")
728
    , mPromise(aPromise)
729
0
  {}
730
731
  NS_IMETHOD
732
  Run() override
733
0
  {
734
0
    MaybeResolve();
735
0
    return NS_OK;
736
0
  }
737
738
  nsresult
739
  Cancel() override
740
0
  {
741
0
    mPromise = nullptr;
742
0
    return NS_OK;
743
0
  }
744
745
private:
746
  ~ResolvePromiseRunnable()
747
0
  {
748
0
    MaybeResolve();
749
0
  }
750
751
  void
752
  MaybeResolve()
753
0
  {
754
0
    if (mPromise) {
755
0
      mPromise->Resolve(true, __func__);
756
0
      mPromise = nullptr;
757
0
    }
758
0
  }
759
760
  RefPtr<GenericPromise::Private> mPromise;
761
};
762
763
} // namespace
764
765
RefPtr<ServiceWorkerRegistrationPromise>
766
ServiceWorkerManager::Register(const ClientInfo& aClientInfo,
767
                               const nsACString& aScopeURL,
768
                               const nsACString& aScriptURL,
769
                               ServiceWorkerUpdateViaCache aUpdateViaCache)
770
0
{
771
0
  nsCOMPtr<nsIURI> scopeURI;
772
0
  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScopeURL, nullptr, nullptr);
773
0
  if (NS_FAILED(rv)) {
774
0
    return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
775
0
  }
776
0
777
0
  nsCOMPtr<nsIURI> scriptURI;
778
0
  rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, nullptr);
779
0
  if (NS_FAILED(rv)) {
780
0
    return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
781
0
  }
782
0
783
0
  rv = ServiceWorkerScopeAndScriptAreValid(aClientInfo, scopeURI, scriptURI);
784
0
  if (NS_FAILED(rv)) {
785
0
    return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
786
0
  }
787
0
788
0
  // If the previous validation step passed then we must have a principal.
789
0
  nsCOMPtr<nsIPrincipal> principal = aClientInfo.GetPrincipal();
790
0
791
0
  nsAutoCString scopeKey;
792
0
  rv = PrincipalToScopeKey(principal, scopeKey);
793
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
794
0
    return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
795
0
  }
796
0
797
0
  RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
798
0
                                                            aScopeURL);
799
0
800
0
  RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb =
801
0
    new ServiceWorkerResolveWindowPromiseOnRegisterCallback();
802
0
803
0
  nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
804
0
  RefPtr<ServiceWorkerRegisterJob> job = new ServiceWorkerRegisterJob(
805
0
    principal, aScopeURL, aScriptURL, loadGroup,
806
0
    static_cast<ServiceWorkerUpdateViaCache>(aUpdateViaCache)
807
0
  );
808
0
809
0
  job->AppendResultCallback(cb);
810
0
  queue->ScheduleJob(job);
811
0
812
0
  MOZ_ASSERT(NS_IsMainThread());
813
0
  Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REGISTRATIONS, 1);
814
0
815
0
  return cb->Promise();
816
0
}
817
818
/*
819
 * Implements the async aspects of the getRegistrations algorithm.
820
 */
821
class GetRegistrationsRunnable final : public Runnable
822
{
823
  const ClientInfo mClientInfo;
824
  RefPtr<ServiceWorkerRegistrationListPromise::Private> mPromise;
825
public:
826
  explicit GetRegistrationsRunnable(const ClientInfo& aClientInfo)
827
    : Runnable("dom::ServiceWorkerManager::GetRegistrationsRunnable")
828
    , mClientInfo(aClientInfo)
829
    , mPromise(new ServiceWorkerRegistrationListPromise::Private(__func__))
830
0
  {}
831
832
  RefPtr<ServiceWorkerRegistrationListPromise>
833
  Promise() const
834
0
  {
835
0
    return mPromise;
836
0
  }
837
838
  NS_IMETHOD
839
  Run() override
840
0
  {
841
0
    auto scopeExit = MakeScopeExit([&] {
842
0
      mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
843
0
    });
844
0
845
0
    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
846
0
    if (!swm) {
847
0
      return NS_OK;
848
0
    }
849
0
850
0
    nsCOMPtr<nsIPrincipal> principal = mClientInfo.GetPrincipal();
851
0
    if (!principal) {
852
0
      return NS_OK;
853
0
    }
854
0
855
0
    nsTArray<ServiceWorkerRegistrationDescriptor> array;
856
0
857
0
    if (NS_WARN_IF(!BasePrincipal::Cast(principal)->IsCodebasePrincipal())) {
858
0
      return NS_OK;
859
0
    }
860
0
861
0
    nsAutoCString scopeKey;
862
0
    nsresult rv = swm->PrincipalToScopeKey(principal, scopeKey);
863
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
864
0
      return rv;
865
0
    }
866
0
867
0
    ServiceWorkerManager::RegistrationDataPerPrincipal* data;
868
0
    if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
869
0
      scopeExit.release();
870
0
      mPromise->Resolve(array, __func__);
871
0
      return NS_OK;
872
0
    }
873
0
874
0
    for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) {
875
0
      RefPtr<ServiceWorkerRegistrationInfo> info =
876
0
        data->mInfos.GetWeak(data->mOrderedScopes[i]);
877
0
      if (info->IsPendingUninstall()) {
878
0
        continue;
879
0
      }
880
0
881
0
      NS_ConvertUTF8toUTF16 scope(data->mOrderedScopes[i]);
882
0
883
0
      nsCOMPtr<nsIURI> scopeURI;
884
0
      nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope, nullptr, nullptr);
885
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
886
0
        break;
887
0
      }
888
0
889
0
      rv = principal->CheckMayLoad(scopeURI, true /* report */,
890
0
                                   false /* allowIfInheritsPrincipal */);
891
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
892
0
        continue;
893
0
      }
894
0
895
0
      array.AppendElement(info->Descriptor());
896
0
    }
897
0
898
0
    scopeExit.release();
899
0
    mPromise->Resolve(array, __func__);
900
0
901
0
    return NS_OK;
902
0
  }
903
};
904
905
RefPtr<ServiceWorkerRegistrationListPromise>
906
ServiceWorkerManager::GetRegistrations(const ClientInfo& aClientInfo) const
907
0
{
908
0
  RefPtr<GetRegistrationsRunnable> runnable =
909
0
    new GetRegistrationsRunnable(aClientInfo);
910
0
  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable));
911
0
  return runnable->Promise();;
912
0
}
913
914
/*
915
 * Implements the async aspects of the getRegistration algorithm.
916
 */
917
class GetRegistrationRunnable final : public Runnable
918
{
919
  const ClientInfo mClientInfo;
920
  RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
921
  nsCString mURL;
922
923
public:
924
  GetRegistrationRunnable(const ClientInfo& aClientInfo,
925
                          const nsACString& aURL)
926
    : Runnable("dom::ServiceWorkerManager::GetRegistrationRunnable")
927
    , mClientInfo(aClientInfo)
928
    , mPromise(new ServiceWorkerRegistrationPromise::Private(__func__))
929
    , mURL(aURL)
930
0
  {}
931
932
  RefPtr<ServiceWorkerRegistrationPromise>
933
  Promise() const
934
0
  {
935
0
    return mPromise;
936
0
  }
937
938
  NS_IMETHOD
939
  Run() override
940
0
  {
941
0
    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
942
0
    if (!swm) {
943
0
      mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
944
0
      return NS_OK;
945
0
    }
946
0
947
0
    nsCOMPtr<nsIPrincipal> principal = mClientInfo.GetPrincipal();
948
0
    if (!principal) {
949
0
      mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
950
0
      return NS_OK;
951
0
    }
952
0
953
0
    nsCOMPtr<nsIURI> uri;
954
0
    nsresult rv = NS_NewURI(getter_AddRefs(uri), mURL, nullptr, nullptr);
955
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
956
0
      mPromise->Reject(rv, __func__);
957
0
      return NS_OK;
958
0
    }
959
0
960
0
    rv = principal->CheckMayLoad(uri, true /* report */,
961
0
                                 false /* allowIfInheritsPrinciple */);
962
0
    if (NS_FAILED(rv)) {
963
0
      mPromise->Reject(NS_ERROR_DOM_SECURITY_ERR, __func__);
964
0
      return NS_OK;
965
0
    }
966
0
967
0
    RefPtr<ServiceWorkerRegistrationInfo> registration =
968
0
      swm->GetServiceWorkerRegistrationInfo(principal, uri);
969
0
970
0
    if (!registration) {
971
0
      // Reject with NS_OK means "not found".
972
0
      mPromise->Reject(NS_OK, __func__);
973
0
      return NS_OK;
974
0
    }
975
0
976
0
    mPromise->Resolve(registration->Descriptor(), __func__);
977
0
978
0
    return NS_OK;
979
0
  }
980
};
981
982
RefPtr<ServiceWorkerRegistrationPromise>
983
ServiceWorkerManager::GetRegistration(const ClientInfo& aClientInfo,
984
                                      const nsACString& aURL) const
985
0
{
986
0
  MOZ_ASSERT(NS_IsMainThread());
987
0
988
0
  RefPtr<GetRegistrationRunnable> runnable =
989
0
    new GetRegistrationRunnable(aClientInfo, aURL);
990
0
  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable));
991
0
992
0
  return runnable->Promise();
993
0
}
994
995
NS_IMETHODIMP
996
ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes,
997
                                    const nsACString& aScope,
998
                                    uint32_t aDataLength,
999
                                    uint8_t* aDataBytes,
1000
                                    uint8_t optional_argc)
1001
0
{
1002
0
  if (optional_argc == 2) {
1003
0
    nsTArray<uint8_t> data;
1004
0
    if (!data.InsertElementsAt(0, aDataBytes, aDataLength, fallible)) {
1005
0
      return NS_ERROR_OUT_OF_MEMORY;
1006
0
    }
1007
0
    return SendPushEvent(aOriginAttributes, aScope, EmptyString(), Some(data));
1008
0
  }
1009
0
  MOZ_ASSERT(optional_argc == 0);
1010
0
  return SendPushEvent(aOriginAttributes, aScope, EmptyString(), Nothing());
1011
0
}
1012
1013
nsresult
1014
ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes,
1015
                                    const nsACString& aScope,
1016
                                    const nsAString& aMessageId,
1017
                                    const Maybe<nsTArray<uint8_t>>& aData)
1018
0
{
1019
0
  OriginAttributes attrs;
1020
0
  if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
1021
0
    return NS_ERROR_INVALID_ARG;
1022
0
  }
1023
0
1024
0
  ServiceWorkerInfo* serviceWorker = GetActiveWorkerInfoForScope(attrs, aScope);
1025
0
  if (NS_WARN_IF(!serviceWorker)) {
1026
0
    return NS_ERROR_FAILURE;
1027
0
  }
1028
0
1029
0
  RefPtr<ServiceWorkerRegistrationInfo> registration =
1030
0
    GetRegistration(serviceWorker->Principal(), aScope);
1031
0
  MOZ_DIAGNOSTIC_ASSERT(registration);
1032
0
1033
0
  return serviceWorker->WorkerPrivate()->SendPushEvent(aMessageId, aData,
1034
0
                                                       registration);
1035
0
}
1036
1037
NS_IMETHODIMP
1038
ServiceWorkerManager::SendPushSubscriptionChangeEvent(const nsACString& aOriginAttributes,
1039
                                                      const nsACString& aScope)
1040
0
{
1041
0
  OriginAttributes attrs;
1042
0
  if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
1043
0
    return NS_ERROR_INVALID_ARG;
1044
0
  }
1045
0
1046
0
  ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
1047
0
  if (!info) {
1048
0
    return NS_ERROR_FAILURE;
1049
0
  }
1050
0
  return info->WorkerPrivate()->SendPushSubscriptionChangeEvent();
1051
0
}
1052
1053
nsresult
1054
ServiceWorkerManager::SendNotificationEvent(const nsAString& aEventName,
1055
                                            const nsACString& aOriginSuffix,
1056
                                            const nsACString& aScope,
1057
                                            const nsAString& aID,
1058
                                            const nsAString& aTitle,
1059
                                            const nsAString& aDir,
1060
                                            const nsAString& aLang,
1061
                                            const nsAString& aBody,
1062
                                            const nsAString& aTag,
1063
                                            const nsAString& aIcon,
1064
                                            const nsAString& aData,
1065
                                            const nsAString& aBehavior)
1066
0
{
1067
0
  OriginAttributes attrs;
1068
0
  if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
1069
0
    return NS_ERROR_INVALID_ARG;
1070
0
  }
1071
0
1072
0
  ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
1073
0
  if (!info) {
1074
0
    return NS_ERROR_FAILURE;
1075
0
  }
1076
0
1077
0
  ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate();
1078
0
  return workerPrivate->SendNotificationEvent(aEventName, aID, aTitle, aDir,
1079
0
                                              aLang, aBody, aTag,
1080
0
                                              aIcon, aData, aBehavior,
1081
0
                                              NS_ConvertUTF8toUTF16(aScope));
1082
0
}
1083
1084
NS_IMETHODIMP
1085
ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix,
1086
                                                 const nsACString& aScope,
1087
                                                 const nsAString& aID,
1088
                                                 const nsAString& aTitle,
1089
                                                 const nsAString& aDir,
1090
                                                 const nsAString& aLang,
1091
                                                 const nsAString& aBody,
1092
                                                 const nsAString& aTag,
1093
                                                 const nsAString& aIcon,
1094
                                                 const nsAString& aData,
1095
                                                 const nsAString& aBehavior)
1096
0
{
1097
0
  return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLICK_EVENT_NAME),
1098
0
                               aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
1099
0
                               aBody, aTag, aIcon, aData, aBehavior);
1100
0
}
1101
1102
NS_IMETHODIMP
1103
ServiceWorkerManager::SendNotificationCloseEvent(const nsACString& aOriginSuffix,
1104
                                                 const nsACString& aScope,
1105
                                                 const nsAString& aID,
1106
                                                 const nsAString& aTitle,
1107
                                                 const nsAString& aDir,
1108
                                                 const nsAString& aLang,
1109
                                                 const nsAString& aBody,
1110
                                                 const nsAString& aTag,
1111
                                                 const nsAString& aIcon,
1112
                                                 const nsAString& aData,
1113
                                                 const nsAString& aBehavior)
1114
0
{
1115
0
  return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLOSE_EVENT_NAME),
1116
0
                               aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
1117
0
                               aBody, aTag, aIcon, aData, aBehavior);
1118
0
}
1119
1120
RefPtr<ServiceWorkerRegistrationPromise>
1121
ServiceWorkerManager::WhenReady(const ClientInfo& aClientInfo)
1122
0
{
1123
0
  AssertIsOnMainThread();
1124
0
1125
0
  for (auto &prd : mPendingReadyList) {
1126
0
    if (prd->mClientHandle->Info().Id() == aClientInfo.Id() &&
1127
0
        prd->mClientHandle->Info().PrincipalInfo() == aClientInfo.PrincipalInfo()) {
1128
0
      return prd->mPromise;
1129
0
    }
1130
0
  }
1131
0
1132
0
  RefPtr<ServiceWorkerRegistrationInfo> reg =
1133
0
    GetServiceWorkerRegistrationInfo(aClientInfo);
1134
0
  if (reg && reg->GetActive()) {
1135
0
    return ServiceWorkerRegistrationPromise::CreateAndResolve(reg->Descriptor(),
1136
0
                                                              __func__);
1137
0
  }
1138
0
1139
0
  nsCOMPtr<nsISerialEventTarget> target =
1140
0
    SystemGroup::EventTargetFor(TaskCategory::Other);
1141
0
1142
0
  RefPtr<ClientHandle> handle = ClientManager::CreateHandle(aClientInfo, target);
1143
0
  mPendingReadyList.AppendElement(MakeUnique<PendingReadyData>(handle));
1144
0
1145
0
  RefPtr<ServiceWorkerManager> self(this);
1146
0
  handle->OnDetach()->Then( target, __func__,
1147
0
    [self = std::move(self), aClientInfo] {
1148
0
      self->RemovePendingReadyPromise(aClientInfo);
1149
0
    });
1150
0
1151
0
  return mPendingReadyList.LastElement()->mPromise;
1152
0
}
1153
1154
void
1155
ServiceWorkerManager::CheckPendingReadyPromises()
1156
0
{
1157
0
  nsTArray<UniquePtr<PendingReadyData>> pendingReadyList;
1158
0
  mPendingReadyList.SwapElements(pendingReadyList);
1159
0
  for (uint32_t i = 0; i < pendingReadyList.Length(); ++i) {
1160
0
    UniquePtr<PendingReadyData> prd(std::move(pendingReadyList[i]));
1161
0
1162
0
    RefPtr<ServiceWorkerRegistrationInfo> reg =
1163
0
      GetServiceWorkerRegistrationInfo(prd->mClientHandle->Info());
1164
0
1165
0
    if (reg && reg->GetActive()) {
1166
0
      prd->mPromise->Resolve(reg->Descriptor(), __func__);
1167
0
    } else {
1168
0
      mPendingReadyList.AppendElement(std::move(prd));
1169
0
    }
1170
0
  }
1171
0
}
1172
1173
void
1174
ServiceWorkerManager::RemovePendingReadyPromise(const ClientInfo& aClientInfo)
1175
0
{
1176
0
  nsTArray<UniquePtr<PendingReadyData>> pendingReadyList;
1177
0
  mPendingReadyList.SwapElements(pendingReadyList);
1178
0
  for (uint32_t i = 0; i < pendingReadyList.Length(); ++i) {
1179
0
    UniquePtr<PendingReadyData> prd(std::move(pendingReadyList[i]));
1180
0
1181
0
    if (prd->mClientHandle->Info().Id() == aClientInfo.Id() &&
1182
0
        prd->mClientHandle->Info().PrincipalInfo() == aClientInfo.PrincipalInfo()) {
1183
0
      prd->mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
1184
0
    } else {
1185
0
      mPendingReadyList.AppendElement(std::move(prd));
1186
0
    }
1187
0
  }
1188
0
}
1189
1190
void
1191
ServiceWorkerManager::NoteInheritedController(const ClientInfo& aClientInfo,
1192
                                              const ServiceWorkerDescriptor& aController)
1193
0
{
1194
0
  MOZ_ASSERT(NS_IsMainThread());
1195
0
1196
0
  nsCOMPtr<nsIPrincipal> principal =
1197
0
    PrincipalInfoToPrincipal(aController.PrincipalInfo());
1198
0
  NS_ENSURE_TRUE_VOID(principal);
1199
0
1200
0
  nsCOMPtr<nsIURI> scope;
1201
0
  nsresult rv =
1202
0
    NS_NewURI(getter_AddRefs(scope), aController.Scope(), nullptr, nullptr);
1203
0
  NS_ENSURE_SUCCESS_VOID(rv);
1204
0
1205
0
  RefPtr<ServiceWorkerRegistrationInfo> registration =
1206
0
    GetServiceWorkerRegistrationInfo(principal, scope);
1207
0
  NS_ENSURE_TRUE_VOID(registration);
1208
0
  NS_ENSURE_TRUE_VOID(registration->GetActive());
1209
0
1210
0
  StartControllingClient(aClientInfo, registration, false /* aControlClientHandle */);
1211
0
}
1212
1213
ServiceWorkerInfo*
1214
ServiceWorkerManager::GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes,
1215
                                                  const nsACString& aScope)
1216
0
{
1217
0
  MOZ_ASSERT(NS_IsMainThread());
1218
0
1219
0
  nsCOMPtr<nsIURI> scopeURI;
1220
0
  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
1221
0
  if (NS_FAILED(rv)) {
1222
0
    return nullptr;
1223
0
  }
1224
0
  nsCOMPtr<nsIPrincipal> principal =
1225
0
    BasePrincipal::CreateCodebasePrincipal(scopeURI, aOriginAttributes);
1226
0
  RefPtr<ServiceWorkerRegistrationInfo> registration =
1227
0
    GetServiceWorkerRegistrationInfo(principal, scopeURI);
1228
0
  if (!registration) {
1229
0
    return nullptr;
1230
0
  }
1231
0
1232
0
  return registration->GetActive();
1233
0
}
1234
1235
namespace {
1236
1237
class UnregisterJobCallback final : public ServiceWorkerJob::Callback
1238
{
1239
  nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
1240
1241
  ~UnregisterJobCallback()
1242
0
  {
1243
0
  }
1244
1245
public:
1246
  explicit UnregisterJobCallback(nsIServiceWorkerUnregisterCallback* aCallback)
1247
    : mCallback(aCallback)
1248
0
  {
1249
0
    MOZ_ASSERT(NS_IsMainThread());
1250
0
    MOZ_ASSERT(mCallback);
1251
0
  }
1252
1253
  void
1254
  JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override
1255
0
  {
1256
0
    MOZ_ASSERT(NS_IsMainThread());
1257
0
    MOZ_ASSERT(aJob);
1258
0
1259
0
    if (aStatus.Failed()) {
1260
0
      mCallback->UnregisterFailed();
1261
0
      return;
1262
0
    }
1263
0
1264
0
    MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Unregister);
1265
0
    RefPtr<ServiceWorkerUnregisterJob> unregisterJob =
1266
0
      static_cast<ServiceWorkerUnregisterJob*>(aJob);
1267
0
    mCallback->UnregisterSucceeded(unregisterJob->GetResult());
1268
0
  }
1269
1270
  NS_INLINE_DECL_REFCOUNTING(UnregisterJobCallback, override)
1271
};
1272
1273
} // anonymous namespace
1274
1275
NS_IMETHODIMP
1276
ServiceWorkerManager::Unregister(nsIPrincipal* aPrincipal,
1277
                                 nsIServiceWorkerUnregisterCallback* aCallback,
1278
                                 const nsAString& aScope)
1279
0
{
1280
0
  MOZ_ASSERT(NS_IsMainThread());
1281
0
1282
0
  if (!aPrincipal) {
1283
0
    return NS_ERROR_FAILURE;
1284
0
  }
1285
0
1286
0
  nsresult rv;
1287
0
1288
0
// This is not accessible by content, and callers should always ensure scope is
1289
0
// a correct URI, so this is wrapped in DEBUG
1290
#ifdef DEBUG
1291
  nsCOMPtr<nsIURI> scopeURI;
1292
  rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
1293
  if (NS_WARN_IF(NS_FAILED(rv))) {
1294
    return NS_ERROR_DOM_SECURITY_ERR;
1295
  }
1296
#endif
1297
1298
0
  nsAutoCString scopeKey;
1299
0
  rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1300
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1301
0
    return rv;
1302
0
  }
1303
0
1304
0
  NS_ConvertUTF16toUTF8 scope(aScope);
1305
0
  RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope);
1306
0
1307
0
  RefPtr<ServiceWorkerUnregisterJob> job =
1308
0
    new ServiceWorkerUnregisterJob(aPrincipal, scope, true /* send to parent */);
1309
0
1310
0
  if (aCallback) {
1311
0
    RefPtr<UnregisterJobCallback> cb = new UnregisterJobCallback(aCallback);
1312
0
    job->AppendResultCallback(cb);
1313
0
  }
1314
0
1315
0
  queue->ScheduleJob(job);
1316
0
  return NS_OK;
1317
0
}
1318
1319
nsresult
1320
ServiceWorkerManager::NotifyUnregister(nsIPrincipal* aPrincipal,
1321
                                       const nsAString& aScope)
1322
0
{
1323
0
  MOZ_ASSERT(NS_IsMainThread());
1324
0
  MOZ_ASSERT(aPrincipal);
1325
0
1326
0
  nsresult rv;
1327
0
1328
0
// This is not accessible by content, and callers should always ensure scope is
1329
0
// a correct URI, so this is wrapped in DEBUG
1330
#ifdef DEBUG
1331
  nsCOMPtr<nsIURI> scopeURI;
1332
  rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
1333
  if (NS_WARN_IF(NS_FAILED(rv))) {
1334
    return rv;
1335
  }
1336
#endif
1337
1338
0
  nsAutoCString scopeKey;
1339
0
  rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1340
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1341
0
    return rv;
1342
0
  }
1343
0
1344
0
  NS_ConvertUTF16toUTF8 scope(aScope);
1345
0
  RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope);
1346
0
1347
0
  RefPtr<ServiceWorkerUnregisterJob> job =
1348
0
    new ServiceWorkerUnregisterJob(aPrincipal, scope,
1349
0
                                    false /* send to parent */);
1350
0
1351
0
  queue->ScheduleJob(job);
1352
0
  return NS_OK;
1353
0
}
1354
1355
void
1356
ServiceWorkerManager::WorkerIsIdle(ServiceWorkerInfo* aWorker)
1357
0
{
1358
0
  MOZ_ASSERT(NS_IsMainThread());
1359
0
  MOZ_DIAGNOSTIC_ASSERT(aWorker);
1360
0
1361
0
  RefPtr<ServiceWorkerRegistrationInfo> reg =
1362
0
    GetRegistration(aWorker->Principal(), aWorker->Scope());
1363
0
  if (!reg) {
1364
0
    return;
1365
0
  }
1366
0
1367
0
  if (reg->GetActive() != aWorker) {
1368
0
    return;
1369
0
  }
1370
0
1371
0
  if (!reg->IsControllingClients() && reg->IsPendingUninstall()) {
1372
0
    RemoveRegistration(reg);
1373
0
    return;
1374
0
  }
1375
0
1376
0
  reg->TryToActivateAsync();
1377
0
}
1378
1379
already_AddRefed<ServiceWorkerJobQueue>
1380
ServiceWorkerManager::GetOrCreateJobQueue(const nsACString& aKey,
1381
                                          const nsACString& aScope)
1382
0
{
1383
0
  MOZ_ASSERT(!aKey.IsEmpty());
1384
0
  ServiceWorkerManager::RegistrationDataPerPrincipal* data;
1385
0
  // XXX we could use LookupForAdd here to avoid a hashtable lookup, except that
1386
0
  // leads to a false positive assertion, see bug 1370674 comment 7.
1387
0
  if (!mRegistrationInfos.Get(aKey, &data)) {
1388
0
    data = new RegistrationDataPerPrincipal();
1389
0
    mRegistrationInfos.Put(aKey, data);
1390
0
  }
1391
0
1392
0
  RefPtr<ServiceWorkerJobQueue> queue =
1393
0
    data->mJobQueues.LookupForAdd(aScope).OrInsert(
1394
0
      []() { return new ServiceWorkerJobQueue(); });
1395
0
1396
0
  return queue.forget();
1397
0
}
1398
1399
/* static */
1400
already_AddRefed<ServiceWorkerManager>
1401
ServiceWorkerManager::GetInstance()
1402
0
{
1403
0
  // Note: We don't simply check gInstance for null-ness here, since otherwise
1404
0
  // this can resurrect the ServiceWorkerManager pretty late during shutdown.
1405
0
  static bool firstTime = true;
1406
0
  if (firstTime) {
1407
0
    RefPtr<ServiceWorkerRegistrar> swr;
1408
0
1409
0
    // Don't create the ServiceWorkerManager until the ServiceWorkerRegistrar is
1410
0
    // initialized.
1411
0
    if (XRE_IsParentProcess()) {
1412
0
      swr = ServiceWorkerRegistrar::Get();
1413
0
      if (!swr) {
1414
0
        return nullptr;
1415
0
      }
1416
0
    }
1417
0
1418
0
    firstTime = false;
1419
0
1420
0
    MOZ_ASSERT(NS_IsMainThread());
1421
0
1422
0
    gInstance = new ServiceWorkerManager();
1423
0
    gInstance->Init(swr);
1424
0
    ClearOnShutdown(&gInstance);
1425
0
  }
1426
0
  RefPtr<ServiceWorkerManager> copy = gInstance.get();
1427
0
  return copy.forget();
1428
0
}
1429
1430
void
1431
ServiceWorkerManager::FinishFetch(ServiceWorkerRegistrationInfo* aRegistration)
1432
0
{
1433
0
}
1434
1435
void
1436
ServiceWorkerManager::ReportToAllClients(const nsCString& aScope,
1437
                                         const nsString& aMessage,
1438
                                         const nsString& aFilename,
1439
                                         const nsString& aLine,
1440
                                         uint32_t aLineNumber,
1441
                                         uint32_t aColumnNumber,
1442
                                         uint32_t aFlags)
1443
0
{
1444
0
  ConsoleUtils::ReportForServiceWorkerScope(NS_ConvertUTF8toUTF16(aScope),
1445
0
                                            aMessage,
1446
0
                                            aFilename,
1447
0
                                            aLineNumber,
1448
0
                                            aColumnNumber,
1449
0
                                            ConsoleUtils::eError);
1450
0
}
1451
1452
/* static */
1453
void
1454
ServiceWorkerManager::LocalizeAndReportToAllClients(
1455
                                          const nsCString& aScope,
1456
                                          const char* aStringKey,
1457
                                          const nsTArray<nsString>& aParamArray,
1458
                                          uint32_t aFlags,
1459
                                          const nsString& aFilename,
1460
                                          const nsString& aLine,
1461
                                          uint32_t aLineNumber,
1462
                                          uint32_t aColumnNumber)
1463
0
{
1464
0
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1465
0
  if (!swm) {
1466
0
    return;
1467
0
  }
1468
0
1469
0
  nsresult rv;
1470
0
  nsAutoString message;
1471
0
  rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1472
0
                                             aStringKey, aParamArray, message);
1473
0
  if (NS_SUCCEEDED(rv)) {
1474
0
    swm->ReportToAllClients(aScope, message,
1475
0
                            aFilename, aLine, aLineNumber, aColumnNumber,
1476
0
                            aFlags);
1477
0
  } else {
1478
0
    NS_WARNING("Failed to format and therefore report localized error.");
1479
0
  }
1480
0
}
1481
1482
void
1483
ServiceWorkerManager::HandleError(JSContext* aCx,
1484
                                  nsIPrincipal* aPrincipal,
1485
                                  const nsCString& aScope,
1486
                                  const nsString& aWorkerURL,
1487
                                  const nsString& aMessage,
1488
                                  const nsString& aFilename,
1489
                                  const nsString& aLine,
1490
                                  uint32_t aLineNumber,
1491
                                  uint32_t aColumnNumber,
1492
                                  uint32_t aFlags,
1493
                                  JSExnType aExnType)
1494
0
{
1495
0
  MOZ_ASSERT(NS_IsMainThread());
1496
0
  MOZ_ASSERT(aPrincipal);
1497
0
1498
0
  nsAutoCString scopeKey;
1499
0
  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1500
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1501
0
    return;
1502
0
  }
1503
0
1504
0
  ServiceWorkerManager::RegistrationDataPerPrincipal* data;
1505
0
  if (NS_WARN_IF(!mRegistrationInfos.Get(scopeKey, &data))) {
1506
0
    return;
1507
0
  }
1508
0
1509
0
  // Always report any uncaught exceptions or errors to the console of
1510
0
  // each client.
1511
0
  ReportToAllClients(aScope, aMessage, aFilename, aLine, aLineNumber,
1512
0
                     aColumnNumber, aFlags);
1513
0
}
1514
1515
void
1516
ServiceWorkerManager::LoadRegistration(
1517
                             const ServiceWorkerRegistrationData& aRegistration)
1518
0
{
1519
0
  MOZ_ASSERT(NS_IsMainThread());
1520
0
1521
0
  nsCOMPtr<nsIPrincipal> principal =
1522
0
    PrincipalInfoToPrincipal(aRegistration.principal());
1523
0
  if (!principal) {
1524
0
    return;
1525
0
  }
1526
0
1527
0
  RefPtr<ServiceWorkerRegistrationInfo> registration =
1528
0
    GetRegistration(principal, aRegistration.scope());
1529
0
  if (!registration) {
1530
0
    registration =
1531
0
      CreateNewRegistration(
1532
0
        aRegistration.scope(),
1533
0
        principal,
1534
0
        static_cast<ServiceWorkerUpdateViaCache>(aRegistration.updateViaCache())
1535
0
      );
1536
0
  } else {
1537
0
    // If active worker script matches our expectations for a "current worker",
1538
0
    // then we are done. Since scripts with the same URL might have different
1539
0
    // contents such as updated scripts or scripts with different LoadFlags, we
1540
0
    // use the CacheName to judje whether the two scripts are identical, where
1541
0
    // the CacheName is an UUID generated when a new script is found.
1542
0
    if (registration->GetActive() &&
1543
0
        registration->GetActive()->CacheName() == aRegistration.cacheName()) {
1544
0
      // No needs for updates.
1545
0
      return;
1546
0
    }
1547
0
  }
1548
0
1549
0
  registration->SetLastUpdateTime(aRegistration.lastUpdateTime());
1550
0
1551
0
  nsLoadFlags importsLoadFlags = nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
1552
0
  importsLoadFlags |=
1553
0
    aRegistration.updateViaCache() == static_cast<uint16_t>(ServiceWorkerUpdateViaCache::None)
1554
0
      ? nsIRequest::LOAD_NORMAL
1555
0
      : nsIRequest::VALIDATE_ALWAYS;
1556
0
1557
0
  const nsCString& currentWorkerURL = aRegistration.currentWorkerURL();
1558
0
  if (!currentWorkerURL.IsEmpty()) {
1559
0
    registration->SetActive(
1560
0
      new ServiceWorkerInfo(registration->Principal(),
1561
0
                            registration->Scope(),
1562
0
                            registration->Id(),
1563
0
                            registration->Version(),
1564
0
                            currentWorkerURL,
1565
0
                            aRegistration.cacheName(),
1566
0
                            importsLoadFlags));
1567
0
    registration->GetActive()->SetHandlesFetch(aRegistration.currentWorkerHandlesFetch());
1568
0
    registration->GetActive()->SetInstalledTime(aRegistration.currentWorkerInstalledTime());
1569
0
    registration->GetActive()->SetActivatedTime(aRegistration.currentWorkerActivatedTime());
1570
0
  }
1571
0
}
1572
1573
void
1574
ServiceWorkerManager::LoadRegistrations(
1575
                  const nsTArray<ServiceWorkerRegistrationData>& aRegistrations)
1576
0
{
1577
0
  MOZ_ASSERT(NS_IsMainThread());
1578
0
1579
0
  for (uint32_t i = 0, len = aRegistrations.Length(); i < len; ++i) {
1580
0
    LoadRegistration(aRegistrations[i]);
1581
0
  }
1582
0
}
1583
1584
void
1585
ServiceWorkerManager::StoreRegistration(
1586
                                   nsIPrincipal* aPrincipal,
1587
                                   ServiceWorkerRegistrationInfo* aRegistration)
1588
0
{
1589
0
  MOZ_ASSERT(aPrincipal);
1590
0
  MOZ_ASSERT(aRegistration);
1591
0
1592
0
  if (mShuttingDown) {
1593
0
    return;
1594
0
  }
1595
0
1596
0
  ServiceWorkerRegistrationData data;
1597
0
  nsresult rv = PopulateRegistrationData(aPrincipal, aRegistration, data);
1598
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1599
0
    return;
1600
0
  }
1601
0
1602
0
  PrincipalInfo principalInfo;
1603
0
  if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
1604
0
                                                    &principalInfo)))) {
1605
0
    return;
1606
0
  }
1607
0
1608
0
  mActor->SendRegister(data);
1609
0
}
1610
1611
already_AddRefed<ServiceWorkerRegistrationInfo>
1612
ServiceWorkerManager::GetServiceWorkerRegistrationInfo(const ClientInfo& aClientInfo) const
1613
0
{
1614
0
  nsCOMPtr<nsIPrincipal> principal = aClientInfo.GetPrincipal();
1615
0
  NS_ENSURE_TRUE(principal, nullptr);
1616
0
1617
0
  nsCOMPtr<nsIURI> uri;
1618
0
  nsresult rv = NS_NewURI(getter_AddRefs(uri), aClientInfo.URL(),
1619
0
                          nullptr, nullptr);
1620
0
  NS_ENSURE_SUCCESS(rv, nullptr);
1621
0
1622
0
  return GetServiceWorkerRegistrationInfo(principal, uri);
1623
0
}
1624
1625
already_AddRefed<ServiceWorkerRegistrationInfo>
1626
ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal,
1627
                                                       nsIURI* aURI) const
1628
0
{
1629
0
  MOZ_ASSERT(aPrincipal);
1630
0
  MOZ_ASSERT(aURI);
1631
0
1632
0
  //XXXnsm Temporary fix until Bug 1171432 is fixed.
1633
0
  if (NS_WARN_IF(BasePrincipal::Cast(aPrincipal)->AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID)) {
1634
0
    return nullptr;
1635
0
  }
1636
0
1637
0
  nsAutoCString scopeKey;
1638
0
  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1639
0
  if (NS_FAILED(rv)) {
1640
0
    return nullptr;
1641
0
  }
1642
0
1643
0
  return GetServiceWorkerRegistrationInfo(scopeKey, aURI);
1644
0
}
1645
1646
already_AddRefed<ServiceWorkerRegistrationInfo>
1647
ServiceWorkerManager::GetServiceWorkerRegistrationInfo(const nsACString& aScopeKey,
1648
                                                       nsIURI* aURI) const
1649
0
{
1650
0
  MOZ_ASSERT(aURI);
1651
0
1652
0
  nsAutoCString spec;
1653
0
  nsresult rv = aURI->GetSpec(spec);
1654
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1655
0
    return nullptr;
1656
0
  }
1657
0
1658
0
  nsAutoCString scope;
1659
0
  RegistrationDataPerPrincipal* data;
1660
0
  if (!FindScopeForPath(aScopeKey, spec, &data, scope)) {
1661
0
    return nullptr;
1662
0
  }
1663
0
1664
0
  MOZ_ASSERT(data);
1665
0
1666
0
  RefPtr<ServiceWorkerRegistrationInfo> registration;
1667
0
  data->mInfos.Get(scope, getter_AddRefs(registration));
1668
0
  // ordered scopes and registrations better be in sync.
1669
0
  MOZ_ASSERT(registration);
1670
0
1671
#ifdef DEBUG
1672
  nsAutoCString origin;
1673
  rv = registration->Principal()->GetOrigin(origin);
1674
  MOZ_ASSERT(NS_SUCCEEDED(rv));
1675
  MOZ_ASSERT(origin.Equals(aScopeKey));
1676
#endif
1677
1678
0
  if (registration->IsPendingUninstall()) {
1679
0
    return nullptr;
1680
0
  }
1681
0
  return registration.forget();
1682
0
}
1683
1684
/* static */ nsresult
1685
ServiceWorkerManager::PrincipalToScopeKey(nsIPrincipal* aPrincipal,
1686
                                          nsACString& aKey)
1687
0
{
1688
0
  MOZ_ASSERT(aPrincipal);
1689
0
1690
0
  if (!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal()) {
1691
0
    return NS_ERROR_FAILURE;
1692
0
  }
1693
0
1694
0
  nsresult rv = aPrincipal->GetOrigin(aKey);
1695
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1696
0
    return rv;
1697
0
  }
1698
0
1699
0
  return NS_OK;
1700
0
}
1701
1702
/* static */ nsresult
1703
ServiceWorkerManager::PrincipalInfoToScopeKey(const PrincipalInfo& aPrincipalInfo,
1704
                                              nsACString& aKey)
1705
0
{
1706
0
  if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo) {
1707
0
    return NS_ERROR_FAILURE;
1708
0
  }
1709
0
1710
0
  auto content = aPrincipalInfo.get_ContentPrincipalInfo();
1711
0
1712
0
  nsAutoCString suffix;
1713
0
  content.attrs().CreateSuffix(suffix);
1714
0
1715
0
  aKey = content.originNoSuffix();
1716
0
  aKey.Append(suffix);
1717
0
1718
0
  return NS_OK;
1719
0
}
1720
1721
/* static */ void
1722
ServiceWorkerManager::AddScopeAndRegistration(const nsACString& aScope,
1723
                                              ServiceWorkerRegistrationInfo* aInfo)
1724
0
{
1725
0
  MOZ_ASSERT(aInfo);
1726
0
  MOZ_ASSERT(aInfo->Principal());
1727
0
1728
0
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1729
0
  if (!swm) {
1730
0
    // browser shutdown
1731
0
    return;
1732
0
  }
1733
0
1734
0
  nsAutoCString scopeKey;
1735
0
  nsresult rv = swm->PrincipalToScopeKey(aInfo->Principal(), scopeKey);
1736
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1737
0
    return;
1738
0
  }
1739
0
1740
0
  MOZ_ASSERT(!scopeKey.IsEmpty());
1741
0
1742
0
  RegistrationDataPerPrincipal* data =
1743
0
    swm->mRegistrationInfos.LookupForAdd(scopeKey).OrInsert(
1744
0
      []() { return new RegistrationDataPerPrincipal(); });
1745
0
1746
0
  for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) {
1747
0
    const nsCString& current = data->mOrderedScopes[i];
1748
0
1749
0
    // Perfect match!
1750
0
    if (aScope.Equals(current)) {
1751
0
      data->mInfos.Put(aScope, aInfo);
1752
0
      swm->NotifyListenersOnRegister(aInfo);
1753
0
      return;
1754
0
    }
1755
0
1756
0
    // Sort by length, with longest match first.
1757
0
    // /foo/bar should be before /foo/
1758
0
    // Similarly /foo/b is between the two.
1759
0
    if (StringBeginsWith(aScope, current)) {
1760
0
      data->mOrderedScopes.InsertElementAt(i, aScope);
1761
0
      data->mInfos.Put(aScope, aInfo);
1762
0
      swm->NotifyListenersOnRegister(aInfo);
1763
0
      return;
1764
0
    }
1765
0
  }
1766
0
1767
0
  data->mOrderedScopes.AppendElement(aScope);
1768
0
  data->mInfos.Put(aScope, aInfo);
1769
0
  swm->NotifyListenersOnRegister(aInfo);
1770
0
}
1771
1772
/* static */ bool
1773
ServiceWorkerManager::FindScopeForPath(const nsACString& aScopeKey,
1774
                                       const nsACString& aPath,
1775
                                       RegistrationDataPerPrincipal** aData,
1776
                                       nsACString& aMatch)
1777
0
{
1778
0
  MOZ_ASSERT(aData);
1779
0
1780
0
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1781
0
1782
0
  if (!swm || !swm->mRegistrationInfos.Get(aScopeKey, aData)) {
1783
0
    return false;
1784
0
  }
1785
0
1786
0
  for (uint32_t i = 0; i < (*aData)->mOrderedScopes.Length(); ++i) {
1787
0
    const nsCString& current = (*aData)->mOrderedScopes[i];
1788
0
    if (StringBeginsWith(aPath, current)) {
1789
0
      aMatch = current;
1790
0
      return true;
1791
0
    }
1792
0
  }
1793
0
1794
0
  return false;
1795
0
}
1796
1797
/* static */ bool
1798
ServiceWorkerManager::HasScope(nsIPrincipal* aPrincipal,
1799
                               const nsACString& aScope)
1800
0
{
1801
0
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1802
0
  if (!swm) {
1803
0
    return false;
1804
0
  }
1805
0
1806
0
  nsAutoCString scopeKey;
1807
0
  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1808
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1809
0
    return false;
1810
0
  }
1811
0
1812
0
  RegistrationDataPerPrincipal* data;
1813
0
  if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
1814
0
    return false;
1815
0
  }
1816
0
1817
0
  return data->mOrderedScopes.Contains(aScope);
1818
0
}
1819
1820
/* static */ void
1821
ServiceWorkerManager::RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo* aRegistration)
1822
0
{
1823
0
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1824
0
  if (!swm) {
1825
0
    return;
1826
0
  }
1827
0
1828
0
  nsAutoCString scopeKey;
1829
0
  nsresult rv = swm->PrincipalToScopeKey(aRegistration->Principal(), scopeKey);
1830
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1831
0
    return;
1832
0
  }
1833
0
1834
0
  RegistrationDataPerPrincipal* data;
1835
0
  if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
1836
0
    return;
1837
0
  }
1838
0
1839
0
  if (auto entry = data->mUpdateTimers.Lookup(aRegistration->Scope())) {
1840
0
    entry.Data()->Cancel();
1841
0
    entry.Remove();
1842
0
  }
1843
0
1844
0
  // Verify there are no controlled clients for the purged registration.
1845
0
  for (auto iter = swm->mControlledClients.Iter(); !iter.Done(); iter.Next()) {
1846
0
    auto& reg = iter.UserData()->mRegistrationInfo;
1847
0
    if (reg->Scope().Equals(aRegistration->Scope()) &&
1848
0
        reg->Principal()->Equals(aRegistration->Principal())) {
1849
0
      MOZ_DIAGNOSTIC_ASSERT(aRegistration->IsCorrupt(),
1850
0
                            "controlled client when removing non-corrupt registration");
1851
0
      iter.Remove();
1852
0
      break;
1853
0
    }
1854
0
  }
1855
0
1856
0
  RefPtr<ServiceWorkerRegistrationInfo> info;
1857
0
  data->mInfos.Remove(aRegistration->Scope(), getter_AddRefs(info));
1858
0
  data->mOrderedScopes.RemoveElement(aRegistration->Scope());
1859
0
  swm->NotifyListenersOnUnregister(info);
1860
0
1861
0
  swm->MaybeRemoveRegistrationInfo(scopeKey);
1862
0
  aRegistration->NotifyRemoved();
1863
0
}
1864
1865
void
1866
ServiceWorkerManager::MaybeRemoveRegistrationInfo(const nsACString& aScopeKey)
1867
0
{
1868
0
  if (auto entry = mRegistrationInfos.Lookup(aScopeKey)) {
1869
0
    if (entry.Data()->mOrderedScopes.IsEmpty() &&
1870
0
        entry.Data()->mJobQueues.Count() == 0) {
1871
0
      entry.Remove();
1872
0
    }
1873
0
  }
1874
0
}
1875
1876
bool
1877
ServiceWorkerManager::StartControlling(const ClientInfo& aClientInfo,
1878
                                       const ServiceWorkerDescriptor& aServiceWorker)
1879
0
{
1880
0
  MOZ_ASSERT(NS_IsMainThread());
1881
0
1882
0
  nsCOMPtr<nsIPrincipal> principal =
1883
0
    PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
1884
0
  NS_ENSURE_TRUE(principal, false);
1885
0
1886
0
  nsCOMPtr<nsIURI> scope;
1887
0
  nsresult rv =
1888
0
    NS_NewURI(getter_AddRefs(scope), aServiceWorker.Scope(), nullptr, nullptr);
1889
0
  NS_ENSURE_SUCCESS(rv, false);
1890
0
1891
0
  RefPtr<ServiceWorkerRegistrationInfo> registration =
1892
0
    GetServiceWorkerRegistrationInfo(principal, scope);
1893
0
  NS_ENSURE_TRUE(registration, false);
1894
0
  NS_ENSURE_TRUE(registration->GetActive(), false);
1895
0
1896
0
  StartControllingClient(aClientInfo, registration);
1897
0
1898
0
  return true;
1899
0
}
1900
1901
void
1902
ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo)
1903
0
{
1904
0
  MOZ_ASSERT(NS_IsMainThread());
1905
0
  // We perform these success path navigation update steps when the
1906
0
  // document tells us its more or less done loading.  This avoids
1907
0
  // slowing down page load and also lets pages consistently get
1908
0
  // updatefound events when they fire.
1909
0
  //
1910
0
  // 9.8.20 If respondWithEntered is false, then:
1911
0
  // 9.8.22 Else: (respondWith was entered and succeeded)
1912
0
  //    If request is a non-subresource request, then: Invoke Soft Update
1913
0
  //    algorithm.
1914
0
  ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
1915
0
  if (data && data->mRegistrationInfo) {
1916
0
    data->mRegistrationInfo->MaybeScheduleUpdate();
1917
0
  }
1918
0
}
1919
1920
void
1921
ServiceWorkerManager::StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration)
1922
0
{
1923
0
  aRegistration->StopControllingClient();
1924
0
  if (aRegistration->IsControllingClients() || !aRegistration->IsIdle()) {
1925
0
    return;
1926
0
  }
1927
0
1928
0
  if (aRegistration->IsPendingUninstall()) {
1929
0
    RemoveRegistration(aRegistration);
1930
0
    return;
1931
0
  }
1932
0
1933
0
  // We use to aggressively terminate the worker at this point, but it
1934
0
  // caused problems.  There are more uses for a service worker than actively
1935
0
  // controlled documents.  We need to let the worker naturally terminate
1936
0
  // in case its handling push events, message events, etc.
1937
0
  aRegistration->TryToActivateAsync();
1938
0
}
1939
1940
NS_IMETHODIMP
1941
ServiceWorkerManager::GetScopeForUrl(nsIPrincipal* aPrincipal,
1942
                                     const nsAString& aUrl, nsAString& aScope)
1943
0
{
1944
0
  MOZ_ASSERT(aPrincipal);
1945
0
1946
0
  nsCOMPtr<nsIURI> uri;
1947
0
  nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl, nullptr, nullptr);
1948
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1949
0
    return NS_ERROR_FAILURE;
1950
0
  }
1951
0
1952
0
  RefPtr<ServiceWorkerRegistrationInfo> r =
1953
0
    GetServiceWorkerRegistrationInfo(aPrincipal, uri);
1954
0
  if (!r) {
1955
0
      return NS_ERROR_FAILURE;
1956
0
  }
1957
0
1958
0
  aScope = NS_ConvertUTF8toUTF16(r->Scope());
1959
0
  return NS_OK;
1960
0
}
1961
1962
namespace {
1963
1964
class ContinueDispatchFetchEventRunnable : public Runnable
1965
{
1966
  RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
1967
  nsCOMPtr<nsIInterceptedChannel> mChannel;
1968
  nsCOMPtr<nsILoadGroup> mLoadGroup;
1969
  bool mIsReload;
1970
public:
1971
  ContinueDispatchFetchEventRunnable(
1972
    ServiceWorkerPrivate* aServiceWorkerPrivate,
1973
    nsIInterceptedChannel* aChannel,
1974
    nsILoadGroup* aLoadGroup,
1975
    bool aIsReload)
1976
    : Runnable("dom::ServiceWorkerManager::ContinueDispatchFetchEventRunnable")
1977
    , mServiceWorkerPrivate(aServiceWorkerPrivate)
1978
    , mChannel(aChannel)
1979
    , mLoadGroup(aLoadGroup)
1980
    , mIsReload(aIsReload)
1981
0
  {
1982
0
    MOZ_ASSERT(aServiceWorkerPrivate);
1983
0
    MOZ_ASSERT(aChannel);
1984
0
  }
1985
1986
  void
1987
  HandleError()
1988
0
  {
1989
0
    MOZ_ASSERT(NS_IsMainThread());
1990
0
    NS_WARNING("Unexpected error while dispatching fetch event!");
1991
0
    nsresult rv = mChannel->ResetInterception();
1992
0
    if (NS_FAILED(rv)) {
1993
0
      NS_WARNING("Failed to resume intercepted network request");
1994
0
      mChannel->CancelInterception(rv);
1995
0
    }
1996
0
  }
1997
1998
  NS_IMETHOD
1999
  Run() override
2000
0
  {
2001
0
    MOZ_ASSERT(NS_IsMainThread());
2002
0
2003
0
    nsCOMPtr<nsIChannel> channel;
2004
0
    nsresult rv = mChannel->GetChannel(getter_AddRefs(channel));
2005
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
2006
0
      HandleError();
2007
0
      return NS_OK;
2008
0
    }
2009
0
2010
0
    // The channel might have encountered an unexpected error while ensuring
2011
0
    // the upload stream is cloneable.  Check here and reset the interception
2012
0
    // if that happens.
2013
0
    nsresult status;
2014
0
    rv = channel->GetStatus(&status);
2015
0
    if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(status))) {
2016
0
      HandleError();
2017
0
      return NS_OK;
2018
0
    }
2019
0
2020
0
    nsString clientId;
2021
0
    nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
2022
0
    if (loadInfo) {
2023
0
      Maybe<ClientInfo> clientInfo = loadInfo->GetClientInfo();
2024
0
      if (clientInfo.isSome()) {
2025
0
        char buf[NSID_LENGTH];
2026
0
        clientInfo.ref().Id().ToProvidedString(buf);
2027
0
        NS_ConvertASCIItoUTF16 uuid(buf);
2028
0
2029
0
        // Remove {} and the null terminator
2030
0
        clientId.Assign(Substring(uuid, 1, NSID_LENGTH - 3));
2031
0
      }
2032
0
    }
2033
0
2034
0
    rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup, clientId,
2035
0
                                               mIsReload);
2036
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
2037
0
      HandleError();
2038
0
    }
2039
0
2040
0
    return NS_OK;
2041
0
  }
2042
};
2043
2044
} // anonymous namespace
2045
2046
void
2047
ServiceWorkerManager::DispatchFetchEvent(nsIInterceptedChannel* aChannel,
2048
                                         ErrorResult& aRv)
2049
0
{
2050
0
  MOZ_ASSERT(aChannel);
2051
0
  MOZ_ASSERT(NS_IsMainThread());
2052
0
2053
0
  nsCOMPtr<nsIChannel> internalChannel;
2054
0
  aRv = aChannel->GetChannel(getter_AddRefs(internalChannel));
2055
0
  if (NS_WARN_IF(aRv.Failed())) {
2056
0
    return;
2057
0
  }
2058
0
2059
0
  nsCOMPtr<nsILoadGroup> loadGroup;
2060
0
  aRv = internalChannel->GetLoadGroup(getter_AddRefs(loadGroup));
2061
0
  if (NS_WARN_IF(aRv.Failed())) {
2062
0
    return;
2063
0
  }
2064
0
2065
0
  nsCOMPtr<nsILoadInfo> loadInfo = internalChannel->GetLoadInfo();
2066
0
  if (NS_WARN_IF(!loadInfo)) {
2067
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
2068
0
    return;
2069
0
  }
2070
0
2071
0
  RefPtr<ServiceWorkerInfo> serviceWorker;
2072
0
2073
0
  if (!nsContentUtils::IsNonSubresourceRequest(internalChannel)) {
2074
0
    const Maybe<ServiceWorkerDescriptor>& controller = loadInfo->GetController();
2075
0
    if (NS_WARN_IF(controller.isNothing())) {
2076
0
      aRv.Throw(NS_ERROR_FAILURE);
2077
0
      return;
2078
0
    }
2079
0
2080
0
    RefPtr<ServiceWorkerRegistrationInfo> registration =
2081
0
      GetRegistration(controller.ref().PrincipalInfo(), controller.ref().Scope());
2082
0
    if (NS_WARN_IF(!registration)) {
2083
0
      aRv.Throw(NS_ERROR_FAILURE);
2084
0
      return;
2085
0
    }
2086
0
2087
0
    serviceWorker = registration->GetActive();
2088
0
    if (NS_WARN_IF(!serviceWorker) ||
2089
0
        NS_WARN_IF(serviceWorker->Descriptor().Id() != controller.ref().Id())) {
2090
0
      aRv.Throw(NS_ERROR_FAILURE);
2091
0
      return;
2092
0
    }
2093
0
  } else {
2094
0
    nsCOMPtr<nsIURI> uri;
2095
0
    aRv = aChannel->GetSecureUpgradedChannelURI(getter_AddRefs(uri));
2096
0
    if (NS_WARN_IF(aRv.Failed())) {
2097
0
      return;
2098
0
    }
2099
0
2100
0
    // non-subresource request means the URI contains the principal
2101
0
    nsCOMPtr<nsIPrincipal> principal =
2102
0
      BasePrincipal::CreateCodebasePrincipal(uri,
2103
0
                                             loadInfo->GetOriginAttributes());
2104
0
2105
0
    RefPtr<ServiceWorkerRegistrationInfo> registration =
2106
0
      GetServiceWorkerRegistrationInfo(principal, uri);
2107
0
    if (NS_WARN_IF(!registration)) {
2108
0
      aRv.Throw(NS_ERROR_FAILURE);
2109
0
      return;
2110
0
    }
2111
0
2112
0
    // While we only enter this method if IsAvailable() previously saw
2113
0
    // an active worker, it is possible for that worker to be removed
2114
0
    // before we get to this point.  Therefore we must handle a nullptr
2115
0
    // active worker here.
2116
0
    serviceWorker = registration->GetActive();
2117
0
    if (NS_WARN_IF(!serviceWorker)) {
2118
0
      aRv.Throw(NS_ERROR_FAILURE);
2119
0
      return;
2120
0
    }
2121
0
2122
0
    // If there is a reserved client it should be marked as controlled before
2123
0
    // the FetchEvent is dispatched.
2124
0
    Maybe<ClientInfo> clientInfo = loadInfo->GetReservedClientInfo();
2125
0
2126
0
    // Also override the initial about:blank controller since the real
2127
0
    // network load may be intercepted by a different service worker.  If
2128
0
    // the intial about:blank has a controller here its simply been
2129
0
    // inherited from its parent.
2130
0
    if (clientInfo.isNothing()) {
2131
0
      clientInfo = loadInfo->GetInitialClientInfo();
2132
0
2133
0
      // TODO: We need to handle the case where the initial about:blank is
2134
0
      //       controlled, but the final document load is not.  Right now
2135
0
      //       the spec does not really say what to do.  There currently
2136
0
      //       is no way for the controller to be cleared from a client in
2137
0
      //       the spec or our implementation.  We may want to force a
2138
0
      //       new inner window to be created instead of reusing the
2139
0
      //       initial about:blank global.  See bug 1419620 and the spec
2140
0
      //       issue here: https://github.com/w3c/ServiceWorker/issues/1232
2141
0
    }
2142
0
2143
0
    if (clientInfo.isSome()) {
2144
0
      // ClientChannelHelper is not called for STS upgrades that get
2145
0
      // intercepted by a service worker when interception occurs in
2146
0
      // the content process.  Therefore the reserved client is not
2147
0
      // properly cleared in that case leading to a situation where
2148
0
      // a ClientSource with an http:// principal is controlled by
2149
0
      // a ServiceWorker with an https:// principal.
2150
0
      //
2151
0
      // This does not occur when interception is handled by the
2152
0
      // simpler InterceptedHttpChannel approach in the parent.
2153
0
      //
2154
0
      // As a temporary work around check for this principal mismatch
2155
0
      // here and perform the ClientChannelHelper's replacement of
2156
0
      // reserved client automatically.
2157
0
      if (!XRE_IsParentProcess()) {
2158
0
        nsCOMPtr<nsIPrincipal> clientPrincipal = clientInfo.ref().GetPrincipal();
2159
0
        if (!clientPrincipal || !clientPrincipal->Equals(principal)) {
2160
0
          UniquePtr<ClientSource> reservedClient =
2161
0
            loadInfo->TakeReservedClientSource();
2162
0
2163
0
          nsCOMPtr<nsISerialEventTarget> target =
2164
0
            reservedClient ? reservedClient->EventTarget()
2165
0
                           : SystemGroup::EventTargetFor(TaskCategory::Other);
2166
0
2167
0
          reservedClient.reset();
2168
0
          reservedClient = ClientManager::CreateSource(ClientType::Window,
2169
0
                                                       target,
2170
0
                                                       principal);
2171
0
2172
0
          loadInfo->GiveReservedClientSource(std::move(reservedClient));
2173
0
2174
0
          clientInfo = loadInfo->GetReservedClientInfo();
2175
0
        }
2176
0
      }
2177
0
2178
0
      // First, attempt to mark the reserved client controlled directly.  This
2179
0
      // will update the controlled status in the ClientManagerService in the
2180
0
      // parent.  It will also eventually propagate back to the ClientSource.
2181
0
      StartControllingClient(clientInfo.ref(), registration);
2182
0
    }
2183
0
2184
0
    uint32_t redirectMode = nsIHttpChannelInternal::REDIRECT_MODE_MANUAL;
2185
0
    nsCOMPtr<nsIHttpChannelInternal> http = do_QueryInterface(internalChannel);
2186
0
    MOZ_ALWAYS_SUCCEEDS(http->GetRedirectMode(&redirectMode));
2187
0
2188
0
    // Synthetic redirects for non-subresource requests with a "follow"
2189
0
    // redirect mode may switch controllers.  This is basically worker
2190
0
    // scripts right now.  In this case we need to explicitly clear the
2191
0
    // controller to avoid assertions on the SetController() below.
2192
0
    if (redirectMode == nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW) {
2193
0
      loadInfo->ClearController();
2194
0
    }
2195
0
2196
0
    // But we also note the reserved state on the LoadInfo.  This allows the
2197
0
    // ClientSource to be updated immediately after the nsIChannel starts.
2198
0
    // This is necessary to have the correct controller in place for immediate
2199
0
    // follow-on requests.
2200
0
    loadInfo->SetController(serviceWorker->Descriptor());
2201
0
  }
2202
0
2203
0
  MOZ_DIAGNOSTIC_ASSERT(serviceWorker);
2204
0
2205
0
  nsCOMPtr<nsIRunnable> continueRunnable =
2206
0
    new ContinueDispatchFetchEventRunnable(serviceWorker->WorkerPrivate(),
2207
0
                                           aChannel, loadGroup,
2208
0
                                           loadInfo->GetIsDocshellReload());
2209
0
2210
0
  // When this service worker was registered, we also sent down the permissions
2211
0
  // for the runnable. They should have arrived by now, but we still need to
2212
0
  // wait for them if they have not.
2213
0
  nsCOMPtr<nsIRunnable> permissionsRunnable = NS_NewRunnableFunction(
2214
0
    "dom::ServiceWorkerManager::DispatchFetchEvent", [=]() {
2215
0
      nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
2216
0
      MOZ_ALWAYS_SUCCEEDS(permMgr->WhenPermissionsAvailable(serviceWorker->Principal(),
2217
0
                                                            continueRunnable));
2218
0
    });
2219
0
2220
0
  nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(internalChannel);
2221
0
2222
0
  // If there is no upload stream, then continue immediately
2223
0
  if (!uploadChannel) {
2224
0
    MOZ_ALWAYS_SUCCEEDS(permissionsRunnable->Run());
2225
0
    return;
2226
0
  }
2227
0
  // Otherwise, ensure the upload stream can be cloned directly.  This may
2228
0
  // require some async copying, so provide a callback.
2229
0
  aRv = uploadChannel->EnsureUploadStreamIsCloneable(permissionsRunnable);
2230
0
}
2231
2232
bool
2233
ServiceWorkerManager::IsAvailable(nsIPrincipal* aPrincipal,
2234
                                  nsIURI* aURI)
2235
0
{
2236
0
  MOZ_ASSERT(aPrincipal);
2237
0
  MOZ_ASSERT(aURI);
2238
0
2239
0
  RefPtr<ServiceWorkerRegistrationInfo> registration =
2240
0
    GetServiceWorkerRegistrationInfo(aPrincipal, aURI);
2241
0
  return registration && registration->GetActive();
2242
0
}
2243
2244
nsresult
2245
ServiceWorkerManager::GetClientRegistration(const ClientInfo& aClientInfo,
2246
                                            ServiceWorkerRegistrationInfo** aRegistrationInfo)
2247
0
{
2248
0
  ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
2249
0
  if (!data || !data->mRegistrationInfo) {
2250
0
    return NS_ERROR_NOT_AVAILABLE;
2251
0
  }
2252
0
2253
0
  // If the document is controlled, the current worker MUST be non-null.
2254
0
  if (!data->mRegistrationInfo->GetActive()) {
2255
0
    return NS_ERROR_NOT_AVAILABLE;
2256
0
  }
2257
0
2258
0
  RefPtr<ServiceWorkerRegistrationInfo> ref = data->mRegistrationInfo;
2259
0
  ref.forget(aRegistrationInfo);
2260
0
  return NS_OK;
2261
0
}
2262
2263
void
2264
ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes,
2265
                                 const nsACString& aScope)
2266
0
{
2267
0
  MOZ_ASSERT(NS_IsMainThread());
2268
0
2269
0
  if (mShuttingDown) {
2270
0
    return;
2271
0
  }
2272
0
2273
0
  if (ServiceWorkerParentInterceptEnabled()) {
2274
0
    SoftUpdateInternal(aOriginAttributes, aScope, nullptr);
2275
0
    return;
2276
0
  }
2277
0
2278
0
  RefPtr<GenericPromise::Private> promise =
2279
0
    new GenericPromise::Private(__func__);
2280
0
2281
0
  RefPtr<CancelableRunnable> successRunnable =
2282
0
    new SoftUpdateRunnable(aOriginAttributes, aScope, true, promise);
2283
0
2284
0
  RefPtr<CancelableRunnable> failureRunnable =
2285
0
    new ResolvePromiseRunnable(promise);
2286
0
2287
0
  ServiceWorkerUpdaterChild* actor =
2288
0
    new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable);
2289
0
2290
0
  mActor->SendPServiceWorkerUpdaterConstructor(actor, aOriginAttributes,
2291
0
                                               nsCString(aScope));
2292
0
}
2293
2294
namespace {
2295
2296
class UpdateJobCallback final : public ServiceWorkerJob::Callback
2297
{
2298
  RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
2299
2300
0
  ~UpdateJobCallback() = default;
2301
2302
public:
2303
  explicit UpdateJobCallback(ServiceWorkerUpdateFinishCallback* aCallback)
2304
    : mCallback(aCallback)
2305
0
  {
2306
0
    MOZ_ASSERT(NS_IsMainThread());
2307
0
    MOZ_ASSERT(mCallback);
2308
0
  }
2309
2310
  void
2311
  JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override
2312
0
  {
2313
0
    MOZ_ASSERT(NS_IsMainThread());
2314
0
    MOZ_ASSERT(aJob);
2315
0
2316
0
    if (aStatus.Failed()) {
2317
0
      mCallback->UpdateFailed(aStatus);
2318
0
      return;
2319
0
    }
2320
0
2321
0
    MOZ_DIAGNOSTIC_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Update);
2322
0
    RefPtr<ServiceWorkerUpdateJob> updateJob =
2323
0
      static_cast<ServiceWorkerUpdateJob*>(aJob);
2324
0
    RefPtr<ServiceWorkerRegistrationInfo> reg = updateJob->GetRegistration();
2325
0
    mCallback->UpdateSucceeded(reg);
2326
0
  }
2327
2328
  NS_INLINE_DECL_REFCOUNTING(UpdateJobCallback, override)
2329
};
2330
2331
} // anonymous namespace
2332
2333
void
2334
ServiceWorkerManager::SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
2335
                                         const nsACString& aScope,
2336
                                         ServiceWorkerUpdateFinishCallback* aCallback)
2337
0
{
2338
0
  MOZ_ASSERT(NS_IsMainThread());
2339
0
2340
0
  if (mShuttingDown) {
2341
0
    return;
2342
0
  }
2343
0
2344
0
  nsCOMPtr<nsIURI> scopeURI;
2345
0
  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope);
2346
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2347
0
    return;
2348
0
  }
2349
0
2350
0
  nsCOMPtr<nsIPrincipal> principal =
2351
0
    BasePrincipal::CreateCodebasePrincipal(scopeURI, aOriginAttributes);
2352
0
  if (NS_WARN_IF(!principal)) {
2353
0
    return;
2354
0
  }
2355
0
2356
0
  nsAutoCString scopeKey;
2357
0
  rv = PrincipalToScopeKey(principal, scopeKey);
2358
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2359
0
    return;
2360
0
  }
2361
0
2362
0
  RefPtr<ServiceWorkerRegistrationInfo> registration =
2363
0
    GetRegistration(scopeKey, aScope);
2364
0
  if (NS_WARN_IF(!registration)) {
2365
0
    return;
2366
0
  }
2367
0
2368
0
  // "If registration's uninstalling flag is set, abort these steps."
2369
0
  if (registration->IsPendingUninstall()) {
2370
0
    return;
2371
0
  }
2372
0
2373
0
  // "If registration's installing worker is not null, abort these steps."
2374
0
  if (registration->GetInstalling()) {
2375
0
    return;
2376
0
  }
2377
0
2378
0
  // "Let newestWorker be the result of running Get Newest Worker algorithm
2379
0
  // passing registration as its argument.
2380
0
  // If newestWorker is null, abort these steps."
2381
0
  RefPtr<ServiceWorkerInfo> newest = registration->Newest();
2382
0
  if (!newest) {
2383
0
    return;
2384
0
  }
2385
0
2386
0
  // "If the registration queue for registration is empty, invoke Update algorithm,
2387
0
  // or its equivalent, with client, registration as its argument."
2388
0
  // TODO(catalinb): We don't implement the force bypass cache flag.
2389
0
  // See: https://github.com/slightlyoff/ServiceWorker/issues/759
2390
0
  RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
2391
0
                                                            aScope);
2392
0
2393
0
  RefPtr<ServiceWorkerUpdateJob> job =
2394
0
    new ServiceWorkerUpdateJob(principal, registration->Scope(),
2395
0
                               newest->ScriptSpec(), nullptr,
2396
0
                               registration->GetUpdateViaCache());
2397
0
2398
0
  if (aCallback) {
2399
0
    RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
2400
0
    job->AppendResultCallback(cb);
2401
0
  }
2402
0
2403
0
  queue->ScheduleJob(job);
2404
0
}
2405
2406
void
2407
ServiceWorkerManager::Update(nsIPrincipal* aPrincipal,
2408
                             const nsACString& aScope,
2409
                             ServiceWorkerUpdateFinishCallback* aCallback)
2410
0
{
2411
0
  MOZ_ASSERT(NS_IsMainThread());
2412
0
2413
0
  if (ServiceWorkerParentInterceptEnabled()) {
2414
0
    UpdateInternal(aPrincipal, aScope, aCallback);
2415
0
    return;
2416
0
  }
2417
0
2418
0
  RefPtr<GenericPromise::Private> promise =
2419
0
    new GenericPromise::Private(__func__);
2420
0
2421
0
  RefPtr<CancelableRunnable> successRunnable =
2422
0
    new UpdateRunnable(aPrincipal, aScope, aCallback,
2423
0
                       UpdateRunnable::eSuccess, promise);
2424
0
2425
0
  RefPtr<CancelableRunnable> failureRunnable =
2426
0
    new UpdateRunnable(aPrincipal, aScope, aCallback,
2427
0
                       UpdateRunnable::eFailure, promise);
2428
0
2429
0
  ServiceWorkerUpdaterChild* actor =
2430
0
    new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable);
2431
0
2432
0
  mActor->SendPServiceWorkerUpdaterConstructor(actor,
2433
0
                                               aPrincipal->OriginAttributesRef(),
2434
0
                                               nsCString(aScope));
2435
0
}
2436
2437
void
2438
ServiceWorkerManager::UpdateInternal(nsIPrincipal* aPrincipal,
2439
                                     const nsACString& aScope,
2440
                                     ServiceWorkerUpdateFinishCallback* aCallback)
2441
0
{
2442
0
  MOZ_ASSERT(aPrincipal);
2443
0
  MOZ_ASSERT(aCallback);
2444
0
2445
0
  nsAutoCString scopeKey;
2446
0
  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
2447
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2448
0
    return;
2449
0
  }
2450
0
2451
0
  RefPtr<ServiceWorkerRegistrationInfo> registration =
2452
0
    GetRegistration(scopeKey, aScope);
2453
0
  if (NS_WARN_IF(!registration)) {
2454
0
    return;
2455
0
  }
2456
0
2457
0
  // "Let newestWorker be the result of running Get Newest Worker algorithm
2458
0
  // passing registration as its argument.
2459
0
  // If newestWorker is null, return a promise rejected with "InvalidStateError"
2460
0
  RefPtr<ServiceWorkerInfo> newest = registration->Newest();
2461
0
  if (!newest) {
2462
0
    ErrorResult error(NS_ERROR_DOM_INVALID_STATE_ERR);
2463
0
    aCallback->UpdateFailed(error);
2464
0
2465
0
    // In case the callback does not consume the exception
2466
0
    error.SuppressException();
2467
0
2468
0
    return;
2469
0
  }
2470
0
2471
0
  RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, aScope);
2472
0
2473
0
  // "Invoke Update algorithm, or its equivalent, with client, registration as
2474
0
  // its argument."
2475
0
  RefPtr<ServiceWorkerUpdateJob> job =
2476
0
    new ServiceWorkerUpdateJob(aPrincipal, registration->Scope(),
2477
0
                               newest->ScriptSpec(), nullptr,
2478
0
                               registration->GetUpdateViaCache());
2479
0
2480
0
  RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
2481
0
  job->AppendResultCallback(cb);
2482
0
2483
0
  queue->ScheduleJob(job);
2484
0
}
2485
2486
already_AddRefed<GenericPromise>
2487
ServiceWorkerManager::MaybeClaimClient(const ClientInfo& aClientInfo,
2488
                                       ServiceWorkerRegistrationInfo* aWorkerRegistration)
2489
0
{
2490
0
  MOZ_DIAGNOSTIC_ASSERT(aWorkerRegistration);
2491
0
2492
0
  RefPtr<GenericPromise> ref;
2493
0
2494
0
  if (!aWorkerRegistration->GetActive()) {
2495
0
    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
2496
0
                                          __func__);
2497
0
    return ref.forget();
2498
0
  }
2499
0
2500
0
  // Same origin check
2501
0
  nsCOMPtr<nsIPrincipal> principal(aClientInfo.GetPrincipal());
2502
0
  if (!aWorkerRegistration->Principal()->Equals(principal)) {
2503
0
    ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_SECURITY_ERR, __func__);
2504
0
    return ref.forget();
2505
0
  }
2506
0
2507
0
  // The registration that should be controlling the client
2508
0
  RefPtr<ServiceWorkerRegistrationInfo> matchingRegistration =
2509
0
    GetServiceWorkerRegistrationInfo(aClientInfo);
2510
0
2511
0
  // The registration currently controlling the client
2512
0
  RefPtr<ServiceWorkerRegistrationInfo> controllingRegistration;
2513
0
  GetClientRegistration(aClientInfo, getter_AddRefs(controllingRegistration));
2514
0
2515
0
  if (aWorkerRegistration != matchingRegistration ||
2516
0
      aWorkerRegistration == controllingRegistration) {
2517
0
    ref = GenericPromise::CreateAndResolve(true, __func__);
2518
0
    return ref.forget();
2519
0
  }
2520
0
2521
0
  ref = StartControllingClient(aClientInfo, aWorkerRegistration);
2522
0
  return ref.forget();
2523
0
}
2524
2525
already_AddRefed<GenericPromise>
2526
ServiceWorkerManager::MaybeClaimClient(const ClientInfo& aClientInfo,
2527
                                       const ServiceWorkerDescriptor& aServiceWorker)
2528
0
{
2529
0
  RefPtr<GenericPromise> ref;
2530
0
2531
0
  nsCOMPtr<nsIPrincipal> principal = aServiceWorker.GetPrincipal();
2532
0
  if (!principal) {
2533
0
    ref = GenericPromise::CreateAndResolve(false, __func__);
2534
0
    return ref.forget();
2535
0
  }
2536
0
2537
0
  RefPtr<ServiceWorkerRegistrationInfo> registration =
2538
0
    GetRegistration(principal, aServiceWorker.Scope());
2539
0
2540
0
  // While ServiceWorkerManager is distributed across child processes its
2541
0
  // possible for us to sometimes get a claim for a new worker that has
2542
0
  // not propagated to this process yet.  For now, simply note that we
2543
0
  // are done.  The fix for this is to move the SWM to the parent process
2544
0
  // so there are no consistency errors.
2545
0
  if (NS_WARN_IF(!registration) || NS_WARN_IF(!registration->GetActive())) {
2546
0
    ref = GenericPromise::CreateAndResolve(false, __func__);
2547
0
    return ref.forget();
2548
0
  }
2549
0
2550
0
  ref = MaybeClaimClient(aClientInfo, registration);
2551
0
  return ref.forget();
2552
0
}
2553
2554
void
2555
ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal,
2556
                                         const nsCString& aScope,
2557
                                         uint64_t aServiceWorkerID)
2558
0
{
2559
0
  RefPtr<ServiceWorkerRegistrationInfo> registration =
2560
0
    GetRegistration(aPrincipal, aScope);
2561
0
  if (NS_WARN_IF(!registration)) {
2562
0
    return;
2563
0
  }
2564
0
2565
0
  RefPtr<ServiceWorkerInfo> worker =
2566
0
    registration->GetServiceWorkerInfoById(aServiceWorkerID);
2567
0
2568
0
  if (NS_WARN_IF(!worker)) {
2569
0
    return;
2570
0
  }
2571
0
2572
0
  worker->SetSkipWaitingFlag();
2573
0
2574
0
  if (worker->State() == ServiceWorkerState::Installed) {
2575
0
    registration->TryToActivateAsync();
2576
0
  }
2577
0
}
2578
2579
void
2580
ServiceWorkerManager::UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration)
2581
0
{
2582
0
  MOZ_ASSERT(NS_IsMainThread());
2583
0
2584
0
  RefPtr<ServiceWorkerInfo> activeWorker = aRegistration->GetActive();
2585
0
  MOZ_DIAGNOSTIC_ASSERT(activeWorker);
2586
0
2587
0
  AutoTArray<RefPtr<ClientHandle>, 16> handleList;
2588
0
  for (auto iter = mControlledClients.Iter(); !iter.Done(); iter.Next()) {
2589
0
    if (iter.UserData()->mRegistrationInfo != aRegistration) {
2590
0
      continue;
2591
0
    }
2592
0
2593
0
    handleList.AppendElement(iter.UserData()->mClientHandle);
2594
0
  }
2595
0
2596
0
  // Fire event after iterating mControlledClients is done to prevent
2597
0
  // modification by reentering from the event handlers during iteration.
2598
0
  for (auto& handle : handleList) {
2599
0
    RefPtr<GenericPromise> p = handle->Control(activeWorker->Descriptor());
2600
0
2601
0
    RefPtr<ServiceWorkerManager> self = this;
2602
0
2603
0
    // If we fail to control the client, then automatically remove it
2604
0
    // from our list of controlled clients.
2605
0
    p->Then(
2606
0
      SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
2607
0
      [] (bool) {
2608
0
        // do nothing on success
2609
0
      }, [self, clientInfo = handle->Info()] (nsresult aRv) {
2610
0
        // failed to control, forget about this client
2611
0
        self->StopControllingClient(clientInfo);
2612
0
      });
2613
0
  }
2614
0
}
2615
2616
already_AddRefed<ServiceWorkerRegistrationInfo>
2617
ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal,
2618
                                      const nsACString& aScope) const
2619
0
{
2620
0
  MOZ_ASSERT(aPrincipal);
2621
0
2622
0
  nsAutoCString scopeKey;
2623
0
  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
2624
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2625
0
    return nullptr;
2626
0
  }
2627
0
2628
0
  return GetRegistration(scopeKey, aScope);
2629
0
}
2630
2631
already_AddRefed<ServiceWorkerRegistrationInfo>
2632
ServiceWorkerManager::GetRegistration(const PrincipalInfo& aPrincipalInfo,
2633
                                      const nsACString& aScope) const
2634
0
{
2635
0
  nsAutoCString scopeKey;
2636
0
  nsresult rv = PrincipalInfoToScopeKey(aPrincipalInfo, scopeKey);
2637
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2638
0
    return nullptr;
2639
0
  }
2640
0
2641
0
  return GetRegistration(scopeKey, aScope);
2642
0
}
2643
2644
NS_IMETHODIMP
2645
ServiceWorkerManager::GetRegistrationByPrincipal(nsIPrincipal* aPrincipal,
2646
                                                 const nsAString& aScope,
2647
                                                 nsIServiceWorkerRegistrationInfo** aInfo)
2648
0
{
2649
0
  MOZ_ASSERT(aPrincipal);
2650
0
  MOZ_ASSERT(aInfo);
2651
0
2652
0
  nsCOMPtr<nsIURI> scopeURI;
2653
0
  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
2654
0
  if (NS_FAILED(rv)) {
2655
0
    return NS_ERROR_FAILURE;
2656
0
  }
2657
0
2658
0
  RefPtr<ServiceWorkerRegistrationInfo> info =
2659
0
    GetServiceWorkerRegistrationInfo(aPrincipal, scopeURI);
2660
0
  if (!info) {
2661
0
    return NS_ERROR_FAILURE;
2662
0
  }
2663
0
  info.forget(aInfo);
2664
0
2665
0
  return NS_OK;
2666
0
}
2667
2668
already_AddRefed<ServiceWorkerRegistrationInfo>
2669
ServiceWorkerManager::GetRegistration(const nsACString& aScopeKey,
2670
                                      const nsACString& aScope) const
2671
0
{
2672
0
  RefPtr<ServiceWorkerRegistrationInfo> reg;
2673
0
2674
0
  RegistrationDataPerPrincipal* data;
2675
0
  if (!mRegistrationInfos.Get(aScopeKey, &data)) {
2676
0
    return reg.forget();
2677
0
  }
2678
0
2679
0
  data->mInfos.Get(aScope, getter_AddRefs(reg));
2680
0
  return reg.forget();
2681
0
}
2682
2683
already_AddRefed<ServiceWorkerRegistrationInfo>
2684
ServiceWorkerManager::CreateNewRegistration(
2685
    const nsCString& aScope,
2686
    nsIPrincipal* aPrincipal,
2687
    ServiceWorkerUpdateViaCache aUpdateViaCache)
2688
0
{
2689
#ifdef DEBUG
2690
  MOZ_ASSERT(NS_IsMainThread());
2691
  nsCOMPtr<nsIURI> scopeURI;
2692
  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
2693
  MOZ_ASSERT(NS_SUCCEEDED(rv));
2694
2695
  RefPtr<ServiceWorkerRegistrationInfo> tmp =
2696
    GetRegistration(aPrincipal, aScope);
2697
  MOZ_ASSERT(!tmp);
2698
#endif
2699
2700
0
  RefPtr<ServiceWorkerRegistrationInfo> registration =
2701
0
    new ServiceWorkerRegistrationInfo(aScope, aPrincipal, aUpdateViaCache);
2702
0
2703
0
  // From now on ownership of registration is with
2704
0
  // mServiceWorkerRegistrationInfos.
2705
0
  AddScopeAndRegistration(aScope, registration);
2706
0
  return registration.forget();
2707
0
}
2708
2709
void
2710
ServiceWorkerManager::MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
2711
0
{
2712
0
  MOZ_ASSERT(aRegistration);
2713
0
  RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
2714
0
  if (!newest && HasScope(aRegistration->Principal(), aRegistration->Scope())) {
2715
0
    RemoveRegistration(aRegistration);
2716
0
  }
2717
0
}
2718
2719
void
2720
ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
2721
0
{
2722
0
  // Note, we do not need to call mActor->SendUnregister() here.  There are a few
2723
0
  // ways we can get here:
2724
0
  // 1) Through a normal unregister which calls SendUnregister() in the unregister
2725
0
  //    job Start() method.
2726
0
  // 2) Through origin storage being purged.  These result in ForceUnregister()
2727
0
  //    starting unregister jobs which in turn call SendUnregister().
2728
0
  // 3) Through the failure to install a new service worker.  Since we don't store
2729
0
  //    the registration until install succeeds, we do not need to call
2730
0
  //    SendUnregister here.
2731
0
  // Assert these conditions by testing for pending uninstall (cases 1 and 2) or
2732
0
  // null workers (case 3).
2733
#ifdef DEBUG
2734
  RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
2735
  MOZ_ASSERT(aRegistration->IsPendingUninstall() || !newest);
2736
#endif
2737
2738
0
  MOZ_ASSERT(HasScope(aRegistration->Principal(), aRegistration->Scope()));
2739
0
2740
0
  // When a registration is removed, we must clear its contents since the DOM
2741
0
  // object may be held by content script.
2742
0
  aRegistration->Clear();
2743
0
2744
0
  RemoveScopeAndRegistration(aRegistration);
2745
0
}
2746
2747
NS_IMETHODIMP
2748
ServiceWorkerManager::GetAllRegistrations(nsIArray** aResult)
2749
0
{
2750
0
  MOZ_ASSERT(NS_IsMainThread());
2751
0
2752
0
  nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID));
2753
0
  if (!array) {
2754
0
    return NS_ERROR_OUT_OF_MEMORY;
2755
0
  }
2756
0
2757
0
  for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
2758
0
    for (auto it2 = it1.UserData()->mInfos.Iter(); !it2.Done(); it2.Next()) {
2759
0
      ServiceWorkerRegistrationInfo* reg = it2.UserData();
2760
0
      MOZ_ASSERT(reg);
2761
0
2762
0
      if (reg->IsPendingUninstall()) {
2763
0
        continue;
2764
0
      }
2765
0
2766
0
      array->AppendElement(reg);
2767
0
    }
2768
0
  }
2769
0
2770
0
  array.forget(aResult);
2771
0
  return NS_OK;
2772
0
}
2773
2774
// MUST ONLY BE CALLED FROM Remove(), RemoveAll() and RemoveAllRegistrations()!
2775
void
2776
ServiceWorkerManager::ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
2777
                                      ServiceWorkerRegistrationInfo* aRegistration)
2778
0
{
2779
0
  MOZ_ASSERT(aRegistrationData);
2780
0
  MOZ_ASSERT(aRegistration);
2781
0
2782
0
  RefPtr<ServiceWorkerJobQueue> queue;
2783
0
  aRegistrationData->mJobQueues.Get(aRegistration->Scope(), getter_AddRefs(queue));
2784
0
  if (queue) {
2785
0
    queue->CancelAll();
2786
0
  }
2787
0
2788
0
  if (auto entry = aRegistrationData->mUpdateTimers.Lookup(aRegistration->Scope())) {
2789
0
    entry.Data()->Cancel();
2790
0
    entry.Remove();
2791
0
  }
2792
0
2793
0
  // Since Unregister is async, it is ok to call it in an enumeration.
2794
0
  Unregister(aRegistration->Principal(), nullptr, NS_ConvertUTF8toUTF16(aRegistration->Scope()));
2795
0
}
2796
2797
void
2798
ServiceWorkerManager::Remove(const nsACString& aHost)
2799
0
{
2800
0
  MOZ_ASSERT(NS_IsMainThread());
2801
0
2802
0
  nsCOMPtr<nsIEffectiveTLDService> tldService =
2803
0
    do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
2804
0
  if (NS_WARN_IF(!tldService)) {
2805
0
    return;
2806
0
  }
2807
0
2808
0
  for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
2809
0
    ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
2810
0
    for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
2811
0
      ServiceWorkerRegistrationInfo* reg = it2.UserData();
2812
0
      nsCOMPtr<nsIURI> scopeURI;
2813
0
      nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), it2.Key(),
2814
0
                              nullptr, nullptr);
2815
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2816
0
        continue;
2817
0
      }
2818
0
2819
0
      nsAutoCString host;
2820
0
      rv = scopeURI->GetHost(host);
2821
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2822
0
        continue;
2823
0
      }
2824
0
2825
0
      // This way subdomains are also cleared.
2826
0
      bool hasRootDomain = false;
2827
0
      rv = tldService->HasRootDomain(host, aHost, &hasRootDomain);
2828
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
2829
0
        continue;
2830
0
      }
2831
0
2832
0
      if (hasRootDomain) {
2833
0
        ForceUnregister(data, reg);
2834
0
      }
2835
0
    }
2836
0
  }
2837
0
}
2838
2839
void
2840
ServiceWorkerManager::PropagateRemove(const nsACString& aHost)
2841
0
{
2842
0
  MOZ_ASSERT(NS_IsMainThread());
2843
0
  mActor->SendPropagateRemove(nsCString(aHost));
2844
0
}
2845
2846
void
2847
ServiceWorkerManager::RemoveAll()
2848
0
{
2849
0
  MOZ_ASSERT(NS_IsMainThread());
2850
0
2851
0
  for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
2852
0
    ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
2853
0
    for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
2854
0
      ServiceWorkerRegistrationInfo* reg = it2.UserData();
2855
0
      ForceUnregister(data, reg);
2856
0
    }
2857
0
  }
2858
0
}
2859
2860
void
2861
ServiceWorkerManager::PropagateRemoveAll()
2862
0
{
2863
0
  MOZ_ASSERT(NS_IsMainThread());
2864
0
  MOZ_ASSERT(XRE_IsParentProcess());
2865
0
  mActor->SendPropagateRemoveAll();
2866
0
}
2867
2868
void
2869
ServiceWorkerManager::RemoveAllRegistrations(OriginAttributesPattern* aPattern)
2870
0
{
2871
0
  MOZ_ASSERT(NS_IsMainThread());
2872
0
2873
0
  MOZ_ASSERT(aPattern);
2874
0
2875
0
  for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
2876
0
    ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
2877
0
2878
0
    // We can use iteration because ForceUnregister (and Unregister) are
2879
0
    // async. Otherwise doing some R/W operations on an hashtable during
2880
0
    // iteration will crash.
2881
0
    for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
2882
0
      ServiceWorkerRegistrationInfo* reg = it2.UserData();
2883
0
2884
0
      MOZ_ASSERT(reg);
2885
0
      MOZ_ASSERT(reg->Principal());
2886
0
2887
0
      bool matches =
2888
0
        aPattern->Matches(reg->Principal()->OriginAttributesRef());
2889
0
      if (!matches) {
2890
0
        continue;
2891
0
      }
2892
0
2893
0
      ForceUnregister(data, reg);
2894
0
    }
2895
0
  }
2896
0
}
2897
2898
NS_IMETHODIMP
2899
ServiceWorkerManager::AddListener(nsIServiceWorkerManagerListener* aListener)
2900
0
{
2901
0
  MOZ_ASSERT(NS_IsMainThread());
2902
0
2903
0
  if (!aListener || mListeners.Contains(aListener)) {
2904
0
    return NS_ERROR_INVALID_ARG;
2905
0
  }
2906
0
2907
0
  mListeners.AppendElement(aListener);
2908
0
2909
0
  return NS_OK;
2910
0
}
2911
2912
NS_IMETHODIMP
2913
ServiceWorkerManager::RemoveListener(nsIServiceWorkerManagerListener* aListener)
2914
0
{
2915
0
  MOZ_ASSERT(NS_IsMainThread());
2916
0
2917
0
  if (!aListener || !mListeners.Contains(aListener)) {
2918
0
    return NS_ERROR_INVALID_ARG;
2919
0
  }
2920
0
2921
0
  mListeners.RemoveElement(aListener);
2922
0
2923
0
  return NS_OK;
2924
0
}
2925
2926
NS_IMETHODIMP
2927
ServiceWorkerManager::Observe(nsISupports* aSubject,
2928
                              const char* aTopic,
2929
                              const char16_t* aData)
2930
0
{
2931
0
  if (strcmp(aTopic, CLEAR_ORIGIN_DATA) == 0) {
2932
0
    MOZ_ASSERT(XRE_IsParentProcess());
2933
0
    OriginAttributesPattern pattern;
2934
0
    MOZ_ALWAYS_TRUE(pattern.Init(nsAutoString(aData)));
2935
0
2936
0
    RemoveAllRegistrations(&pattern);
2937
0
    return NS_OK;
2938
0
  }
2939
0
2940
0
  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
2941
0
    MaybeStartShutdown();
2942
0
    return NS_OK;
2943
0
  }
2944
0
2945
0
  MOZ_CRASH("Received message we aren't supposed to be registered for!");
2946
0
  return NS_OK;
2947
0
}
2948
2949
NS_IMETHODIMP
2950
ServiceWorkerManager::PropagateSoftUpdate(JS::Handle<JS::Value> aOriginAttributes,
2951
                                          const nsAString& aScope,
2952
                                          JSContext* aCx)
2953
0
{
2954
0
  MOZ_ASSERT(NS_IsMainThread());
2955
0
2956
0
  OriginAttributes attrs;
2957
0
  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
2958
0
    return NS_ERROR_INVALID_ARG;
2959
0
  }
2960
0
2961
0
  PropagateSoftUpdate(attrs, aScope);
2962
0
  return NS_OK;
2963
0
}
2964
2965
void
2966
ServiceWorkerManager::PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
2967
                                          const nsAString& aScope)
2968
0
{
2969
0
  MOZ_ASSERT(NS_IsMainThread());
2970
0
  mActor->SendPropagateSoftUpdate(aOriginAttributes, nsString(aScope));
2971
0
}
2972
2973
NS_IMETHODIMP
2974
ServiceWorkerManager::PropagateUnregister(nsIPrincipal* aPrincipal,
2975
                                          nsIServiceWorkerUnregisterCallback* aCallback,
2976
                                          const nsAString& aScope)
2977
0
{
2978
0
  MOZ_ASSERT(NS_IsMainThread());
2979
0
  MOZ_ASSERT(aPrincipal);
2980
0
2981
0
  PrincipalInfo principalInfo;
2982
0
  if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
2983
0
                                                    &principalInfo)))) {
2984
0
    return NS_ERROR_FAILURE;
2985
0
  }
2986
0
2987
0
  mActor->SendPropagateUnregister(principalInfo, nsString(aScope));
2988
0
2989
0
  nsresult rv = Unregister(aPrincipal, aCallback, aScope);
2990
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2991
0
    return rv;
2992
0
  }
2993
0
2994
0
  return NS_OK;
2995
0
}
2996
2997
void
2998
ServiceWorkerManager::NotifyListenersOnRegister(
2999
                                        nsIServiceWorkerRegistrationInfo* aInfo)
3000
0
{
3001
0
  nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners);
3002
0
  for (size_t index = 0; index < listeners.Length(); ++index) {
3003
0
    listeners[index]->OnRegister(aInfo);
3004
0
  }
3005
0
}
3006
3007
void
3008
ServiceWorkerManager::NotifyListenersOnUnregister(
3009
                                        nsIServiceWorkerRegistrationInfo* aInfo)
3010
0
{
3011
0
  nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners);
3012
0
  for (size_t index = 0; index < listeners.Length(); ++index) {
3013
0
    listeners[index]->OnUnregister(aInfo);
3014
0
  }
3015
0
}
3016
3017
class UpdateTimerCallback final : public nsITimerCallback
3018
                                , public nsINamed
3019
{
3020
  nsCOMPtr<nsIPrincipal> mPrincipal;
3021
  const nsCString mScope;
3022
3023
  ~UpdateTimerCallback()
3024
0
  {
3025
0
  }
3026
3027
public:
3028
  UpdateTimerCallback(nsIPrincipal* aPrincipal, const nsACString& aScope)
3029
    : mPrincipal(aPrincipal)
3030
    , mScope(aScope)
3031
0
  {
3032
0
    MOZ_ASSERT(NS_IsMainThread());
3033
0
    MOZ_ASSERT(mPrincipal);
3034
0
    MOZ_ASSERT(!mScope.IsEmpty());
3035
0
  }
3036
3037
  NS_IMETHOD
3038
  Notify(nsITimer* aTimer) override
3039
0
  {
3040
0
    MOZ_ASSERT(NS_IsMainThread());
3041
0
3042
0
    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
3043
0
    if (!swm) {
3044
0
      // shutting down, do nothing
3045
0
      return NS_OK;
3046
0
    }
3047
0
3048
0
    swm->UpdateTimerFired(mPrincipal, mScope);
3049
0
    return NS_OK;
3050
0
  }
3051
3052
  NS_IMETHOD
3053
  GetName(nsACString& aName) override
3054
0
  {
3055
0
    aName.AssignLiteral("UpdateTimerCallback");
3056
0
    return NS_OK;
3057
0
  }
3058
3059
  NS_DECL_ISUPPORTS
3060
};
3061
3062
NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback, nsINamed)
3063
3064
bool
3065
ServiceWorkerManager::MayHaveActiveServiceWorkerInstance(ContentParent* aContent,
3066
                                                         nsIPrincipal* aPrincipal)
3067
0
{
3068
0
  MOZ_ASSERT(NS_IsMainThread());
3069
0
  MOZ_ASSERT(aPrincipal);
3070
0
3071
0
  if (mShuttingDown) {
3072
0
    return false;
3073
0
  }
3074
0
3075
0
  nsAutoCString scopeKey;
3076
0
  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
3077
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3078
0
    return false;
3079
0
  }
3080
0
3081
0
  RegistrationDataPerPrincipal* data;
3082
0
  if (!mRegistrationInfos.Get(scopeKey, &data)) {
3083
0
    return false;
3084
0
  }
3085
0
3086
0
  return true;
3087
0
}
3088
3089
void
3090
ServiceWorkerManager::ScheduleUpdateTimer(nsIPrincipal* aPrincipal,
3091
                                          const nsACString& aScope)
3092
0
{
3093
0
  MOZ_ASSERT(NS_IsMainThread());
3094
0
  MOZ_ASSERT(aPrincipal);
3095
0
  MOZ_ASSERT(!aScope.IsEmpty());
3096
0
3097
0
  if (mShuttingDown) {
3098
0
    return;
3099
0
  }
3100
0
3101
0
  nsAutoCString scopeKey;
3102
0
  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
3103
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3104
0
    return;
3105
0
  }
3106
0
3107
0
  RegistrationDataPerPrincipal* data;
3108
0
  if (!mRegistrationInfos.Get(scopeKey, &data)) {
3109
0
    return;
3110
0
  }
3111
0
3112
0
  nsCOMPtr<nsITimer>& timer = data->mUpdateTimers.GetOrInsert(aScope);
3113
0
  if (timer) {
3114
0
    // There is already a timer scheduled.  In this case just use the original
3115
0
    // schedule time.  We don't want to push it out to a later time since that
3116
0
    // could allow updates to be starved forever if events are continuously
3117
0
    // fired.
3118
0
    return;
3119
0
  }
3120
0
3121
0
  nsCOMPtr<nsITimerCallback> callback = new UpdateTimerCallback(aPrincipal,
3122
0
                                                                aScope);
3123
0
3124
0
  const uint32_t UPDATE_DELAY_MS = 1000;
3125
0
3126
0
  // Label with SystemGroup because UpdateTimerCallback only sends an IPC message
3127
0
  // (PServiceWorkerUpdaterConstructor) without touching any web contents.
3128
0
  rv = NS_NewTimerWithCallback(getter_AddRefs(timer),
3129
0
                               callback, UPDATE_DELAY_MS,
3130
0
                               nsITimer::TYPE_ONE_SHOT,
3131
0
                               SystemGroup::EventTargetFor(TaskCategory::Other));
3132
0
3133
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3134
0
    data->mUpdateTimers.Remove(aScope); // another lookup, but very rare
3135
0
    return;
3136
0
  }
3137
0
}
3138
3139
void
3140
ServiceWorkerManager::UpdateTimerFired(nsIPrincipal* aPrincipal,
3141
                                       const nsACString& aScope)
3142
0
{
3143
0
  MOZ_ASSERT(NS_IsMainThread());
3144
0
  MOZ_ASSERT(aPrincipal);
3145
0
  MOZ_ASSERT(!aScope.IsEmpty());
3146
0
3147
0
  if (mShuttingDown) {
3148
0
    return;
3149
0
  }
3150
0
3151
0
  // First cleanup the timer.
3152
0
  nsAutoCString scopeKey;
3153
0
  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
3154
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3155
0
    return;
3156
0
  }
3157
0
3158
0
  RegistrationDataPerPrincipal* data;
3159
0
  if (!mRegistrationInfos.Get(scopeKey, &data)) {
3160
0
    return;
3161
0
  }
3162
0
3163
0
  if (auto entry = data->mUpdateTimers.Lookup(aScope)) {
3164
0
    entry.Data()->Cancel();
3165
0
    entry.Remove();
3166
0
  }
3167
0
3168
0
  RefPtr<ServiceWorkerRegistrationInfo> registration;
3169
0
  data->mInfos.Get(aScope, getter_AddRefs(registration));
3170
0
  if (!registration) {
3171
0
    return;
3172
0
  }
3173
0
3174
0
  if (!registration->CheckAndClearIfUpdateNeeded()) {
3175
0
    return;
3176
0
  }
3177
0
3178
0
  OriginAttributes attrs = aPrincipal->OriginAttributesRef();
3179
0
3180
0
  SoftUpdate(attrs, aScope);
3181
0
}
3182
3183
void
3184
ServiceWorkerManager::MaybeSendUnregister(nsIPrincipal* aPrincipal,
3185
                                          const nsACString& aScope)
3186
0
{
3187
0
  MOZ_ASSERT(NS_IsMainThread());
3188
0
  MOZ_ASSERT(aPrincipal);
3189
0
  MOZ_ASSERT(!aScope.IsEmpty());
3190
0
3191
0
  if (!mActor) {
3192
0
    return;
3193
0
  }
3194
0
3195
0
  PrincipalInfo principalInfo;
3196
0
  nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
3197
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
3198
0
    return;
3199
0
  }
3200
0
3201
0
  Unused << mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(aScope));
3202
0
}
3203
3204
} // namespace dom
3205
} // namespace mozilla