Bug 1510809 - Fire updatefound correctly for SWC.register r=asuth
authorBlake Kaplan <mrbkap@gmail.com>
Thu, 29 Nov 2018 23:06:32 +0000
changeset 508090 6b447ab87004213505fbdcb0d2538a2634550737
parent 508089 ea4b9bbb0cf49ff47f18427132141e42f71982ee
child 508091 f9900ac6071c17ff7af3ccfaf2a8ce02ea66cffc
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1510809
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1510809 - Fire updatefound correctly for SWC.register r=asuth Currently, we are required to fire updatefound in three cases: * When a "soft" update finds an update. * When a call to ServiceWorkerRegistration.update finds an update. * When ServiceWorkerContainer.register registers a ServiceWorker. In the first case, there are no requirements on the timing of the event. For the second two cases, however, the promise returned by update or register needs to resolve before updatefound is dispatched. We hack around the first case by explicitly counting the calls to update and only dispatching updatefound when the final promise resolves. In the case of SWC.register, the ServiceWorkerRegistration object might not even exist when it notices that we need to fire updatefound, which suggests that we need some code to tell it when to fire the event; except that in the soft update case, there is no obvious place to do so. So, the easiest way to resolve this is to have the parent process tell the ServiceWorkerRegistration when to fire updatefound itself. This way, we don't rely on any tricky timing and everything is consistent with itself. Differential Revision: https://phabricator.services.mozilla.com/D13368
dom/serviceworkers/PServiceWorkerRegistration.ipdl
dom/serviceworkers/RemoteServiceWorkerRegistrationImpl.cpp
dom/serviceworkers/RemoteServiceWorkerRegistrationImpl.h
dom/serviceworkers/ServiceWorkerRegistration.cpp
dom/serviceworkers/ServiceWorkerRegistration.h
dom/serviceworkers/ServiceWorkerRegistrationChild.cpp
dom/serviceworkers/ServiceWorkerRegistrationChild.h
dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp
dom/serviceworkers/ServiceWorkerRegistrationImpl.h
dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp
dom/serviceworkers/ServiceWorkerRegistrationListener.h
dom/serviceworkers/ServiceWorkerRegistrationProxy.cpp
dom/serviceworkers/ServiceWorkerRegistrationProxy.h
dom/serviceworkers/ServiceWorkerUpdateJob.cpp
--- a/dom/serviceworkers/PServiceWorkerRegistration.ipdl
+++ b/dom/serviceworkers/PServiceWorkerRegistration.ipdl
@@ -18,12 +18,13 @@ parent:
 
   async Unregister() returns (bool aSuccess, CopyableErrorResult aRv);
   async Update() returns (IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult aResult);
 
 child:
   async __delete__();
 
   async UpdateState(IPCServiceWorkerRegistrationDescriptor aDescriptor);
+  async FireUpdateFound();
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/serviceworkers/RemoteServiceWorkerRegistrationImpl.cpp
+++ b/dom/serviceworkers/RemoteServiceWorkerRegistrationImpl.cpp
@@ -164,10 +164,18 @@ RemoteServiceWorkerRegistrationImpl::Rev
 void
 RemoteServiceWorkerRegistrationImpl::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor)
 {
   if (mOuter) {
     mOuter->UpdateState(aDescriptor);
   }
 }
 
