Bug 1317266 - Transition ServiceWorker instances when the SW changes states instead of just invalidating them. r=bkelly
authorCatalin Badea <catalin.badea392@gmail.com>
Mon, 21 Nov 2016 02:14:54 +0000
changeset 323584 ac6b94962bf249fb5178cf83524f2944566ed96e
parent 323583 3c1343636012438a1b8671fcfd2b2a3b8275774d
child 323585 e5164f497e4270c3527188e83fd332367dff1f8e
push id21
push usermaklebus@msu.edu
push dateThu, 01 Dec 2016 06:22:08 +0000
reviewersbkelly
bugs1317266
milestone53.0a1
Bug 1317266 - Transition ServiceWorker instances when the SW changes states instead of just invalidating them. r=bkelly
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
dom/workers/ServiceWorkerRegistration.cpp
dom/workers/ServiceWorkerRegistration.h
dom/workers/ServiceWorkerRegistrationInfo.cpp
dom/workers/ServiceWorkerRegistrationInfo.h
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -2522,16 +2522,36 @@ ServiceWorkerManager::GetActive(nsPIDOMW
                                 nsISupports** aServiceWorker)
 {
   return GetServiceWorkerForScope(aWindow, aScope,
                                   WhichServiceWorker::ACTIVE_WORKER,
                                   aServiceWorker);
 }
 
 void
+ServiceWorkerManager::TransitionServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
+                                                                WhichServiceWorker aWhichOne)
+{
+  AssertIsOnMainThread();
+  nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
+  while (it.HasMore()) {
+    RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
+    nsAutoString regScope;
+    target->GetScope(regScope);
+    MOZ_ASSERT(!regScope.IsEmpty());
+
+    NS_ConvertUTF16toUTF8 utf8Scope(regScope);
+
+    if (utf8Scope.Equals(aRegistration->mScope)) {
+      target->TransitionWorker(aWhichOne);
+    }
+  }
+}
+
+void
 ServiceWorkerManager::InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                                                 WhichServiceWorker aWhichOnes)
 {
   AssertIsOnMainThread();
   nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
   while (it.HasMore()) {
     RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
     nsAutoString regScope;
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -358,16 +358,19 @@ private:
   ServiceWorkerInfo*
   GetActiveWorkerInfoForScope(const PrincipalOriginAttributes& aOriginAttributes,
                               const nsACString& aScope);
 
   ServiceWorkerInfo*
   GetActiveWorkerInfoForDocument(nsIDocument* aDocument);
 
   void
+  TransitionServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
+                                            WhichServiceWorker aWhichOne);
+  void
   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                             WhichServiceWorker aWhichOnes);
 
   void
   NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
 
   void
   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -123,16 +123,19 @@ public:
   // ServiceWorkerRegistrationListener
   void
   UpdateFound() override;
 
   void
   InvalidateWorkers(WhichServiceWorker aWhichOnes) override;
 
   void
+  TransitionWorker(WhichServiceWorker aWhichOne) override;
+
+  void
   RegistrationRemoved() override;
 
   void
   GetScope(nsAString& aScope) const override
   {
     aScope = mScope;
   }
 
@@ -300,16 +303,34 @@ ServiceWorkerRegistrationMainThread::Get
 
 void
 ServiceWorkerRegistrationMainThread::UpdateFound()
 {
   DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
 }
 
 void
+ServiceWorkerRegistrationMainThread::TransitionWorker(WhichServiceWorker aWhichOne)
+{
+  AssertIsOnMainThread();
+
+  // We assert the worker's previous state because the 'statechange'
+  // event is dispatched in a queued runnable.
+  if (aWhichOne == WhichServiceWorker::INSTALLING_WORKER) {
+    MOZ_ASSERT_IF(mInstallingWorker, mInstallingWorker->State() == ServiceWorkerState::Installing);
+    mWaitingWorker = mInstallingWorker.forget();
+  } else if (aWhichOne == WhichServiceWorker::WAITING_WORKER) {
+    MOZ_ASSERT_IF(mWaitingWorker, mWaitingWorker->State() == ServiceWorkerState::Installed);
+    mActiveWorker = mWaitingWorker.forget();
+  } else {
+    MOZ_ASSERT_UNREACHABLE("Invalid transition!");
+  }
+}
+
+void
 ServiceWorkerRegistrationMainThread::InvalidateWorkers(WhichServiceWorker aWhichOnes)
 {
   AssertIsOnMainThread();
   if (aWhichOnes & WhichServiceWorker::INSTALLING_WORKER) {
     mInstallingWorker = nullptr;
   }
 
   if (aWhichOnes & WhichServiceWorker::WAITING_WORKER) {
@@ -979,16 +1000,23 @@ public:
     }
   }
 
   // ServiceWorkerRegistrationListener
   void
   UpdateFound() override;
 
   void
