Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/serviceworkers/ServiceWorkerRegistrationImpl.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 "ServiceWorkerRegistrationImpl.h"
8
9
#include "ipc/ErrorIPCUtils.h"
10
#include "mozilla/dom/DOMPrefs.h"
11
#include "mozilla/dom/Promise.h"
12
#include "mozilla/dom/PromiseWorkerProxy.h"
13
#include "mozilla/dom/PushManagerBinding.h"
14
#include "mozilla/dom/PushManager.h"
15
#include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
16
#include "mozilla/dom/WorkerCommon.h"
17
#include "mozilla/dom/WorkerPrivate.h"
18
#include "mozilla/dom/WorkerRef.h"
19
#include "mozilla/dom/WorkerScope.h"
20
#include "mozilla/Services.h"
21
#include "mozilla/Unused.h"
22
#include "nsCycleCollectionParticipant.h"
23
#include "nsNetUtil.h"
24
#include "nsServiceManagerUtils.h"
25
#include "ServiceWorker.h"
26
#include "ServiceWorkerManager.h"
27
#include "ServiceWorkerPrivate.h"
28
#include "ServiceWorkerRegistration.h"
29
30
#include "nsIDocument.h"
31
#include "nsIServiceWorkerManager.h"
32
#include "nsISupportsPrimitives.h"
33
#include "nsPIDOMWindow.h"
34
#include "nsContentUtils.h"
35
36
namespace mozilla {
37
namespace dom {
38
39
////////////////////////////////////////////////////
40
// Main Thread implementation
41
42
ServiceWorkerRegistrationMainThread::ServiceWorkerRegistrationMainThread(const ServiceWorkerRegistrationDescriptor& aDescriptor)
43
  : mOuter(nullptr)
44
  , mDescriptor(aDescriptor)
45
  , mScope(NS_ConvertUTF8toUTF16(aDescriptor.Scope()))
46
  , mListeningForEvents(false)
47
0
{
48
0
  MOZ_ASSERT(NS_IsMainThread());
49
0
}
50
51
ServiceWorkerRegistrationMainThread::~ServiceWorkerRegistrationMainThread()
52
0
{
53
0
  MOZ_DIAGNOSTIC_ASSERT(!mListeningForEvents);
54
0
  MOZ_DIAGNOSTIC_ASSERT(!mOuter);
55
0
}
56
57
// XXXnsm, maybe this can be optimized to only add when a event handler is
58
// registered.
59
void
60
ServiceWorkerRegistrationMainThread::StartListeningForEvents()
61
0
{
62
0
  MOZ_ASSERT(NS_IsMainThread());
63
0
  MOZ_ASSERT(!mListeningForEvents);
64
0
  MOZ_DIAGNOSTIC_ASSERT(!mInfo);
65
0
66
0
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
67
0
  NS_ENSURE_TRUE_VOID(swm);
68
0
69
0
  mInfo = swm->GetRegistration(mDescriptor.PrincipalInfo(),
70
0
                               mDescriptor.Scope());
71
0
  NS_ENSURE_TRUE_VOID(mInfo);
72
0
73
0
  mInfo->AddInstance(this, mDescriptor);
74
0
  mListeningForEvents = true;
75
0
}
76
77
void
78
ServiceWorkerRegistrationMainThread::StopListeningForEvents()
79
0
{
80
0
  MOZ_ASSERT(NS_IsMainThread());
81
0
  if (!mListeningForEvents) {
82
0
    return;
83
0
  }
84
0
85
0
  MOZ_DIAGNOSTIC_ASSERT(mInfo);
86
0
  mInfo->RemoveInstance(this);
87
0
  mInfo = nullptr;
88
0
89
0
  mListeningForEvents = false;
90
0
}
91
92
void
93
ServiceWorkerRegistrationMainThread::RegistrationRemovedInternal()
94
0
{
95
0
  MOZ_ASSERT(NS_IsMainThread());
96
0
  // Its possible for the binding object to be collected while we the
97
0
  // runnable to call this method is in the event queue.  Double check
98
0
  // whether there is still anything to do here.
99
0
  if (mOuter) {
100
0
    mOuter->RegistrationRemoved();
101
0
  }
102
0
  StopListeningForEvents();
103
0
}
104
105
void
106
ServiceWorkerRegistrationMainThread::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor)
107
0
{
108
0
  NS_ENSURE_TRUE_VOID(mOuter);
109
0
110
0
  nsIGlobalObject* global = mOuter->GetParentObject();
111
0
  NS_ENSURE_TRUE_VOID(global);
112
0
113
0
  RefPtr<ServiceWorkerRegistrationMainThread> self = this;
114
0
  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
115
0
    "ServiceWorkerRegistrationMainThread::UpdateState",
116
0
    [self, desc = std::move(aDescriptor)] () mutable {
117
0
      self->mDescriptor = std::move(desc);
118
0
      NS_ENSURE_TRUE_VOID(self->mOuter);
119
0
      self->mOuter->UpdateState(self->mDescriptor);
120
0
    });
121
0
122
0
  Unused <<
123
0
    global->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget(),
124
0
                                                          NS_DISPATCH_NORMAL);
