Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/serviceworkers/ServiceWorkerRegistration.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 "ServiceWorkerRegistration.h"
8
9
#include "mozilla/dom/DOMMozPromiseRequestHolder.h"
10
#include "mozilla/dom/Notification.h"
11
#include "mozilla/dom/Promise.h"
12
#include "mozilla/dom/PushManager.h"
13
#include "mozilla/dom/ServiceWorker.h"
14
#include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
15
#include "mozilla/dom/WorkerPrivate.h"
16
#include "nsCycleCollectionParticipant.h"
17
#include "nsISupportsPrimitives.h"
18
#include "nsPIDOMWindow.h"
19
#include "RemoteServiceWorkerRegistrationImpl.h"
20
#include "ServiceWorkerRegistrationImpl.h"
21
22
namespace mozilla {
23
namespace dom {
24
25
NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistration,
26
                                   DOMEventTargetHelper,
27
                                   mInstallingWorker,
28
                                   mWaitingWorker,
29
                                   mActiveWorker,
30
                                   mPushManager);
31
32
NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
33
NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
34
35
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerRegistration)
36
0
  NS_INTERFACE_MAP_ENTRY(ServiceWorkerRegistration)
37
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
38
39
namespace {
40
const uint64_t kInvalidUpdateFoundId = 0;
41
} // anonymous namespace
42
43
ServiceWorkerRegistration::ServiceWorkerRegistration(nsIGlobalObject* aGlobal,
44
                                                     const ServiceWorkerRegistrationDescriptor& aDescriptor,
45
                                                     ServiceWorkerRegistration::Inner* aInner)
46
  : DOMEventTargetHelper(aGlobal)
47
  , mDescriptor(aDescriptor)
48
  , mInner(aInner)
49
  , mScheduledUpdateFoundId(kInvalidUpdateFoundId)
50
  , mDispatchedUpdateFoundId(kInvalidUpdateFoundId)
51
  , mPendingUpdatePromises(0)
52
0
{
53
0
  MOZ_DIAGNOSTIC_ASSERT(mInner);
54
0
55
0
  KeepAliveIfHasListenersFor(NS_LITERAL_STRING("updatefound"));
56
0
57
0
  UpdateState(mDescriptor);
58
0
  mInner->SetServiceWorkerRegistration(this);
59
0
}
60
61
ServiceWorkerRegistration::~ServiceWorkerRegistration()
62
0
{
63
0
  mInner->ClearServiceWorkerRegistration(this);
64
0
}
65
66
JSObject*
67
ServiceWorkerRegistration::WrapObject(JSContext* aCx,
68
                                      JS::Handle<JSObject*> aGivenProto)
69
0
{
70
0
  return ServiceWorkerRegistration_Binding::Wrap(aCx, this, aGivenProto);
71
0
}
72
73
/* static */ already_AddRefed<ServiceWorkerRegistration>
74
ServiceWorkerRegistration::CreateForMainThread(nsPIDOMWindowInner* aWindow,
75
                                               const ServiceWorkerRegistrationDescriptor& aDescriptor)
76
0
{
77
0
  MOZ_ASSERT(aWindow);
78
0
  MOZ_ASSERT(NS_IsMainThread());
79
0
80
0
  RefPtr<Inner> inner;
81
0
  if (ServiceWorkerParentInterceptEnabled()) {
82
0
    inner = new RemoteServiceWorkerRegistrationImpl(aDescriptor);
83
0
  } else {
84
0
    inner = new ServiceWorkerRegistrationMainThread(aDescriptor);
85
0
  }
86
0
  NS_ENSURE_TRUE(inner, nullptr);
87
0
88
0
  RefPtr<ServiceWorkerRegistration> registration =
89
0
    new ServiceWorkerRegistration(aWindow->AsGlobal(), aDescriptor, inner);
90
0
91
0
  return registration.forget();
92
0
}
93
94
/* static */ already_AddRefed<ServiceWorkerRegistration>
95
ServiceWorkerRegistration::CreateForWorker(WorkerPrivate* aWorkerPrivate,
96
                                           nsIGlobalObject* aGlobal,
97
                                           const ServiceWorkerRegistrationDescriptor& aDescriptor)