+void
+RemoteServiceWorkerRegistrationImpl::FireUpdateFound()
+{
+  if (mOuter) {
+    mOuter->MaybeDispatchUpdateFoundRunnable();
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/serviceworkers/RemoteServiceWorkerRegistrationImpl.h
+++ b/dom/serviceworkers/RemoteServiceWorkerRegistrationImpl.h
@@ -44,15 +44,18 @@ public:
   explicit RemoteServiceWorkerRegistrationImpl(const ServiceWorkerRegistrationDescriptor& aDescriptor);
 
   void
   RevokeActor(ServiceWorkerRegistrationChild* aActor);
 
   void
   UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor);
 
+  void
+  FireUpdateFound();
+
   NS_INLINE_DECL_REFCOUNTING(RemoteServiceWorkerRegistrationImpl, override)
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_remoteserviceworkerregistrationimpl_h__
--- a/dom/serviceworkers/ServiceWorkerRegistration.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistration.cpp
@@ -43,17 +43,16 @@ const uint64_t kInvalidUpdateFoundId = 0
 ServiceWorkerRegistration::ServiceWorkerRegistration(nsIGlobalObject* aGlobal,
                                                      const ServiceWorkerRegistrationDescriptor& aDescriptor,
                                                      ServiceWorkerRegistration::Inner* aInner)
   : DOMEventTargetHelper(aGlobal)
   , mDescriptor(aDescriptor)
   , mInner(aInner)
   , mScheduledUpdateFoundId(kInvalidUpdateFoundId)
   , mDispatchedUpdateFoundId(kInvalidUpdateFoundId)
-  , mPendingUpdatePromises(0)
 {
   MOZ_DIAGNOSTIC_ASSERT(mInner);
 
   KeepAliveIfHasListenersFor(NS_LITERAL_STRING("updatefound"));
 
   UpdateState(mDescriptor);
   mInner->SetServiceWorkerRegistration(this);
 }
@@ -219,32 +218,28 @@ ServiceWorkerRegistration::Update(ErrorR
 
   RefPtr<Promise> outer = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   RefPtr<ServiceWorkerRegistration> self = this;
 
-  mPendingUpdatePromises += 1;
-
   mInner->Update(
     [outer, self](const ServiceWorkerRegistrationDescriptor& aDesc) {
-      auto scopeExit = MakeScopeExit([&] { self->UpdatePromiseSettled(); });
       nsIGlobalObject* global = self->GetParentObject();
       MOZ_DIAGNOSTIC_ASSERT(global);
       RefPtr<ServiceWorkerRegistration> ref =
         global->GetOrCreateServiceWorkerRegistration(aDesc);
       if (!ref) {
         outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
         return;
       }
       outer->MaybeResolve(ref);
     }, [outer, self] (ErrorResult& aRv) {
-      auto scopeExit = MakeScopeExit([&] { self->UpdatePromiseSettled(); });
       outer->MaybeReject(aRv);
     });
 
   return outer.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerRegistration::Unregister(ErrorResult& aRv)
@@ -377,16 +372,20 @@ ServiceWorkerRegistration::WhenVersionRe
 
   mVersionCallbackList.AppendElement(
     MakeUnique<VersionCallback>(aVersion, std::move(aCallback)));
 }
 
 void
 ServiceWorkerRegistration::MaybeScheduleUpdateFound(const Maybe<ServiceWorkerDescriptor>& aInstallingDescriptor)
 {
+  // This function sets mScheduledUpdateFoundId to note when we were told about
+  // a new installing worker. We rely on a call to
+  // MaybeDispatchUpdateFoundRunnable (called indirectly from UpdateJobCallback)
+  // to actually fire the event.
   uint64_t newId = aInstallingDescriptor.isSome()
                  ? aInstallingDescriptor.ref().Id()
                  : kInvalidUpdateFoundId;
 
   if (mScheduledUpdateFoundId != kInvalidUpdateFoundId) {
     if (mScheduledUpdateFoundId == newId) {
       return;
     }
@@ -397,18 +396,22 @@ ServiceWorkerRegistration::MaybeSchedule
   bool updateFound = newId != kInvalidUpdateFoundId &&
                      mDispatchedUpdateFoundId != newId;
 
   if (!updateFound) {
     return;
   }
 
   mScheduledUpdateFoundId = newId;
+}
 
-  if (mPendingUpdatePromises > 0) {
+void
+ServiceWorkerRegistration::MaybeDispatchUpdateFoundRunnable()
+{
+  if (mScheduledUpdateFoundId == kInvalidUpdateFoundId) {
     return;
   }
 
   nsIGlobalObject* global = GetParentObject();
   NS_ENSURE_TRUE_VOID(global);
 
   nsCOMPtr<nsIRunnable> r = NewCancelableRunnableMethod(
     "ServiceWorkerRegistration::MaybeDispatchUpdateFound",
@@ -430,38 +433,16 @@ ServiceWorkerRegistration::MaybeDispatch
     return;
   }
 
   mDispatchedUpdateFoundId = scheduledId;
   DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
 }
 
 void
-ServiceWorkerRegistration::UpdatePromiseSettled()
-{
-  MOZ_DIAGNOSTIC_ASSERT(mPendingUpdatePromises > 0);
-  mPendingUpdatePromises -= 1;
-  if (mPendingUpdatePromises > 0 ||
-      mScheduledUpdateFoundId == kInvalidUpdateFoundId) {
-    return;
-  }
-
-  nsIGlobalObject* global = GetParentObject();
-  NS_ENSURE_TRUE_VOID(global);
-
-  nsCOMPtr<nsIRunnable> r = NewCancelableRunnableMethod(
-    "ServiceWorkerRegistration::MaybeDispatchUpdateFound",
-    this,
-    &ServiceWorkerRegistration::MaybeDispatchUpdateFound);
-
-  Unused << global->EventTargetFor(TaskCategory::Other)->Dispatch(
-    r.forget(), NS_DISPATCH_NORMAL);
-}
-
-void
 ServiceWorkerRegistration::UpdateStateInternal(const Maybe<ServiceWorkerDescriptor>& aInstalling,
                                                const Maybe<ServiceWorkerDescriptor>& aWaiting,
                                                const Maybe<ServiceWorkerDescriptor>& aActive)
 {
   // Do this immediately as it may flush an already pending updatefound
   // event.  In that case we want to fire the pending event before
   // modifying any of the registration properties.
   MaybeScheduleUpdateFound(aInstalling);
--- a/dom/serviceworkers/ServiceWorkerRegistration.h
+++ b/dom/serviceworkers/ServiceWorkerRegistration.h
@@ -117,16 +117,19 @@ public:
                    ErrorResult& aRv);
 
   const ServiceWorkerRegistrationDescriptor&
   Descriptor() const;
 
   void
   WhenVersionReached(uint64_t aVersion, ServiceWorkerBoolCallback&& aCallback);
 
+  void
+  MaybeDispatchUpdateFoundRunnable();
+
 private:
   ServiceWorkerRegistration(nsIGlobalObject* aGlobal,
                             const ServiceWorkerRegistrationDescriptor& aDescriptor,
                             Inner* aInner);
 
   ~ServiceWorkerRegistration();
 
   void
@@ -135,19 +138,16 @@ private:
                       const Maybe<ServiceWorkerDescriptor>& aActive);
 
   void
   MaybeScheduleUpdateFound(const Maybe<ServiceWorkerDescriptor>& aInstallingDescriptor);
 
   void
   MaybeDispatchUpdateFound();
 
-  void
-  UpdatePromiseSettled();
-
   ServiceWorkerRegistrationDescriptor mDescriptor;
   RefPtr<Inner> mInner;
 
   RefPtr<ServiceWorker> mInstallingWorker;
   RefPtr<ServiceWorker> mWaitingWorker;
   RefPtr<ServiceWorker> mActiveWorker;
   RefPtr<PushManager> mPushManager;
 
@@ -162,17 +162,16 @@ private:
     {
       MOZ_DIAGNOSTIC_ASSERT(mFunc);
     }
   };
   nsTArray<UniquePtr<VersionCallback>> mVersionCallbackList;
 
   uint64_t mScheduledUpdateFoundId;
   uint64_t mDispatchedUpdateFoundId;
-  uint32_t mPendingUpdatePromises;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerRegistration, NS_DOM_SERVICEWORKERREGISTRATION_IID)
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_ServiceWorkerRegistration_h */
--- a/dom/serviceworkers/ServiceWorkerRegistrationChild.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationChild.cpp
@@ -31,16 +31,25 @@ IPCResult
 ServiceWorkerRegistrationChild::RecvUpdateState(const IPCServiceWorkerRegistrationDescriptor& aDescriptor)
 {
   if (mOwner) {
     mOwner->UpdateState(ServiceWorkerRegistrationDescriptor(aDescriptor));
   }
   return IPC_OK();
 }
 