125
0
}
126
127
void
128
ServiceWorkerRegistrationMainThread::RegistrationRemoved()
129
0
{
130
0
  NS_ENSURE_TRUE_VOID(mOuter);
131
0
132
0
  nsIGlobalObject* global = mOuter->GetParentObject();
133
0
  NS_ENSURE_TRUE_VOID(global);
134
0
135
0
  // Queue a runnable to clean up the registration.  This is necessary
136
0
  // because there may be runnables in the event queue already to
137
0
  // update the registration state.  We want to let those run
138
0
  // if possible before clearing our mOuter reference.
139
0
  nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
140
0
    "ServiceWorkerRegistrationMainThread::RegistrationRemoved",
141
0
    this,
142
0
    &ServiceWorkerRegistrationMainThread::RegistrationRemovedInternal);
143
0
144
0
  Unused <<
145
0
    global->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget(),
146
0
                                                          NS_DISPATCH_NORMAL);
147
0
}
148
149
bool
150
ServiceWorkerRegistrationMainThread::MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor)
151
0
{
152
0
  return mOuter->MatchesDescriptor(aDescriptor);
153
0
}
154
155
void
156
ServiceWorkerRegistrationMainThread::SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg)
157
0
{
158
0
  MOZ_DIAGNOSTIC_ASSERT(aReg);
159
0
  MOZ_DIAGNOSTIC_ASSERT(!mOuter);
160
0
  mOuter = aReg;
161
0
  StartListeningForEvents();
162
0
}
163
164
void
165
ServiceWorkerRegistrationMainThread::ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg)
166
0
{
167
0
  MOZ_ASSERT_IF(mOuter, mOuter == aReg);
168
0
  StopListeningForEvents();
169
0
  mOuter = nullptr;
170
0
}
171
172
namespace {
173
174
void
175
UpdateInternal(nsIPrincipal* aPrincipal,
176
               const nsACString& aScope,
177
               ServiceWorkerUpdateFinishCallback* aCallback)
178
0
{
179
0
  MOZ_ASSERT(NS_IsMainThread());
180
0
  MOZ_ASSERT(aPrincipal);
181
0
  MOZ_ASSERT(aCallback);
182
0
183
0
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
184
0
  if (!swm) {
185
0
    // browser shutdown
186
0
    return;
187
0
  }
188
0
189
0
  swm->Update(aPrincipal, aScope, aCallback);
190
0
}
191
192
class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
193
{
194
  RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
195
196
  ~MainThreadUpdateCallback()
197
0
  {
198
0
    mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
199
0
  }
200
201
public:
202
  MainThreadUpdateCallback()
203
    : mPromise(new ServiceWorkerRegistrationPromise::Private(__func__))
204
0
  {
205
0
  }
206
207
  void
208
  UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override
209
0
  {
210
0
    mPromise->Resolve(aRegistration->Descriptor(), __func__);
211
0
  }
212
213
  void
214
  UpdateFailed(ErrorResult& aStatus) override
215
0
  {
216
0
    mPromise->Reject(std::move(aStatus), __func__);
217
0
  }
218
219
  RefPtr<ServiceWorkerRegistrationPromise>
220
  Promise() const
221
0
  {
222
0
    return mPromise;
223
0
  }
224
};
225
226
class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
227
{
228
  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
229
  RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
230
231
0
  ~WorkerThreadUpdateCallback() = default;
232
233
public:
234
  WorkerThreadUpdateCallback(RefPtr<ThreadSafeWorkerRef>&& aWorkerRef,
235
                             ServiceWorkerRegistrationPromise::Private* aPromise)
236
    : mWorkerRef(std::move(aWorkerRef))
237
    , mPromise(aPromise)
238
0
  {
239
0
    MOZ_ASSERT(NS_IsMainThread());
240
0
  }
241
242
  void
243
  UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override
244
0
  {
245
0
    mPromise->Resolve(aRegistration->Descriptor(), __func__);
246
0
    mWorkerRef = nullptr;
247
0
  }
248
249
  void
250
  UpdateFailed(ErrorResult& aStatus) override
251
0
  {
252
0
    mPromise->Reject(std::move(aStatus), __func__);
253
0
    mWorkerRef = nullptr;
254
0
  }
255
};
256
257
class SWRUpdateRunnable final : public Runnable
258
{
259
  class TimerCallback final : public nsITimerCallback
260
  {
261
    RefPtr<ServiceWorkerPrivate> mPrivate;
262
    RefPtr<Runnable> mRunnable;
263
264
  public:
265
    TimerCallback(ServiceWorkerPrivate* aPrivate,
266
                  Runnable* aRunnable)
267
      : mPrivate(aPrivate)
268
      , mRunnable(aRunnable)
269
0
    {
270
0
      MOZ_ASSERT(mPrivate);
271
0
      MOZ_ASSERT(aRunnable);
272
0
    }
273
274
    NS_IMETHOD
275
    Notify(nsITimer *aTimer) override
276
0
    {
277
0
      mRunnable->Run();
278
0
      mPrivate->RemoveISupports(aTimer);
279
0
280
0
      return NS_OK;
281
0
    }
282
283
    NS_DECL_THREADSAFE_ISUPPORTS
284
285
  private:
286
    ~TimerCallback()
287
0
    { }
288
  };
289
290
public:
291
  SWRUpdateRunnable(StrongWorkerRef* aWorkerRef,
292
                    ServiceWorkerRegistrationPromise::Private* aPromise,
293
                    const ServiceWorkerDescriptor& aDescriptor)
294
    : Runnable("dom::SWRUpdateRunnable")
295
    , mMutex("SWRUpdateRunnable")
296
    , mWorkerRef(new ThreadSafeWorkerRef(aWorkerRef))
297
    , mPromise(aPromise)
298
    , mDescriptor(aDescriptor)
299
    , mDelayed(false)
300
0
  {
301
0
    MOZ_DIAGNOSTIC_ASSERT(mWorkerRef);
302
0
    MOZ_DIAGNOSTIC_ASSERT(mPromise);
303
0
  }
304
305
  NS_IMETHOD
306
  Run() override
307
0
  {
308
0
    MOZ_ASSERT(NS_IsMainThread());
309
0
    ErrorResult result;
310
0
311
0
    nsCOMPtr<nsIPrincipal> principal = mDescriptor.GetPrincipal();
312
0
    if (NS_WARN_IF(!principal)) {
313
0
      mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
314
0
      return NS_OK;
315
0
    }
316
0
317
0
    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
318
0
    if (NS_WARN_IF(!swm)) {
319
0
      mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
320
0
      return NS_OK;
321
0
    }
322
0
323
0
    // This will delay update jobs originating from a service worker thread.
324
0
    // We don't currently handle ServiceWorkerRegistration.update() from other
325
0
    // worker types. Also, we assume this registration matches self.registration
326
0
    // on the service worker global. This is ok for now because service worker globals
327
0
    // are the only worker contexts where we expose ServiceWorkerRegistration.
328
0
    RefPtr<ServiceWorkerRegistrationInfo> registration =
329
0
      swm->GetRegistration(principal, mDescriptor.Scope());
330
0
    if (NS_WARN_IF(!registration)) {
331
0
      return NS_OK;
332
0
    }
333
0
334
0
    RefPtr<ServiceWorkerInfo> worker = registration->GetByDescriptor(mDescriptor);
335
0
    uint32_t delay = registration->GetUpdateDelay();
336
0
337
0
    // if we have a timer object, it means we've already been delayed once.
338
0
    if (delay && !mDelayed) {
339
0
      nsCOMPtr<nsITimerCallback> cb = new TimerCallback(worker->WorkerPrivate(), this);
340
0
      Result<nsCOMPtr<nsITimer>, nsresult> result =
341
0
        NS_NewTimerWithCallback(cb, delay, nsITimer::TYPE_ONE_SHOT,
342
0
                                SystemGroup::EventTargetFor(TaskCategory::Other));
343
0
344
0
      nsCOMPtr<nsITimer> timer = result.unwrapOr(nullptr);
345
0
      if (NS_WARN_IF(!timer)) {
346
0
        return NS_OK;
347
0
      }
348
0
349
0
      mDelayed = true;
350
0
351
0
      // We're storing the timer object on the calling service worker's private.
352
0
      // ServiceWorkerPrivate will drop the reference if the worker terminates,
353
0
      // which will cancel the timer.
354
0
      if (!worker->WorkerPrivate()->MaybeStoreISupports(timer)) {
355
0
        // The worker thread is already shutting down.  Just cancel the timer
356
0
        // and let the update runnable be destroyed.
357
0
        timer->Cancel();
358
0
        return NS_OK;
359
0
      }
360
0
361
0
      return NS_OK;
362
0
    }
363
0
364
0
    RefPtr<ServiceWorkerRegistrationPromise::Private> promise;
365
0
    {
366
0
      MutexAutoLock lock(mMutex);
367
0
      promise.swap(mPromise);
368
0
    }
369
0
370
0
    RefPtr<WorkerThreadUpdateCallback> cb =
371
0
      new WorkerThreadUpdateCallback(std::move(mWorkerRef), promise);
372
0
    UpdateInternal(principal, mDescriptor.Scope(), cb);
373
0
374
0
    return NS_OK;
375
0
  }
376
377
private:
378
  ~SWRUpdateRunnable()
379
0
  {
380
0
    MutexAutoLock lock(mMutex);
381
0
    if (mPromise) {
382
0
      mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
383
0
    }
384
0
  }
385
386
  // Protects promise access across threads
387
  Mutex mMutex;
388
389
  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
390
  RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
391
  const ServiceWorkerDescriptor mDescriptor;
392
  bool mDelayed;
393
};
394
395
NS_IMPL_ISUPPORTS(SWRUpdateRunnable::TimerCallback, nsITimerCallback)
396
397
class UnregisterCallback final : public nsIServiceWorkerUnregisterCallback
398
{
399
  RefPtr<GenericPromise::Private> mPromise;
400
401
public:
402
  NS_DECL_ISUPPORTS
403
404
  UnregisterCallback()
405
    : mPromise(new GenericPromise::Private(__func__))
406
0
  {
407
0
  }
408
409
  NS_IMETHOD
410
  UnregisterSucceeded(bool aState) override
411
0
  {
412
0
    mPromise->Resolve(aState, __func__);
413
0
    return NS_OK;
414
0
  }
415
416
  NS_IMETHOD
417
  UnregisterFailed() override
418
0
  {
419
0
    mPromise->Reject(NS_ERROR_DOM_SECURITY_ERR, __func__);
420
0
    return NS_OK;
421
0
  }
422
423
  RefPtr<GenericPromise>
424
  Promise() const
425
0
  {
426
0
    return mPromise;
427
0
  }
428
429
private:
430
0
  ~UnregisterCallback() = default;
431
};
432
433
NS_IMPL_ISUPPORTS(UnregisterCallback, nsIServiceWorkerUnregisterCallback)
434
435
class WorkerUnregisterCallback final : public nsIServiceWorkerUnregisterCallback
436
{
437
  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
438
  RefPtr<GenericPromise::Private> mPromise;
439
public:
440
  NS_DECL_ISUPPORTS
441
442
  WorkerUnregisterCallback(RefPtr<ThreadSafeWorkerRef>&& aWorkerRef,
443
                           RefPtr<GenericPromise::Private>&& aPromise)
444
    : mWorkerRef(std::move(aWorkerRef))
445
    , mPromise(std::move(aPromise))
446
0
  {
447
0
    MOZ_DIAGNOSTIC_ASSERT(mWorkerRef);
448
0
    MOZ_DIAGNOSTIC_ASSERT(mPromise);
449
0
  }
450
451
  NS_IMETHOD
452
  UnregisterSucceeded(bool aState) override
453
0
  {
454
0
    mPromise->Resolve(aState, __func__);
455
0
    mWorkerRef = nullptr;
456
0
    return NS_OK;
457
0
  }
458
459
  NS_IMETHOD
460
  UnregisterFailed() override
461
0
  {
462
0
    mPromise->Reject(NS_ERROR_DOM_SECURITY_ERR, __func__);
463
0
    mWorkerRef = nullptr;
464
0
    return NS_OK;
465
0
  }
466
467
private:
468
0
  ~WorkerUnregisterCallback() = default;
469
};
470
471
NS_IMPL_ISUPPORTS(WorkerUnregisterCallback, nsIServiceWorkerUnregisterCallback);
472
473
/*
474
 * If the worker goes away, we still continue to unregister, but we don't try to
475
 * resolve the worker Promise (which doesn't exist by that point).
476
 */
477
class StartUnregisterRunnable final : public Runnable
478
{
479
  // The promise is protected by the mutex.
480
  Mutex mMutex;
481
482
  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
483
  RefPtr<GenericPromise::Private> mPromise;
484
  const ServiceWorkerRegistrationDescriptor mDescriptor;
485
486
  ~StartUnregisterRunnable()
487
0
  {
488
0
    MutexAutoLock lock(mMutex);
489
0
    if (mPromise) {
490
0
      mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
491
0
    }
492
0
  }
493
494
public:
495
  StartUnregisterRunnable(StrongWorkerRef* aWorkerRef,
496
                          GenericPromise::Private* aPromise,
497
                          const ServiceWorkerRegistrationDescriptor& aDescriptor)
498
    : Runnable("dom::StartUnregisterRunnable")
499
    , mMutex("StartUnregisterRunnable")
500
    , mWorkerRef(new ThreadSafeWorkerRef(aWorkerRef))
501
    , mPromise(aPromise)
502
    , mDescriptor(aDescriptor)
503
0
  {
504
0
    MOZ_DIAGNOSTIC_ASSERT(mWorkerRef);
505
0
    MOZ_DIAGNOSTIC_ASSERT(mPromise);
506
0
  }
507
508
  NS_IMETHOD
509
  Run() override
510
0
  {
511
0
    MOZ_ASSERT(NS_IsMainThread());
512
0
513
0
    nsCOMPtr<nsIPrincipal> principal = mDescriptor.GetPrincipal();
514
0
    if (!principal) {
515
0
      mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
516
0
      return NS_OK;
517
0
    }
518
0
519
0
    nsCOMPtr<nsIServiceWorkerManager> swm =
520
0
      mozilla::services::GetServiceWorkerManager();
521
0
    if (!swm) {
522
0
      mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
523
0
      return NS_OK;
524
0
    }
525
0
526
0
    RefPtr<GenericPromise::Private> promise;
527
0
    {
528
0
      MutexAutoLock lock(mMutex);
529
0
      promise = mPromise.forget();
530
0
    }
531
0
532
0
    RefPtr<WorkerUnregisterCallback> cb =
533
0
      new WorkerUnregisterCallback(std::move(mWorkerRef), std::move(promise));
534
0
535
0
    nsresult rv = swm->Unregister(principal,
536
0
                                  cb,
537
0
                                  NS_ConvertUTF8toUTF16(mDescriptor.Scope()));
538
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
539
0
      mPromise->Reject(rv, __func__);
540
0
      return NS_OK;
541
0
    }
542
0
543
0
    return NS_OK;
544
0
  }
545
};
546
547
} // namespace
548
549
void
550
ServiceWorkerRegistrationMainThread::Update(ServiceWorkerRegistrationCallback&& aSuccessCB,
551
                                            ServiceWorkerFailureCallback&& aFailureCB)