98
0
{
99
0
  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
100
0
  MOZ_DIAGNOSTIC_ASSERT(aGlobal);
101
0
  aWorkerPrivate->AssertIsOnWorkerThread();
102
0
103
0
  RefPtr<Inner> inner;
104
0
  if (ServiceWorkerParentInterceptEnabled()) {
105
0
    inner = new RemoteServiceWorkerRegistrationImpl(aDescriptor);
106
0
  } else {
107
0
    inner = new ServiceWorkerRegistrationWorkerThread(aDescriptor);
108
0
  }
109
0
  NS_ENSURE_TRUE(inner, nullptr);
110
0
111
0
  RefPtr<ServiceWorkerRegistration> registration =
112
0
    new ServiceWorkerRegistration(aGlobal, aDescriptor, inner);
113
0
114
0
  return registration.forget();
115
0
}
116
117
void
118
ServiceWorkerRegistration::DisconnectFromOwner()
119
0
{
120
0
  DOMEventTargetHelper::DisconnectFromOwner();
121
0
}
122
123
void
124
ServiceWorkerRegistration::RegistrationRemoved()
125
0
{
126
0
  // Its possible that the registration will fail to install and be
127
0
  // immediately removed.  In that case we may never receive the
128
0
  // UpdateState() call if the actor was too slow to connect, etc.
129
0
  // Ensure that we force all our known actors to redundant so that
130
0
  // the appropriate statechange events are fired.  If we got the
131
0
  // UpdateState() already then this will be a no-op.
132
0
  UpdateStateInternal(Maybe<ServiceWorkerDescriptor>(),
133
0
                      Maybe<ServiceWorkerDescriptor>(),
134
0
                      Maybe<ServiceWorkerDescriptor>());
135
0
136
0
  // Our underlying registration was removed from SWM, so we
137
0
  // will never get an updatefound event again.  We can let
138
0
  // the object GC if content is not holding it alive.
139
0
  IgnoreKeepAliveIfHasListenersFor(NS_LITERAL_STRING("updatefound"));
140
0
}
141
142
already_AddRefed<ServiceWorker>
143
ServiceWorkerRegistration::GetInstalling() const
144
0
{
145
0
  RefPtr<ServiceWorker> ref = mInstallingWorker;
146
0
  return ref.forget();
147
0
}
148
149
already_AddRefed<ServiceWorker>
150
ServiceWorkerRegistration::GetWaiting() const
151
0
{
152
0
  RefPtr<ServiceWorker> ref = mWaitingWorker;
153
0
  return ref.forget();
154
0
}
155
156
already_AddRefed<ServiceWorker>
157
ServiceWorkerRegistration::GetActive() const
158
0
{
159
0
  RefPtr<ServiceWorker> ref = mActiveWorker;
160
0
  return ref.forget();
161
0
}
162
163
void
164
ServiceWorkerRegistration::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor)
165
0
{
166
0
  MOZ_DIAGNOSTIC_ASSERT(MatchesDescriptor(aDescriptor));
167
0
168
0
  mDescriptor = aDescriptor;
169
0
170
0
  UpdateStateInternal(aDescriptor.GetInstalling(),
171
0
                      aDescriptor.GetWaiting(),
172
0
                      aDescriptor.GetActive());
173
0
174
0
  nsTArray<UniquePtr<VersionCallback>> callbackList;
175
0
  mVersionCallbackList.SwapElements(callbackList);
176
0
  for (auto& cb : callbackList) {
177
0
    if (cb->mVersion > mDescriptor.Version()) {
178
0
      mVersionCallbackList.AppendElement(std::move(cb));
179
0
      continue;
180
0
    }
181
0
182
0
    cb->mFunc(cb->mVersion == mDescriptor.Version());
183
0
  }
184
0
}
185
186
bool
187
ServiceWorkerRegistration::MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) const
188
0
{
189
0
  return aDescriptor.Id() == mDescriptor.Id() &&
190
0
         aDescriptor.PrincipalInfo() == mDescriptor.PrincipalInfo() &&
191
0
         aDescriptor.Scope() == mDescriptor.Scope();
192
0
}
193
194
void
195
ServiceWorkerRegistration::GetScope(nsAString& aScope) const
196
0
{
197
0
  CopyUTF8toUTF16(mDescriptor.Scope(), aScope);
198
0
}
199
200
ServiceWorkerUpdateViaCache
201
ServiceWorkerRegistration::GetUpdateViaCache(ErrorResult& aRv) const
202
0
{
203
0
  return mDescriptor.UpdateViaCache();
204
0
}
205
206
already_AddRefed<Promise>
207
ServiceWorkerRegistration::Update(ErrorResult& aRv)
208
0
{
209
0
  if (!mInner) {
210
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
211
0
    return nullptr;
212
0
  }
213
0
214
0
  nsIGlobalObject* global = GetParentObject();
215
0
  if (!global) {
216
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
217
0
    return nullptr;
218
0
  }
219
0
220
0
  RefPtr<Promise> outer = Promise::Create(global, aRv);
221
0
  if (NS_WARN_IF(aRv.Failed())) {
222
0
    return nullptr;
223
0
  }
224
0
225
0
  RefPtr<ServiceWorkerRegistration> self = this;
226
0
227
0
  mPendingUpdatePromises += 1;
228
0
229
0
  mInner->Update(
230
0
    [outer, self](const ServiceWorkerRegistrationDescriptor& aDesc) {
231
0
      auto scopeExit = MakeScopeExit([&] { self->UpdatePromiseSettled(); });
232
0
      nsIGlobalObject* global = self->GetParentObject();
233
0
      MOZ_DIAGNOSTIC_ASSERT(global);
234
0
      RefPtr<ServiceWorkerRegistration> ref =
235
0
        global->GetOrCreateServiceWorkerRegistration(aDesc);
236
0
      if (!ref) {
237
0
        outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
238
0
        return;
239
0
      }
240
0
      outer->MaybeResolve(ref);
241
0
    }, [outer, self] (ErrorResult& aRv) {
242
0
      auto scopeExit = MakeScopeExit([&] { self->UpdatePromiseSettled(); });
243
0
      outer->MaybeReject(aRv);
244
0
    });
245
0
246
0
  return outer.forget();
247
0
}
248
249
already_AddRefed<Promise>
250
ServiceWorkerRegistration::Unregister(ErrorResult& aRv)
251
0
{
252
0
  nsIGlobalObject* global = GetParentObject();
253
0
  if (NS_WARN_IF(!global)) {
254
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
255
0
    return nullptr;
256
0
  }
257
0
258
0
  RefPtr<Promise> outer = Promise::Create(global, aRv);
259
0
  if (NS_WARN_IF(aRv.Failed())) {
260
0
    return nullptr;
261
0
  }
262
0
263
0
  if (!mInner) {
264
0
    outer->MaybeResolve(false);
265
0
    return outer.forget();
266
0
  }
267
0
268
0
  mInner->Unregister(
269
0
    [outer] (bool aSuccess) {
270
0
      outer->MaybeResolve(aSuccess);
271
0
    }, [outer] (ErrorResult& aRv) {
272
0
      // register() should be resilient and resolve false instead
273
0
      // of rejecting in most cases.
274
0
      outer->MaybeResolve(false);
275
0
    });
276
0
277
0
  return outer.forget();
278
0
}
279
280
already_AddRefed<PushManager>
281
ServiceWorkerRegistration::GetPushManager(JSContext* aCx, ErrorResult& aRv)
282
0
{
283
0
  if (!mPushManager) {
284
0
    nsCOMPtr<nsIGlobalObject> globalObject = GetParentObject();
285
0
286
0
    if (!globalObject) {
287
0
      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
288
0
      return nullptr;
289
0
    }
290
0
291
0
    GlobalObject global(aCx, globalObject->GetGlobalJSObject());
292
0
    mPushManager =
293
0
      PushManager::Constructor(global,
294
0
                               NS_ConvertUTF8toUTF16(mDescriptor.Scope()),
295
0
                               aRv);
296
0
    if (aRv.Failed()) {
297
0
      return nullptr;
298
0
    }
299
0
  }
300
0
301
0
  RefPtr<PushManager> ret = mPushManager;
302
0
  return ret.forget();
303
0
}
304
305
already_AddRefed<Promise>
306
ServiceWorkerRegistration::ShowNotification(JSContext* aCx,
307
                                            const nsAString& aTitle,
308
                                            const NotificationOptions& aOptions,
309
                                            ErrorResult& aRv)