+  TransitionWorker(WhichServiceWorker aWhichOne) override
+  {
+    AssertIsOnMainThread();
+    NS_WARNING("FIXME: Not implemented!");
+  }
+
+  void
   InvalidateWorkers(WhichServiceWorker aWhichOnes) override
   {
     AssertIsOnMainThread();
     // FIXME(nsm);
   }
 
   void
   RegistrationRemoved() override
--- a/dom/workers/ServiceWorkerRegistration.h
+++ b/dom/workers/ServiceWorkerRegistration.h
@@ -40,16 +40,19 @@ public:
 
   virtual void
   UpdateFound() = 0;
 
   virtual void
   InvalidateWorkers(WhichServiceWorker aWhichOnes) = 0;
 
   virtual void
+  TransitionWorker(WhichServiceWorker aWhichOne) = 0;
+
+  virtual void
   RegistrationRemoved() = 0;
 
   virtual void
   GetScope(nsAString& aScope) const = 0;
 };
 
 class ServiceWorkerRegistration : public DOMEventTargetHelper
 {
--- a/dom/workers/ServiceWorkerRegistrationInfo.cpp
+++ b/dom/workers/ServiceWorkerRegistrationInfo.cpp
@@ -43,16 +43,20 @@ public:
 
 void
 ServiceWorkerRegistrationInfo::Clear()
 {
   if (mEvaluatingWorker) {
     mEvaluatingWorker = nullptr;
   }
 
+  UpdateRegistrationStateProperties(WhichServiceWorker::INSTALLING_WORKER |
+                                    WhichServiceWorker::WAITING_WORKER |
+                                    WhichServiceWorker::ACTIVE_WORKER, Invalidate);
+
   if (mInstallingWorker) {
     mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
     mInstallingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
     mInstallingWorker = nullptr;
     // FIXME(nsm): Abort any inflight requests from installing worker.
   }
 
   if (mWaitingWorker) {
@@ -61,20 +65,16 @@ ServiceWorkerRegistrationInfo::Clear()
     mWaitingWorker = nullptr;
   }
 
   if (mActiveWorker) {
     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
     mActiveWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
     mActiveWorker = nullptr;
   }
-
-  NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER |
-                          WhichServiceWorker::WAITING_WORKER |
-                          WhichServiceWorker::ACTIVE_WORKER);
 }
 
 ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope,
                                                              nsIPrincipal* aPrincipal)
   : mControlledDocumentsCounter(0)
   , mUpdateState(NoUpdate)
   , mLastUpdateCheckTime(0)
   , mScope(aScope)
@@ -304,26 +304,46 @@ ServiceWorkerRegistrationInfo::IsLastUpd
 
   if ((now - mLastUpdateCheckTime) > kSecondsPerDay) {
     return true;
   }
   return false;
 }
 
 void
-ServiceWorkerRegistrationInfo::NotifyListenersOnChange(WhichServiceWorker aChangedWorkers)
+ServiceWorkerRegistrationInfo::AsyncUpdateRegistrationStateProperties(WhichServiceWorker aWorker,
+                                                                      TransitionType aTransition)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(aChangedWorkers & (WhichServiceWorker::INSTALLING_WORKER |
-                                WhichServiceWorker::WAITING_WORKER |
-                                WhichServiceWorker::ACTIVE_WORKER));
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (aTransition == Invalidate) {
+    swm->InvalidateServiceWorkerRegistrationWorker(this, aWorker);
+  } else {
+    MOZ_ASSERT(aTransition == TransitionToNextState);
+    swm->TransitionServiceWorkerRegistrationWorker(this, aWorker);
+  }
+}
 
-  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-  swm->InvalidateServiceWorkerRegistrationWorker(this, aChangedWorkers);
+void
+ServiceWorkerRegistrationInfo::UpdateRegistrationStateProperties(WhichServiceWorker aWorker,
+                                                                 TransitionType aTransition)
+{
+  AssertIsOnMainThread();
 
+  nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod<WhichServiceWorker, TransitionType>(
+         this,
+         &ServiceWorkerRegistrationInfo::AsyncUpdateRegistrationStateProperties, aWorker, aTransition);
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
+
+  NotifyChromeRegistrationListeners();
+}
+
+void
+ServiceWorkerRegistrationInfo::NotifyChromeRegistrationListeners()
+{
   nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> listeners(mListeners);
   for (size_t index = 0; index < listeners.Length(); ++index) {
     listeners[index]->OnChange();
   }
 }
 
 void
 ServiceWorkerRegistrationInfo::MaybeScheduleTimeCheckAndUpdate()