552
0
{
553
0
  MOZ_ASSERT(NS_IsMainThread());
554
0
  MOZ_DIAGNOSTIC_ASSERT(mOuter);
555
0
556
0
  nsIGlobalObject* global = mOuter->GetParentObject();
557
0
  if (!global) {
558
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
559
0
    return;
560
0
  }
561
0
562
0
  nsCOMPtr<nsIPrincipal> principal = mDescriptor.GetPrincipal();
563
0
  if (!principal) {
564
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
565
0
    return;
566
0
  }
567
0
568
0
  RefPtr<MainThreadUpdateCallback> cb = new MainThreadUpdateCallback();
569
0
  UpdateInternal(principal, NS_ConvertUTF16toUTF8(mScope), cb);
570
0
571
0
  auto holder =
572
0
    MakeRefPtr<DOMMozPromiseRequestHolder<ServiceWorkerRegistrationPromise>>(global);
573
0
574
0
  cb->Promise()->Then(
575
0
    global->EventTargetFor(TaskCategory::Other), __func__,
576
0
    [successCB = std::move(aSuccessCB), holder] (const ServiceWorkerRegistrationDescriptor& aDescriptor) {
577
0
      holder->Complete();
578
0
      successCB(aDescriptor);
579
0
    }, [failureCB = std::move(aFailureCB), holder] (const CopyableErrorResult& aRv) {
580
0
      holder->Complete();
581
0
      failureCB(CopyableErrorResult(aRv));
582
0
    })->Track(*holder);
583
0
}
584
585
void
586
ServiceWorkerRegistrationMainThread::Unregister(ServiceWorkerBoolCallback&& aSuccessCB,
587
                                                ServiceWorkerFailureCallback&& aFailureCB)