310
0
{
311
0
  nsIGlobalObject* global = GetParentObject();
312
0
  if (!global) {
313
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
314
0
    return nullptr;
315
0
  }
316
0
317
0
  NS_ConvertUTF8toUTF16 scope(mDescriptor.Scope());
318
0
319
0
  // Until we ship ServiceWorker objects on worker threads the active
320
0
  // worker will always be nullptr.  So limit this check to main
321
0
  // thread for now.
322
0
  if (mDescriptor.GetActive().isNothing() && NS_IsMainThread()) {
323
0
    aRv.ThrowTypeError<MSG_NO_ACTIVE_WORKER>(scope);
324
0
    return nullptr;
325
0
  }
326
0
327
0
  RefPtr<Promise> p =
328
0
    Notification::ShowPersistentNotification(aCx, global, scope,
329
0
                                             aTitle, aOptions, aRv);
330
0
  if (NS_WARN_IF(aRv.Failed())) {
331
0
    return nullptr;
332
0
  }
333
0
334
0
  return p.forget();
335
0
}
336
337
already_AddRefed<Promise>
338
ServiceWorkerRegistration::GetNotifications(const GetNotificationOptions& aOptions,
339
                                            ErrorResult& aRv)
340
0
{
341
0
  nsIGlobalObject* global = GetParentObject();
342
0
  if (!global) {
343
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
344
0
    return nullptr;
345
0
  }
346
0
347
0
  NS_ConvertUTF8toUTF16 scope(mDescriptor.Scope());
348
0
349
0
  if (NS_IsMainThread()) {
350
0
    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global);
351
0
    if (NS_WARN_IF(!window)) {
352
0
      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
353
0
      return nullptr;
354
0
    }
355
0
    return Notification::Get(window, aOptions, scope, aRv);
356
0
  }