+IPCResult
+ServiceWorkerRegistrationChild::RecvFireUpdateFound()
+{
+  if (mOwner) {
+    mOwner->FireUpdateFound();
+  }
+  return IPC_OK();
+}
+
 void
 ServiceWorkerRegistrationChild::WorkerShuttingDown()
 {
   MaybeStartTeardown();
 }
 
 ServiceWorkerRegistrationChild::ServiceWorkerRegistrationChild(WorkerHolderToken* aWorkerHolderToken)
   : mWorkerHolderToken(aWorkerHolderToken)
--- a/dom/serviceworkers/ServiceWorkerRegistrationChild.h
+++ b/dom/serviceworkers/ServiceWorkerRegistrationChild.h
@@ -24,16 +24,19 @@ class ServiceWorkerRegistrationChild fin
 
   // PServiceWorkerRegistrationChild
   void
   ActorDestroy(ActorDestroyReason aReason) override;
 
   mozilla::ipc::IPCResult
   RecvUpdateState(const IPCServiceWorkerRegistrationDescriptor& aDescriptor) override;
 
+  mozilla::ipc::IPCResult
+  RecvFireUpdateFound() override;
+
   // WorkerHolderToken::Listener
   void
   WorkerShuttingDown() override;
 
 public:
   explicit ServiceWorkerRegistrationChild(WorkerHolderToken* aWorkerHolderToken);
   ~ServiceWorkerRegistrationChild() = default;
 