588
0
{
589
0
  MOZ_ASSERT(NS_IsMainThread());
590
0
  MOZ_DIAGNOSTIC_ASSERT(mOuter);
591
0
592
0
  nsIGlobalObject* global = mOuter->GetParentObject();
593
0
  if (!global) {
594
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
595
0
    return;
596
0
  }
597
0
598
0
  nsCOMPtr<nsIServiceWorkerManager> swm =
599
0
    mozilla::services::GetServiceWorkerManager();
600
0
  if (!swm) {
601
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
602
0
    return;
603
0
  }
604
0
605
0
  nsCOMPtr<nsIPrincipal> principal = mDescriptor.GetPrincipal();
606
0
  if (!principal) {
607
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
608
0
    return;
609
0
  }
610
0
611
0
  RefPtr<UnregisterCallback> cb = new UnregisterCallback();
612
0
613
0
  nsresult rv = swm->Unregister(principal, cb,
614
0
                                NS_ConvertUTF8toUTF16(mDescriptor.Scope()));
615
0
  if (NS_FAILED(rv)) {
616
0
    aFailureCB(CopyableErrorResult(rv));
617
0
    return;
618
0
  }
619
0
620
0
  auto holder = MakeRefPtr<DOMMozPromiseRequestHolder<GenericPromise>>(global);
621
0
622
0
  cb->Promise()->Then(
623
0
    global->EventTargetFor(TaskCategory::Other), __func__,
624
0
    [successCB = std::move(aSuccessCB), holder] (bool aResult) {
625
0
      holder->Complete();
626
0
      successCB(aResult);
627
0
    }, [failureCB = std::move(aFailureCB), holder] (nsresult aRv) {
628
0
      holder->Complete();
629
0
      failureCB(CopyableErrorResult(aRv));
630
0
    })->Track(*holder);
631
0
}
632
633
////////////////////////////////////////////////////
634
// Worker Thread implementation
635
636
class WorkerListener final : public ServiceWorkerRegistrationListener
637
{
638
  ServiceWorkerRegistrationDescriptor mDescriptor;
639
  nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mInfo;
640
  nsCOMPtr<nsISerialEventTarget> mEventTarget;
641
  bool mListeningForEvents;
642
643
  // Set and unset on worker thread, used on main-thread and protected by mutex.
644
  ServiceWorkerRegistrationWorkerThread* mRegistration;
645
646
  Mutex mMutex;
647
648
public:
649
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerListener, override)
650
651
  WorkerListener(ServiceWorkerRegistrationWorkerThread* aReg,
652
                 const ServiceWorkerRegistrationDescriptor& aDescriptor,
653
                 nsISerialEventTarget* aEventTarget)