357
0
358
0
  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
359
0
  worker->AssertIsOnWorkerThread();
360
0
  return Notification::WorkerGet(worker, aOptions, scope, aRv);
361
0
}
362
363
const ServiceWorkerRegistrationDescriptor&
364
ServiceWorkerRegistration::Descriptor() const
365
0
{
366
0
  return mDescriptor;
367
0
}
368
369
void
370
ServiceWorkerRegistration::WhenVersionReached(uint64_t aVersion,
371
                                              ServiceWorkerBoolCallback&& aCallback)
372
0
{
373
0
  if (aVersion <= mDescriptor.Version()) {
374
0
    aCallback(aVersion == mDescriptor.Version());
375
0
    return;
376
0
  }
377
0
378
0
  mVersionCallbackList.AppendElement(
379
0
    MakeUnique<VersionCallback>(aVersion, std::move(aCallback)));
380
0
}
381
382
void
383
ServiceWorkerRegistration::MaybeScheduleUpdateFound(const Maybe<ServiceWorkerDescriptor>& aInstallingDescriptor)
384
0
{
385
0
  uint64_t newId = aInstallingDescriptor.isSome()
386
0
                 ? aInstallingDescriptor.ref().Id()
387
0
                 : kInvalidUpdateFoundId;
388
0
389
0
  if (mScheduledUpdateFoundId != kInvalidUpdateFoundId) {
390
0
    if (mScheduledUpdateFoundId == newId) {
391
0
      return;
392
0
    }
393
0
    MaybeDispatchUpdateFound();
394
0
    MOZ_DIAGNOSTIC_ASSERT(mScheduledUpdateFoundId == kInvalidUpdateFoundId);
395
0
  }
396
0
397
0
  bool updateFound = newId != kInvalidUpdateFoundId &&
398
0
                     mDispatchedUpdateFoundId != newId;
399
0
400
0
  if (!updateFound) {
401
0
    return;
402
0
  }
403
0
404
0
  mScheduledUpdateFoundId = newId;
405
0
406
0
  if (mPendingUpdatePromises > 0) {
407
0
    return;
408
0
  }
409
0
410
0
  nsIGlobalObject* global = GetParentObject();
411
0
  NS_ENSURE_TRUE_VOID(global);
412
0
413
0
  nsCOMPtr<nsIRunnable> r = NewCancelableRunnableMethod(
414
0
    "ServiceWorkerRegistration::MaybeDispatchUpdateFound",
415
0
    this,
416
0
    &ServiceWorkerRegistration::MaybeDispatchUpdateFound);
417
0
418
0
  Unused << global->EventTargetFor(TaskCategory::Other)->Dispatch(
419
0
    r.forget(), NS_DISPATCH_NORMAL);
420
0
}
421
422
void
423
ServiceWorkerRegistration::MaybeDispatchUpdateFound()
424
0
{
425
0
  uint64_t scheduledId = mScheduledUpdateFoundId;
426
0
  mScheduledUpdateFoundId = kInvalidUpdateFoundId;
427
0
428
0
  if (scheduledId == kInvalidUpdateFoundId ||
429
0
      scheduledId == mDispatchedUpdateFoundId) {
430
0
    return;
431
0
  }
432
0
433
0
  mDispatchedUpdateFoundId = scheduledId;
434
0
  DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
435
0
}
436
437
void
438
ServiceWorkerRegistration::UpdatePromiseSettled()
439
0
{
440
0
  MOZ_DIAGNOSTIC_ASSERT(mPendingUpdatePromises > 0);
441
0
  mPendingUpdatePromises -= 1;
442
0
  if (mPendingUpdatePromises > 0 ||
443
0
      mScheduledUpdateFoundId == kInvalidUpdateFoundId) {
444
0
    return;
445
0
  }
446
0
447
0
  nsIGlobalObject* global = GetParentObject();
448
0
  NS_ENSURE_TRUE_VOID(global);
449
0
450
0
  nsCOMPtr<nsIRunnable> r = NewCancelableRunnableMethod(
451
0
    "ServiceWorkerRegistration::MaybeDispatchUpdateFound",
452
0
    this,
453
0
    &ServiceWorkerRegistration::MaybeDispatchUpdateFound);
454
0
455
0
  Unused << global->EventTargetFor(TaskCategory::Other)->Dispatch(
456
0
    r.forget(), NS_DISPATCH_NORMAL);
457
0
}
458
459
void
460
ServiceWorkerRegistration::UpdateStateInternal(const Maybe<ServiceWorkerDescriptor>& aInstalling,
461
                                               const Maybe<ServiceWorkerDescriptor>& aWaiting,
462
                                               const Maybe<ServiceWorkerDescriptor>& aActive)