--- a/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp
@@ -98,16 +98,18 @@ ServiceWorkerRegistrationMainThread::Reg
   // runnable to call this method is in the event queue.  Double check
   // whether there is still anything to do here.
   if (mOuter) {
     mOuter->RegistrationRemoved();
   }
   StopListeningForEvents();
 }
 
+// NB: These functions use NS_ENSURE_TRUE_VOID to be noisy about preconditions
+// that would otherwise cause things to silently not happen if they were false.
 void
 ServiceWorkerRegistrationMainThread::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor)
 {
   NS_ENSURE_TRUE_VOID(mOuter);
 
   nsIGlobalObject* global = mOuter->GetParentObject();
   NS_ENSURE_TRUE_VOID(global);
 
@@ -121,16 +123,38 @@ ServiceWorkerRegistrationMainThread::Upd
     });
 
   Unused <<
     global->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget(),
                                                           NS_DISPATCH_NORMAL);
 }
 
 void
+ServiceWorkerRegistrationMainThread::FireUpdateFound()
+{
+  NS_ENSURE_TRUE_VOID(mOuter);
+
+  nsIGlobalObject* global = mOuter->GetParentObject();
+  NS_ENSURE_TRUE_VOID(global);
+
+  RefPtr<ServiceWorkerRegistrationMainThread> self = this;
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+    "ServiceWorkerRegistrationMainThread::FireUpdateFound",
+    [self] () mutable {
+      NS_ENSURE_TRUE_VOID(self->mOuter);
+      self->mOuter->MaybeDispatchUpdateFoundRunnable();
+    });
+
+  Unused <<
+    global->EventTargetFor(TaskCategory::Other)->Dispatch(r.forget(),
+                                                          NS_DISPATCH_NORMAL);
+}
+
+
+void
 ServiceWorkerRegistrationMainThread::RegistrationRemoved()
 {
   NS_ENSURE_TRUE_VOID(mOuter);
 
   nsIGlobalObject* global = mOuter->GetParentObject();
   NS_ENSURE_TRUE_VOID(global);
 
   // Queue a runnable to clean up the registration.  This is necessary
@@ -674,25 +698,48 @@ public:
         this,
         &WorkerListener::UpdateStateOnWorkerThread,
         aDescriptor);
 
     Unused << mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
   }
 
   void
+  FireUpdateFound() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsCOMPtr<nsIRunnable> r =
+      NewCancelableRunnableMethod(
+        "WorkerListener::FireUpdateFound",
+        this,
+        &WorkerListener::FireUpdateFoundOnWorkerThread);
+
+    Unused << mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+  }
+
+  void
   UpdateStateOnWorkerThread(const ServiceWorkerRegistrationDescriptor& aDescriptor)
   {
     MOZ_ASSERT(IsCurrentThreadRunningWorker());
     if (mRegistration) {
       mRegistration->UpdateState(aDescriptor);
     }
   }
 
   void