@@ -431,48 +451,49 @@ void
 ServiceWorkerRegistrationInfo::ClearInstalling()
 {
   AssertIsOnMainThread();
 
   if (!mInstallingWorker) {
     return;
   }
 
+  UpdateRegistrationStateProperties(WhichServiceWorker::INSTALLING_WORKER,
+                                    Invalidate);
   mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
   mInstallingWorker = nullptr;
-  NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER);
 }
 
 void
 ServiceWorkerRegistrationInfo::TransitionEvaluatingToInstalling()
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(mEvaluatingWorker);
   MOZ_ASSERT(!mInstallingWorker);
 
   mInstallingWorker = mEvaluatingWorker.forget();
   mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
-  NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER);
+  NotifyChromeRegistrationListeners();
 }
 
 void
 ServiceWorkerRegistrationInfo::TransitionInstallingToWaiting()
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(mInstallingWorker);
 
   if (mWaitingWorker) {
     MOZ_ASSERT(mInstallingWorker->CacheName() != mWaitingWorker->CacheName());
     mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
   }
 
   mWaitingWorker = mInstallingWorker.forget();
+  UpdateRegistrationStateProperties(WhichServiceWorker::INSTALLING_WORKER,
+                                    TransitionToNextState);
   mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
-  NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER |
-                          WhichServiceWorker::WAITING_WORKER);
 
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   swm->StoreRegistration(mPrincipal, this);
 }
 
 void
 ServiceWorkerRegistrationInfo::SetActive(ServiceWorkerInfo* aServiceWorker)
 {
@@ -492,36 +513,36 @@ ServiceWorkerRegistrationInfo::SetActive
     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
   }
 
   // The active worker is being overriden due to initial load or
   // another process activating a worker.  Move straight to the
   // Activated state.
   mActiveWorker = aServiceWorker;
   mActiveWorker->SetActivateStateUncheckedWithoutEvent(ServiceWorkerState::Activated);
-  NotifyListenersOnChange(WhichServiceWorker::ACTIVE_WORKER);
+  UpdateRegistrationStateProperties(WhichServiceWorker::ACTIVE_WORKER, Invalidate);
 }
 
 void
 ServiceWorkerRegistrationInfo::TransitionWaitingToActive()
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(mWaitingWorker);
 
   if (mActiveWorker) {
     MOZ_ASSERT(mWaitingWorker->CacheName() != mActiveWorker->CacheName());
     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
   }
 
   // We are transitioning from waiting to active normally, so go to
   // the activating state.
   mActiveWorker = mWaitingWorker.forget();
+  UpdateRegistrationStateProperties(WhichServiceWorker::WAITING_WORKER,
+                                    TransitionToNextState);
   mActiveWorker->UpdateState(ServiceWorkerState::Activating);
-  NotifyListenersOnChange(WhichServiceWorker::WAITING_WORKER |
-                          WhichServiceWorker::ACTIVE_WORKER);
 }
 
 bool
 ServiceWorkerRegistrationInfo::IsIdle() const
 {
   return !mActiveWorker || mActiveWorker->WorkerPrivate()->IsIdle();
 }
 
--- a/dom/workers/ServiceWorkerRegistrationInfo.h
+++ b/dom/workers/ServiceWorkerRegistrationInfo.h
@@ -106,19 +106,16 @@ public:
 
   void
   RefreshLastUpdateCheckTime();
 
   bool
   IsLastUpdateCheckTimeOverOneDay() const;
 
   void
-  NotifyListenersOnChange(WhichServiceWorker aChangedWorkers);
-
-  void
   MaybeScheduleTimeCheckAndUpdate();
 
   void
   MaybeScheduleUpdate();
 
   bool
   CheckAndClearIfUpdateNeeded();
 
@@ -170,15 +167,38 @@ public:
   // Transition the current waiting worker to be the new active worker.  The
   // worker is updated to the Activating state.
   void
   TransitionWaitingToActive();
 
   // Determine if the registration is actively performing work.
   bool
   IsIdle() const;
+
+private:
+  enum TransitionType {
+    TransitionToNextState = 0,
+    Invalidate
+  };
+
+  // Queued as a runnable from UpdateRegistrationStateProperties.
+  void
+  AsyncUpdateRegistrationStateProperties(WhichServiceWorker aWorker, TransitionType aType);
+
+  // Roughly equivalent to [[Update Registration State algorithm]]. Make sure
+  // this is called *before* updating SW instances' state, otherwise they
+  // may get CC-ed.
+  void
+  UpdateRegistrationStateProperties(WhichServiceWorker aWorker, TransitionType aType);
+
+  // Used by devtools to track changes to the properties of *nsIServiceWorkerRegistrationInfo*.
+  // Note, this doesn't necessarily need to be in sync with the DOM registration objects, but
+  // it does need to be called in the same task that changed |mInstallingWorker|,
+  // |mWaitingWorker| or |mActiveWorker|.
+  void
+  NotifyChromeRegistrationListeners();
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_workers_serviceworkerregistrationinfo_h