463
0
{
464
0
  // Do this immediately as it may flush an already pending updatefound
465
0
  // event.  In that case we want to fire the pending event before
466
0
  // modifying any of the registration properties.
467
0
  MaybeScheduleUpdateFound(aInstalling);
468
0
469
0
  // Move the currently exposed workers into a separate list
470
0
  // of "old" workers.  We will then potentially add them
471
0
  // back to the registration properties below based on the
472
0
  // given descriptor.  Any that are not restored will need
473
0
  // to be moved to the redundant state.
474
0
  AutoTArray<RefPtr<ServiceWorker>, 3> oldWorkerList({
475
0
    mInstallingWorker.forget(),
476
0
    mWaitingWorker.forget(),
477
0
    mActiveWorker.forget(),
478
0
  });
479
0
480
0
  // Its important that all state changes are actually applied before
481
0
  // dispatching any statechange events.  Each ServiceWorker object
482
0
  // should be in the correct state and the ServiceWorkerRegistration
483
0
  // properties need to be set correctly as well.  To accomplish this
484
0
  // we use a ScopeExit to dispatch any statechange events.
485
0
  auto scopeExit = MakeScopeExit([&] {
486
0
    // Check to see if any of the "old" workers was completely discarded.
487
0
    // Set these workers to the redundant state.
488
0
    for (auto& oldWorker : oldWorkerList) {
489
0
      if (!oldWorker ||
490
0
          oldWorker == mInstallingWorker ||
491
0
          oldWorker == mWaitingWorker ||
492
0
          oldWorker == mActiveWorker) {
493
0
        continue;
494
0
      }
495
0
496
0
      oldWorker->SetState(ServiceWorkerState::Redundant);
497
0
    }
498
0
499
0
    // Check each worker to see if it needs a statechange event dispatched.
500
0
    if (mInstallingWorker) {
501
0
      mInstallingWorker->MaybeDispatchStateChangeEvent();
502
0
    }
503
0
    if (mWaitingWorker) {
504
0
      mWaitingWorker->MaybeDispatchStateChangeEvent();
505
0
    }
506
0
    if (mActiveWorker) {
507
0
      mActiveWorker->MaybeDispatchStateChangeEvent();
508
0
    }
509
0
510
0
    // We also check the "old" workers to see if they need a statechange
511
0
    // event as well.  Note, these may overlap with the known worker properties
512
0
    // above, but MaybeDispatchStateChangeEvent() will ignore duplicated calls.
513
0
    for (auto& oldWorker : oldWorkerList) {
514
0
      if (!oldWorker) {
515
0
        continue;
516
0
      }
517
0
518
0
      oldWorker->MaybeDispatchStateChangeEvent();
519
0
    }
520
0
  });
521
0
522
0
  // Clear all workers if the registration has been detached from the global.
523
0
  // Also, we cannot expose ServiceWorker objects on worker threads yet, so
524
0
  // do the same on when off-main-thread.  This main thread check should be
525
0
  // removed as part of bug 1113522.
526
0
  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
527
0
  if (!global || !NS_IsMainThread()) {
528
0
    return;
529
0
  }
530
0
531
0
  if (aActive.isSome()) {
532
0
    if ((mActiveWorker = global->GetOrCreateServiceWorker(aActive.ref()))) {
533
0
      mActiveWorker->SetState(aActive.ref().State());
534
0
    }
535
0
  } else {
536
0
    mActiveWorker = nullptr;
537
0
  }
538
0
539
0
  if (aWaiting.isSome()) {
540
0
    if ((mWaitingWorker = global->GetOrCreateServiceWorker(aWaiting.ref()))) {
541
0
      mWaitingWorker->SetState(aWaiting.ref().State());
542
0
    }
543
0
  } else {
544
0
    mWaitingWorker = nullptr;
545
0
  }
546
0
547
0
  if (aInstalling.isSome()) {
548
0
    if ((mInstallingWorker = global->GetOrCreateServiceWorker(aInstalling.ref()))) {
549
0
      mInstallingWorker->SetState(aInstalling.ref().State());
550
0
    }
551
0
  } else {
552
0
    mInstallingWorker = nullptr;
553
0
  }
554
0
}
555
556
} // dom namespace
557
} // mozilla namespace