+  FireUpdateFoundOnWorkerThread()
+  {
+    MOZ_ASSERT(IsCurrentThreadRunningWorker());
+    if (mRegistration) {
+      mRegistration->FireUpdateFound();
+    }
+  }
+
+  void
   RegistrationRemoved() override;
 
   void
   GetScope(nsAString& aScope) const override
   {
     CopyUTF8toUTF16(mDescriptor.Scope(), aScope);
   }
 
@@ -934,16 +981,24 @@ ServiceWorkerRegistrationWorkerThread::R
 void
 ServiceWorkerRegistrationWorkerThread::UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor)
 {
   if (mOuter) {
     mOuter->UpdateState(aDescriptor);
   }
 }
 
+void
+ServiceWorkerRegistrationWorkerThread::FireUpdateFound()
+{
+  if (mOuter) {
+    mOuter->MaybeDispatchUpdateFoundRunnable();
+  }
+}
+
 class RegistrationRemovedWorkerRunnable final : public WorkerRunnable
 {
   RefPtr<WorkerListener> mListener;
 public:
   RegistrationRemovedWorkerRunnable(WorkerPrivate* aWorkerPrivate,
                                     WorkerListener* aListener)
     : WorkerRunnable(aWorkerPrivate)
     , mListener(aListener)
--- a/dom/serviceworkers/ServiceWorkerRegistrationImpl.h
+++ b/dom/serviceworkers/ServiceWorkerRegistrationImpl.h
@@ -50,16 +50,19 @@ public:
   Unregister(ServiceWorkerBoolCallback&& aSuccessCB,
              ServiceWorkerFailureCallback&& aFailureCB) override;
 
   // ServiceWorkerRegistrationListener
   void
   UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) override;
 
   void
+  FireUpdateFound() override;
+
+  void
   RegistrationRemoved() override;
 
   void
   GetScope(nsAString& aScope) const override
   {
     aScope = mScope;
   }
 
@@ -124,16 +127,19 @@ private:
   InitListener();
 
   void
   ReleaseListener();
 
   void
   UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor);
 
+  void
+  FireUpdateFound();
+
   // This can be called only by WorkerListener.
   WorkerPrivate*
   GetWorkerPrivate(const MutexAutoLock& aProofOfLock);
 
   ServiceWorkerRegistration* mOuter;
   const ServiceWorkerRegistrationDescriptor mDescriptor;
   const nsString mScope;
   RefPtr<WorkerListener> mListener;
--- a/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp
@@ -805,16 +805,26 @@ ServiceWorkerRegistrationInfo::GetUpdate
   if (!mControlledClientsCounter && mDelayMultiplier < (INT_MAX / 30)) {
     mDelayMultiplier = (mDelayMultiplier ? mDelayMultiplier : 1) * 30;
   }
 
   return delay;
 }
 
 void
+ServiceWorkerRegistrationInfo::FireUpdateFound()
+{
+  nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mInstanceList);
+  while (it.HasMore()) {
+    RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
+    target->FireUpdateFound();
+  }
+}
+
+void
 ServiceWorkerRegistrationInfo::NotifyRemoved()
 {
   nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mInstanceList);
   while (it.HasMore()) {
     RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
     target->RegistrationRemoved();
   }
 }
--- a/dom/serviceworkers/ServiceWorkerRegistrationListener.h
+++ b/dom/serviceworkers/ServiceWorkerRegistrationListener.h
@@ -18,16 +18,19 @@ class ServiceWorkerRegistrationListener
 {
 public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
   virtual void
   UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) = 0;
 
   virtual void
+  FireUpdateFound() = 0;
+
+  virtual void
   RegistrationRemoved() = 0;
 
   virtual void
   GetScope(nsAString& aScope) const = 0;
 
   virtual bool
   MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) = 0;
 };
--- a/dom/serviceworkers/ServiceWorkerRegistrationProxy.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationProxy.cpp
@@ -39,16 +39,26 @@ ServiceWorkerRegistrationProxy::UpdateSt
   AssertIsOnBackgroundThread();
   if (!mActor) {
     return;
   }
   Unused << mActor->SendUpdateState(aDescriptor.ToIPC());
 }
 
 void