654
    : mDescriptor(aDescriptor)
655
    , mEventTarget(aEventTarget)
656
    , mListeningForEvents(false)
657
    , mRegistration(aReg)
658
    , mMutex("WorkerListener::mMutex")
659
0
  {
660
0
    MOZ_ASSERT(IsCurrentThreadRunningWorker());
661
0
    MOZ_ASSERT(mEventTarget);
662
0
    MOZ_ASSERT(mRegistration);
663
0
  }
664
665
  void
666
  StartListeningForEvents()
667
0
  {
668
0
    MOZ_ASSERT(NS_IsMainThread());
669
0
    MOZ_DIAGNOSTIC_ASSERT(!mListeningForEvents);
670
0
    MOZ_DIAGNOSTIC_ASSERT(!mInfo);
671
0
672
0
    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
673
0
    NS_ENSURE_TRUE_VOID(swm);
674
0
675
0
    RefPtr<ServiceWorkerRegistrationInfo> info =
676
0
      swm->GetRegistration(mDescriptor.PrincipalInfo(), mDescriptor.Scope());
677
0
    NS_ENSURE_TRUE_VOID(info);
678
0
679
0
    mInfo = new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
680
0
      "WorkerListener::mInfo", info);
681
0
682
0
    mInfo->AddInstance(this, mDescriptor);
683
0
    mListeningForEvents = true;
684
0
  }
685
686
  void
687
  StopListeningForEvents()
688
0
  {
689
0
    MOZ_ASSERT(NS_IsMainThread());
690
0
691
0
    if (!mListeningForEvents) {
692
0
      return;
693
0
    }
694
0
695
0
    MOZ_DIAGNOSTIC_ASSERT(mInfo);
696
0
    mInfo->RemoveInstance(this);
697
0
    mListeningForEvents = false;
698
0
  }
699
700
  // ServiceWorkerRegistrationListener
701
  void
702
  UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) override
703
0
  {
704
0
    MOZ_ASSERT(NS_IsMainThread());
705
0
706
0
    mDescriptor = aDescriptor;
707
0
708
0
    nsCOMPtr<nsIRunnable> r =
709
0
      NewCancelableRunnableMethod<ServiceWorkerRegistrationDescriptor>(
710
0
        "WorkerListener::UpdateState",
711
0
        this,
712
0
        &WorkerListener::UpdateStateOnWorkerThread,
713
0
        aDescriptor);
714
0
715
0
    Unused << mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
716
0
  }
717
718
  void
719
  UpdateStateOnWorkerThread(const ServiceWorkerRegistrationDescriptor& aDescriptor)
720
0
  {
721
0
    MOZ_ASSERT(IsCurrentThreadRunningWorker());
722
0
    if (mRegistration) {
723
0
      mRegistration->UpdateState(aDescriptor);
724
0
    }
725
0
  }
726
727
  void
728
  RegistrationRemoved() override;
729
730
  void
731
  GetScope(nsAString& aScope) const override
732
0
  {
733
0
    CopyUTF8toUTF16(mDescriptor.Scope(), aScope);
734
0
  }
735
736
  bool
737
  MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) override
738
0
  {
739
0
    // TODO: Not implemented
740
0
    return false;
741
0
  }
742
743
  void
744
  ClearRegistration()
745
0
  {
746
0
    MOZ_ASSERT(IsCurrentThreadRunningWorker());
747
0
    MutexAutoLock lock(mMutex);
748
0
    mRegistration = nullptr;
749
0
  }
750
751
private:
752
  ~WorkerListener()
753
0
  {
754
0
    MOZ_ASSERT(!mListeningForEvents);
755
0
  }
756
};
757
758
ServiceWorkerRegistrationWorkerThread::ServiceWorkerRegistrationWorkerThread(const ServiceWorkerRegistrationDescriptor& aDescriptor)
759
  : mOuter(nullptr)
760
  , mDescriptor(aDescriptor)
761
  , mScope(NS_ConvertUTF8toUTF16(aDescriptor.Scope()))
762
0
{
763
0
}
764
765
ServiceWorkerRegistrationWorkerThread::~ServiceWorkerRegistrationWorkerThread()
766
0
{
767
0
  MOZ_DIAGNOSTIC_ASSERT(!mListener);
768
0
  MOZ_DIAGNOSTIC_ASSERT(!mOuter);
769
0
}
770
771
void
772
ServiceWorkerRegistrationWorkerThread::RegistrationRemoved()
773
0
{
774
0
  // The SWM notifying us that the registration was removed on the MT may
775
0
  // race with ClearServiceWorkerRegistration() on the worker thread.  So
776
0
  // double-check that mOuter is still valid.
777
0
  if (mOuter) {
778
0
    mOuter->RegistrationRemoved();
779
0
  }
780
0
}
781
782
void
783
ServiceWorkerRegistrationWorkerThread::SetServiceWorkerRegistration(ServiceWorkerRegistration* aReg)
784
0
{
785
0
  MOZ_DIAGNOSTIC_ASSERT(aReg);
786
0
  MOZ_DIAGNOSTIC_ASSERT(!mOuter);
787
0
  mOuter = aReg;
788
0
  InitListener();
789
0
}
790
791
void
792
ServiceWorkerRegistrationWorkerThread::ClearServiceWorkerRegistration(ServiceWorkerRegistration* aReg)
793
0
{
794
0
  MOZ_ASSERT_IF(mOuter, mOuter == aReg);
795
0
  ReleaseListener();
796
0
  mOuter = nullptr;
797
0
}
798
799
void
800
ServiceWorkerRegistrationWorkerThread::Update(ServiceWorkerRegistrationCallback&& aSuccessCB,
801
                                              ServiceWorkerFailureCallback&& aFailureCB)