+ServiceWorkerRegistrationProxy::FireUpdateFoundOnBGThread()
+{
+  AssertIsOnBackgroundThread();
+  if (!mActor) {
+    return;
+  }
+  Unused << mActor->SendFireUpdateFound();
+}
+
+void
 ServiceWorkerRegistrationProxy::InitOnMainThread()
 {
   AssertIsOnMainThread();
 
   auto scopeExit = MakeScopeExit([&] {
     MaybeShutdownOnMainThread();
   });
 
@@ -105,16 +115,28 @@ ServiceWorkerRegistrationProxy::UpdateSt
   nsCOMPtr<nsIRunnable> r = NewRunnableMethod<ServiceWorkerRegistrationDescriptor>(
     __func__, this, &ServiceWorkerRegistrationProxy::UpdateStateOnBGThread,
     aDescriptor);
 
   MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
 }
 
 void
+ServiceWorkerRegistrationProxy::FireUpdateFound()
+{
+  AssertIsOnMainThread();
+
+  nsCOMPtr<nsIRunnable> r =
+    NewRunnableMethod(__func__, this,
+                      &ServiceWorkerRegistrationProxy::FireUpdateFoundOnBGThread);
+
+  MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
+}
+
+void
 ServiceWorkerRegistrationProxy::RegistrationRemoved()
 {
   MaybeShutdownOnMainThread();
 }
 
 void
 ServiceWorkerRegistrationProxy::GetScope(nsAString& aScope) const
 {
--- a/dom/serviceworkers/ServiceWorkerRegistrationProxy.h
+++ b/dom/serviceworkers/ServiceWorkerRegistrationProxy.h
@@ -35,31 +35,37 @@ class ServiceWorkerRegistrationProxy fin
 
   // Background thread methods
   void
   MaybeShutdownOnBGThread();
 
   void
   UpdateStateOnBGThread(const ServiceWorkerRegistrationDescriptor& aDescriptor);
 
+  void
+  FireUpdateFoundOnBGThread();
+
   // Main thread methods
   void
   InitOnMainThread();
 
   void
   MaybeShutdownOnMainThread();
 
   void
   StopListeningOnMainThread();
 
   // ServiceWorkerRegistrationListener interface
   void
   UpdateState(const ServiceWorkerRegistrationDescriptor& aDescriptor) override;
 
   void
+  FireUpdateFound() override;
+
+  void
   RegistrationRemoved() override;
 
   void
   GetScope(nsAString& aScope) const override;
 
   bool
   MatchesDescriptor(const ServiceWorkerRegistrationDescriptor& aDescriptor) override;
 
--- a/dom/serviceworkers/ServiceWorkerUpdateJob.cpp
+++ b/dom/serviceworkers/ServiceWorkerUpdateJob.cpp
@@ -537,22 +537,18 @@ ServiceWorkerUpdateJob::Install()
   //
   //  https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#installation-algorithm
 
   mRegistration->TransitionEvaluatingToInstalling();
 
   // Step 6 of the Install algorithm resolving the job promise.
   InvokeResultCallbacks(NS_OK);
 
-  // The job promise cannot be rejected after this point, but the job can
-  // still fail; e.g. if the install event handler throws, etc.
-
-  // Note, the updatefound event is fired automatically when the installing
-  // property is set on the ServiceWorkerRegistration binding object.  This
-  // happens via the TransitionEvaluatingToInstalling() call above.
+  // Queue a task to fire an event named updatefound at all the ServiceWorkerRegistration.
+  mRegistration->FireUpdateFound();
 
   nsMainThreadPtrHandle<ServiceWorkerUpdateJob> handle(
     new nsMainThreadPtrHolder<ServiceWorkerUpdateJob>(
       "ServiceWorkerUpdateJob", this));
   RefPtr<LifeCycleEventCallback> callback = new ContinueInstallRunnable(handle);
 
   // Send the install event to the worker thread
   ServiceWorkerPrivate* workerPrivate =