802
0
{
803
0
  if (NS_WARN_IF(!mWorkerRef->GetPrivate())) {
804
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
805
0
    return;
806
0
  }
807
0
808
0
  RefPtr<StrongWorkerRef> workerRef =
809
0
    StrongWorkerRef::Create(mWorkerRef->GetPrivate(),
810
0
                            "ServiceWorkerRegistration::Update");
811
0
  if (NS_WARN_IF(!workerRef)) {
812
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
813
0
    return;
814
0
  }
815
0
816
0
  nsIGlobalObject* global = workerRef->Private()->GlobalScope();
817
0
  if (NS_WARN_IF(!global)) {
818
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
819
0
    return;
820
0
  }
821
0
822
0
  // Eventually we need to support all workers, but for right now this
823
0
  // code assumes we're on a service worker global as self.registration.
824
0
  if (NS_WARN_IF(!workerRef->Private()->IsServiceWorker())) {
825
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
826
0
    return;
827
0
  }
828
0
829
0
  // Avoid infinite update loops by ignoring update() calls during top
830
0
  // level script evaluation.  See:
831
0
  // https://github.com/slightlyoff/ServiceWorker/issues/800
832
0
  if (workerRef->Private()->IsLoadingWorkerScript()) {
833
0
    aSuccessCB(mDescriptor);
834
0
    return;
835
0
  }
836
0
837
0
  auto promise = MakeRefPtr<ServiceWorkerRegistrationPromise::Private>(__func__);
838
0
  auto holder =
839
0
    MakeRefPtr<DOMMozPromiseRequestHolder<ServiceWorkerRegistrationPromise>>(global);
840
0
841
0
  promise->Then(
842
0
    global->EventTargetFor(TaskCategory::Other), __func__,
843
0
    [successCB = std::move(aSuccessCB), holder] (const ServiceWorkerRegistrationDescriptor& aDescriptor) {
844
0
      holder->Complete();
845
0
      successCB(aDescriptor);
846
0
    }, [failureCB = std::move(aFailureCB), holder] (const CopyableErrorResult& aRv) {
847
0
      holder->Complete();
848
0
      failureCB(CopyableErrorResult(aRv));
849
0
    })->Track(*holder);
850
0
851
0
  RefPtr<SWRUpdateRunnable> r =
852
0
    new SWRUpdateRunnable(workerRef,
853
0
                          promise,
854
0
                          workerRef->Private()->GetServiceWorkerDescriptor());
855
0
856
0
  nsresult rv = workerRef->Private()->DispatchToMainThread(r.forget());
857
0
  if (NS_FAILED(rv)) {
858
0
    promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
859
0
    return;
860
0
  }
861
0
}
862
863
void
864
ServiceWorkerRegistrationWorkerThread::Unregister(ServiceWorkerBoolCallback&& aSuccessCB,
865
                                                  ServiceWorkerFailureCallback&& aFailureCB)
866
0
{
867
0
  if (NS_WARN_IF(!mWorkerRef->GetPrivate())) {
868
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
869
0
    return;
870
0
  }
871
0
872
0
  RefPtr<StrongWorkerRef> workerRef =
873
0
    StrongWorkerRef::Create(mWorkerRef->GetPrivate(), __func__);
874
0
  if (NS_WARN_IF(!workerRef)) {
875
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
876
0
    return;
877
0
  }
878
0
879
0
  // Eventually we need to support all workers, but for right now this
880
0
  // code assumes we're on a service worker global as self.registration.
881
0
  if (NS_WARN_IF(!workerRef->Private()->IsServiceWorker())) {
882
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
883
0
    return;
884
0
  }
885
0
886
0
  nsIGlobalObject* global = workerRef->Private()->GlobalScope();
887
0
  if (!global) {
888
0
    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
889
0
    return;
890
0
  }
891
0
892
0
  auto promise = MakeRefPtr<GenericPromise::Private>(__func__);
893
0
  auto holder = MakeRefPtr<DOMMozPromiseRequestHolder<GenericPromise>>(global);
894
0
895
0
  promise->Then(
896
0
    global->EventTargetFor(TaskCategory::Other), __func__,
897
0
    [successCB = std::move(aSuccessCB), holder] (bool aResult) {
898
0
      holder->Complete();
899
0
      successCB(aResult);
900
0
    }, [failureCB = std::move(aFailureCB), holder] (nsresult aRv) {
901
0
      holder->Complete();
902
0
      failureCB(CopyableErrorResult(aRv));
903
0
    })->Track(*holder);
904
0
905
0
  RefPtr<StartUnregisterRunnable> r =
906
0
    new StartUnregisterRunnable(workerRef, promise, mDescriptor);
907
0
908
0
  nsresult rv = workerRef->Private()->DispatchToMainThread(r);
909
0
  if (NS_FAILED(rv)) {
910
0
    promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
911
0
    return;
912
0
  }
913
0
}
914
915
void
916
ServiceWorkerRegistrationWorkerThread::InitListener()
917
0
{
918
0
  MOZ_ASSERT(!mListener);
919
0
  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
920
0
  MOZ_ASSERT(worker);
921
0
  worker->AssertIsOnWorkerThread();
922
0
923
0
  RefPtr<ServiceWorkerRegistrationWorkerThread> self = this;
924
0
  mWorkerRef = WeakWorkerRef::Create(worker, [self]() {
925
0
    self->ReleaseListener();
926
0
927
0
    // Break the ref-cycle immediately when the worker thread starts to
928
0
    // teardown.  We must make sure its GC'd before the worker RuntimeService is
929
0
    // destroyed.  The WorkerListener may not be able to post a runnable
930
0
    // clearing this value after shutdown begins and thus delaying cleanup too
931
0
    // late.
932
0
    self->mOuter = nullptr;
933
0
  });
934
0
935
0
  if (NS_WARN_IF(!mWorkerRef)) {
936
0
    return;
937
0
  }
938
0
939
0
  mListener = new WorkerListener(this, mDescriptor, worker->HybridEventTarget());
940
0
941
0
  nsCOMPtr<nsIRunnable> r =
942
0
    NewRunnableMethod("dom::WorkerListener::StartListeningForEvents",
943
0
                      mListener,
944
0
                      &WorkerListener::StartListeningForEvents);
945
0
  MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
946
0
}
947
948
void
949
ServiceWorkerRegistrationWorkerThread::ReleaseListener()
950
0
{
951
0
  if (!mListener) {
952
0
    return;
953
0
  }
954
0
955
0
  MOZ_ASSERT(IsCurrentThreadRunningWorker());
956
0
957
0
  mListener->ClearRegistration();
958
0
959
0
  nsCOMPtr<nsIRunnable> r =
960
0
    NewCancelableRunnableMethod("dom::WorkerListener::StopListeningForEvents",
961
0
                                mListener,
962
0
                                &WorkerListener::StopListeningForEvents);
963
0
  // Calling GetPrivate() is safe because this method is called when the
964
0
  // WorkerRef is notified.
965
0
  MOZ_ALWAYS_SUCCEEDS(mWorkerRef->GetPrivate()->DispatchToMainThread(r.forget()));
966
0
967
0
  mListener = nullptr;
968
0
  mWorkerRef = nullptr;
969
0
}
970
971
void
972
ServiceWorkerRegistrationWorkerThread::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor)
973
0
{
974
0
  if (mOuter) {
975
0
    mOuter->UpdateState(aDescriptor);
976
0
  }
977
0
}
978
979
class RegistrationRemovedWorkerRunnable final : public WorkerRunnable
980
{
981
  RefPtr<WorkerListener> mListener;
982
public:
983
  RegistrationRemovedWorkerRunnable(WorkerPrivate* aWorkerPrivate,
984
                                    WorkerListener* aListener)
985
    : WorkerRunnable(aWorkerPrivate)
986
    , mListener(aListener)
987
0
  {
988
0
    // Need this assertion for now since runnables which modify busy count can
989
0
    // only be dispatched from parent thread to worker thread and we don't deal
990
0
    // with nested workers. SW threads can't be nested.
991
0
    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
992
0
  }
993
994
  bool
995
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
996
0
  {
997
0
    MOZ_ASSERT(aWorkerPrivate);
998
0
    aWorkerPrivate->AssertIsOnWorkerThread();
999
0
    mListener->RegistrationRemoved();
1000
0
    return true;
1001
0
  }
1002
};
1003
1004
void
1005
WorkerListener::RegistrationRemoved()
1006
0
{
1007
0
  MutexAutoLock lock(mMutex);
1008
0
  if (!mRegistration) {
1009
0
    return;
1010
0
  }
1011
0
1012
0
  if (NS_IsMainThread()) {
1013
0
    RefPtr<WorkerRunnable> r =
1014
0
      new RegistrationRemovedWorkerRunnable(mRegistration->GetWorkerPrivate(lock), this);
1015
0
    Unused << r->Dispatch();
1016
0
1017
0
    StopListeningForEvents();
1018
0
    return;
1019
0
  }
1020
0
1021
0
  mRegistration->RegistrationRemoved();
1022
0
}
1023
1024
WorkerPrivate*
1025
ServiceWorkerRegistrationWorkerThread::GetWorkerPrivate(const MutexAutoLock& aProofOfLock)
1026
0
{
1027
0
  // In this case, calling GetUnsafePrivate() is ok because we have a proof of
1028
0
  // mutex lock.
1029
0
  MOZ_ASSERT(mWorkerRef && mWorkerRef->GetUnsafePrivate());
1030
0
  return mWorkerRef->GetUnsafePrivate();
1031
0
}
1032
1033
} // dom namespace
1034
} // mozilla namespace