Backed out changeset 4d8423d5a83b (bug 1130065)
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 17 Feb 2015 09:35:27 +0100
changeset 229444 daeecdd85accc5ee37c09bff0004e2df097afd1a
parent 229443 fc8879aec5fb30d7405f6b37000d056fcb9906f7
child 229445 491e81cd9cf8f4cf4aebf80a2c3c9370b03fe59f
push id11352
push userryanvm@gmail.com
push dateTue, 17 Feb 2015 19:29:37 +0000
treeherderfx-team@b6c56fab513d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1130065
milestone38.0a1
backs out4d8423d5a83b243edbf019b1f93c68654d6e6ffa
Backed out changeset 4d8423d5a83b (bug 1130065)
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
dom/workers/test/serviceworkers/install_event_error_worker.js
dom/workers/test/serviceworkers/mochitest.ini
dom/workers/test/serviceworkers/simpleregister/ready.html
dom/workers/test/serviceworkers/test_install_event.html
dom/workers/test/serviceworkers/test_installation_simple.html
dom/workers/test/serviceworkers/test_scopes.html
dom/workers/test/serviceworkers/unregister/index.html
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -125,16 +125,17 @@ ServiceWorkerRegistrationInfo::Clear()
     mInstallingWorker = nullptr;
     // FIXME(nsm): Abort any inflight requests from installing worker.
   }
 
   if (mWaitingWorker) {
     mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
     // Fire statechange.
     mWaitingWorker = nullptr;
+    mWaitingToActivate = false;
   }
 
   if (mActiveWorker) {
     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
     mActiveWorker = nullptr;
   }
 
   nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
@@ -193,33 +194,33 @@ ServiceWorkerManager::ServiceWorkerManag
 }
 
 ServiceWorkerManager::~ServiceWorkerManager()
 {
   // The map will assert if it is not empty when destroyed.
   mServiceWorkerRegistrationInfos.Clear();
 }
 
+class ServiceWorkerRegisterJob;
+
 class ContinueLifecycleTask : public nsISupports
 {
   NS_DECL_ISUPPORTS
 
 protected:
   virtual ~ContinueLifecycleTask()
   { }
 
 public:
   virtual void ContinueAfterWorkerEvent(bool aSuccess,
                                         bool aActivateImmediately) = 0;
 };
 
 NS_IMPL_ISUPPORTS0(ContinueLifecycleTask);
 
-class ServiceWorkerRegisterJob;
-
 class ContinueInstallTask MOZ_FINAL : public ContinueLifecycleTask
 {
   nsRefPtr<ServiceWorkerRegisterJob> mJob;
 
 public:
   explicit ContinueInstallTask(ServiceWorkerRegisterJob* aJob)
     : mJob(aJob)
   { }
@@ -232,17 +233,20 @@ class ContinueActivateTask MOZ_FINAL : p
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
 
 public:
   explicit ContinueActivateTask(ServiceWorkerRegistrationInfo* aReg)
     : mRegistration(aReg)
   { }
 
   void
-  ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */) MOZ_OVERRIDE;
+  ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */) MOZ_OVERRIDE
+  {
+    mRegistration->FinishActivate(aSuccess);
+  }
 };
 
 class ContinueLifecycleRunnable MOZ_FINAL : public nsRunnable
 {
   nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
   bool mSuccess;
   bool mActivateImmediately;
 
@@ -598,61 +602,53 @@ public:
 
   // Public so our error handling code can continue with a successful worker.
   void
   ContinueInstall()
   {
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     MOZ_ASSERT(swm->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
     swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
-    // This is effectively the end of Step 4.3 of the [[Update]] algorithm.
-    // The invocation of [[Install]] is not part of the atomic block.
-
-    // Begin [[Install]] atomic step 4.
     if (mRegistration->mInstallingWorker) {
       // FIXME(nsm): Terminate and stuff
       mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
     }
 
     swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                    WhichServiceWorker::INSTALLING_WORKER);
     mRegistration->mInstallingWorker = new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec);
     mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
 
     Succeed();
 
-    // Step 4.6 "Queue a task..." for updatefound.
     nsCOMPtr<nsIRunnable> upr =
       NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm,
                                                                   &ServiceWorkerManager::FireUpdateFound,
                                                                   mRegistration);
     NS_DispatchToMainThread(upr);
 
+    nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
+        new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
+
     nsRefPtr<ServiceWorker> serviceWorker;
     nsresult rv =
       swm->CreateServiceWorker(mRegistration->mInstallingWorker->ScriptSpec(),
                                mRegistration->mScope,
                                getter_AddRefs(serviceWorker));
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      ContinueAfterInstallEvent(false /* aSuccess */, false /* aActivateImmediately */);
+      ContinueAfterInstallEvent(false /* success */, false /* activate immediately */);
       return;
     }
 
-    nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
-        new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
-
     nsRefPtr<LifecycleEventWorkerRunnable> r =
       new LifecycleEventWorkerRunnable(serviceWorker->GetWorkerPrivate(), NS_LITERAL_STRING("install"), handle);
 
     AutoJSAPI jsapi;
     jsapi.Init();
-
-    // This triggers Step 4.7 "Queue a task to run the following substeps..."
-    // which sends the install event to the worker.
     r->Dispatch(jsapi.cx());
   }
 
 private:
   void
   Update()
   {
     MOZ_ASSERT(mRegistration);
@@ -724,18 +720,17 @@ private:
     mCallback->UpdateSucceeded(mRegistration);
     mCallback = nullptr;
   }
 
   void
   FailCommon(nsresult aRv)
   {
     mCallback = nullptr;
-    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    swm->MaybeRemoveRegistration(mRegistration);
+    MaybeRemoveRegistration();
     // Ensures that the job can't do anything useful from this point on.
     mRegistration = nullptr;
     Done(aRv);
   }
 
   // This MUST only be called when the job is still performing actions related
   // to registration or update. After the spec resolves the update promise, use
   // Done() with the failure code instead.
@@ -743,56 +738,71 @@ private:
   Fail(nsresult aRv)
   {
     MOZ_ASSERT(mCallback);
     mCallback->UpdateFailed(aRv);
     FailCommon(aRv);
   }
 
   void
-  ContinueAfterInstallEvent(bool aInstallEventSuccess, bool aActivateImmediately)
+  MaybeRemoveRegistration()
   {
+    MOZ_ASSERT(mRegistration);
+    nsRefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
+    if (!newest) {
+      nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+      swm->RemoveRegistration(mRegistration);
+    }
+  }
+
+  void
+  ContinueAfterInstallEvent(bool aSuccess, bool aActivateImmediately)
+  {
+    // By this point the callback should've been notified about success or fail
+    // and nulled.
+    MOZ_ASSERT(!mCallback);
+
     if (!mRegistration->mInstallingWorker) {
       NS_WARNING("mInstallingWorker was null.");
       return Done(NS_ERROR_DOM_ABORT_ERR);
     }
 
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
 
     // "If installFailed is true"
-    if (!aInstallEventSuccess) {
+    if (!aSuccess) {
       mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
       mRegistration->mInstallingWorker = nullptr;
       swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                      WhichServiceWorker::INSTALLING_WORKER);
-      swm->MaybeRemoveRegistration(mRegistration);
+      MaybeRemoveRegistration();
       return Done(NS_ERROR_DOM_ABORT_ERR);
     }
 
     // "If registration's waiting worker is not null"
     if (mRegistration->mWaitingWorker) {
       // FIXME(nsm): Terminate
       mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
     }
 
     // Although the spec first sets waiting worker and then updates its state,
     // our ServiceWorkerInfo does not hold a list of associated ServiceWorker
     // objects in content JS. This means if we want to fire an event on
     // ServiceWorkerRegistration.installing, we need to do it first, before
     // swapping it with waiting worker.
     mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installed);
     mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget();
+    mRegistration->mWaitingToActivate = false;
     swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                    WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
 
     // FIXME(nsm): Bug 982711 Deal with activateImmediately.
     NS_WARN_IF_FALSE(!aActivateImmediately, "Immediate activation using replace() is not supported yet");
+    mRegistration->TryToActivate();
     Done(NS_OK);
-    // Activate() is invoked out of band of atomic.
-    mRegistration->TryToActivate();
   }
 };
 
 NS_IMPL_ISUPPORTS_INHERITED(ServiceWorkerRegisterJob, ServiceWorkerJob, nsIStreamLoaderObserver);
 
 NS_IMETHODIMP
 ContinueUpdateRunnable::Run()
 {
@@ -801,18 +811,16 @@ ContinueUpdateRunnable::Run()
   nsRefPtr<ServiceWorkerRegisterJob> upjob = static_cast<ServiceWorkerRegisterJob*>(job.get());
   upjob->ContinueInstall();
   return NS_OK;
 }
 
 void
 ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately)
 {
-  // This does not start the job immediately if there are other jobs in the
-  // queue, which captures the "atomic" behaviour we want.
   mJob->ContinueAfterInstallEvent(aSuccess, aActivateImmediately);
 }
 
 // If we return an error code here, the ServiceWorkerContainer will
 // automatically reject the Promise.
 NS_IMETHODIMP
 ServiceWorkerManager::Register(nsIDOMWindow* aWindow,
                                const nsAString& aScope,
@@ -1085,30 +1093,28 @@ LifecycleEventWorkerRunnable::DispatchLi
     new LifecycleEventPromiseHandler(mTask, activateImmediately);
   waitUntilPromise->AppendNativeHandler(handler);
   return true;
 }
 
 void
 ServiceWorkerRegistrationInfo::TryToActivate()
 {
+  mWaitingToActivate = true;
   if (!IsControllingDocuments()) {
     Activate();
   }
 }
 
 void
-ContinueActivateTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */)
-{
-  mRegistration->FinishActivate(aSuccess);
-}
-
-void
 ServiceWorkerRegistrationInfo::Activate()
 {
+  MOZ_ASSERT(mWaitingToActivate);
+  mWaitingToActivate = false;
+
   nsRefPtr<ServiceWorkerInfo> activatingWorker = mWaitingWorker;
   nsRefPtr<ServiceWorkerInfo> exitingWorker = mActiveWorker;
 
   nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   swm->InvalidateServiceWorkerRegistrationWorker(this, WhichServiceWorker::WAITING_WORKER | WhichServiceWorker::ACTIVE_WORKER);
   if (!activatingWorker) {
     NS_WARNING("No activatingWorker!");
     return;
@@ -1119,40 +1125,36 @@ ServiceWorkerRegistrationInfo::Activate(
     // Terminate worker
     exitingWorker->UpdateState(ServiceWorkerState::Redundant);
   }
 
   mActiveWorker = activatingWorker.forget();
   mWaitingWorker = nullptr;
   mActiveWorker->UpdateState(ServiceWorkerState::Activating);
 
-  // FIXME(nsm): Unlink appcache if there is one.
-
   swm->CheckPendingReadyPromises();
   swm->StoreRegistration(mPrincipal, this);
 
   // "Queue a task to fire a simple event named controllerchange..."
   nsCOMPtr<nsIRunnable> controllerChangeRunnable =
-    NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm,
-                                                                &ServiceWorkerManager::FireControllerChange,
-                                                                this);
+    NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm, &ServiceWorkerManager::FireControllerChange, this);
   NS_DispatchToMainThread(controllerChangeRunnable);
 
+  // XXXnsm I have my doubts about this. Leaving the main thread means that
+  // subsequent calls to Activate() not from a Register() call, i.e. due to all
+  // controlled documents going away, may lead to two or more calls being
+  // interleaved.
   MOZ_ASSERT(mActiveWorker);
   nsRefPtr<ServiceWorker> serviceWorker;
   nsresult rv =
     swm->CreateServiceWorker(mActiveWorker->ScriptSpec(),
                              mScope,
                              getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    nsCOMPtr<nsIRunnable> r =
-      NS_NewRunnableMethodWithArg<bool>(this,
-                                        &ServiceWorkerRegistrationInfo::FinishActivate,
-                                        false /* success */);
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+    FinishActivate(false /* success */);
     return;
   }
 
   nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
     new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueActivateTask(this)));
 
   nsRefPtr<LifecycleEventWorkerRunnable> r =
     new LifecycleEventWorkerRunnable(serviceWorker->GetWorkerPrivate(), NS_LITERAL_STRING("activate"), handle);
@@ -1678,20 +1680,17 @@ ServiceWorkerManager::HandleError(JSCont
 
   regJob->Fail(init);
   return true;
 }
 
 void
 ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
 {
-  if (mPendingUninstall || !mActiveWorker) {
-    return;
-  }
-
+  MOZ_ASSERT(mActiveWorker);
   if (aSuccess) {
     mActiveWorker->UpdateState(ServiceWorkerState::Activated);
   } else {
     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
     mActiveWorker = nullptr;
   }
 }
 
@@ -1937,34 +1936,59 @@ ServiceWorkerManager::MaybeStartControll
     MOZ_ASSERT(!mControlledDocuments.Contains(aDoc));
     registration->StartControllingADocument();
     // Use the already_AddRefed<> form of Put to avoid the addref-deref since
     // we don't need the registration pointer in this function anymore.
     mControlledDocuments.Put(aDoc, registration.forget());
   }
 }
 
+class ServiceWorkerActivateAfterUnloadingJob MOZ_FINAL : public ServiceWorkerJob
+{
+  nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
+public:
+  ServiceWorkerActivateAfterUnloadingJob(ServiceWorkerJobQueue* aQueue,
+                                         ServiceWorkerRegistrationInfo* aReg)
+    : ServiceWorkerJob(aQueue)
+    , mRegistration(aReg)
+  { }
+
+  void
+  Start()
+  {
+    if (mRegistration->mPendingUninstall) {
+      mRegistration->Clear();
+      nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+      swm->RemoveRegistration(mRegistration);
+    } else {
+      mRegistration->TryToActivate();
+    }
+
+    Done(NS_OK);
+  }
+};
+
 void
 ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
 {
   MOZ_ASSERT(aDoc);
   nsRefPtr<ServiceWorkerRegistrationInfo> registration;
   mControlledDocuments.Remove(aDoc, getter_AddRefs(registration));
   // A document which was uncontrolled does not maintain that state itself, so
   // it will always call MaybeStopControlling() even if there isn't an
   // associated registration. So this check is required.
   if (registration) {
     registration->StopControllingADocument();
     if (!registration->IsControllingDocuments()) {
-      if (registration->mPendingUninstall) {
-        registration->Clear();
-        RemoveRegistration(registration);
-      } else {
-        registration->TryToActivate();
-      }
+      ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(registration->mScope);
+      // The remaining tasks touch registration->mPendingUninstall, so queue
+      // them up in a job.
+      nsRefPtr<ServiceWorkerActivateAfterUnloadingJob> job =
+        new ServiceWorkerActivateAfterUnloadingJob(queue, registration);
+      queue->Append(job);
     }
   }
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::GetScopeForUrl(const nsAString& aUrl, nsAString& aScope)
 {
   nsCOMPtr<nsIURI> uri;
@@ -2408,19 +2432,9 @@ ServiceWorkerManager::CreateNewRegistrat
   ServiceWorkerRegistrationInfo* registration = new ServiceWorkerRegistrationInfo(aScope, aPrincipal);
   // From now on ownership of registration is with
   // mServiceWorkerRegistrationInfos.
   mServiceWorkerRegistrationInfos.Put(aScope, registration);
   AddScope(mOrderedScopes, aScope);
   return registration;
 }
 
-void
-ServiceWorkerManager::MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
-{
-  MOZ_ASSERT(aRegistration);
-  nsRefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
-  if (!newest) {
-    RemoveRegistration(aRegistration);
-  }
-}
-
 END_WORKERS_NAMESPACE
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -44,17 +44,16 @@ namespace workers {
 
 class ServiceWorker;
 class ServiceWorkerInfo;
 
 class ServiceWorkerJobQueue;
 
 class ServiceWorkerJob : public nsISupports
 {
-protected:
   // The queue keeps the jobs alive, so they can hold a rawptr back to the
   // queue.
   ServiceWorkerJobQueue* mQueue;
 
 public:
   NS_DECL_ISUPPORTS
 
   virtual void Start() = 0;
@@ -147,16 +146,17 @@ public:
   nsRefPtr<ServiceWorkerInfo> mActiveWorker;
   nsRefPtr<ServiceWorkerInfo> mWaitingWorker;
   nsRefPtr<ServiceWorkerInfo> mInstallingWorker;
 
   // When unregister() is called on a registration, it is not immediately
   // removed since documents may be controlled. It is marked as
   // pendingUninstall and when all controlling documents go away, removed.
   bool mPendingUninstall;
+  bool mWaitingToActivate;
 
   explicit ServiceWorkerRegistrationInfo(const nsACString& aScope,
                                          nsIPrincipal* aPrincipal);
 
   already_AddRefed<ServiceWorkerInfo>
   Newest()
   {
     nsRefPtr<ServiceWorkerInfo> newest;
@@ -284,19 +284,22 @@ public:
  * The ServiceWorkerManager is a per-process global that deals with the
  * installation, querying and event dispatch of ServiceWorkers for all the
  * origins in the process.
  */
 class ServiceWorkerManager MOZ_FINAL
   : public nsIServiceWorkerManager
   , public nsIIPCBackgroundChildCreateCallback
 {
+  friend class ActivationRunnable;
   friend class GetReadyPromiseRunnable;
   friend class GetRegistrationsRunnable;
   friend class GetRegistrationRunnable;
+  friend class QueueFireUpdateFoundRunnable;
+  friend class ServiceWorkerActivateAfterUnloadingJob;
   friend class ServiceWorkerRegisterJob;
   friend class ServiceWorkerRegistrationInfo;
   friend class ServiceWorkerUnregisterJob;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERMANAGER
   NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
@@ -485,19 +488,16 @@ private:
   }
 
   static PLDHashOperator
   CheckPendingReadyPromisesEnumerator(nsISupports* aSupports,
                                       nsAutoPtr<PendingReadyPromise>& aData,
                                       void* aUnused);
 
   nsClassHashtable<nsISupportsHashKey, PendingReadyPromise> mPendingReadyPromises;
- 
-  void
-  MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
 
   mozilla::ipc::PBackgroundChild* mActor;
 
   struct PendingOperation;
   nsTArray<PendingOperation> mPendingOperations;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerManager,
--- a/dom/workers/test/serviceworkers/install_event_error_worker.js
+++ b/dom/workers/test/serviceworkers/install_event_error_worker.js
@@ -1,4 +1,4 @@
 // Worker that errors on receiving an install event.
 oninstall = function(e) {
   undefined.doSomething;
-};
+}
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = buildapp == 'b2g'
+skip-if = buildapp == 'b2g' || android_version == "10" # bug 1056702
 support-files =
   worker.js
   worker2.js
   worker3.js
   parse_error_worker.js
   activate_event_error_worker.js
   install_event_worker.js
   install_event_error_worker.js
@@ -17,18 +17,23 @@ support-files =
   sw_clients/simple.html
   sw_clients/service_worker_controlled.html
   get_serviced_worker.js
   worker_unregister.js
   worker_update.js
   message_posting_worker.js
 
 [test_unregister.html]
+skip-if = true # bug 1094375
 [test_installation_simple.html]
+skip-if = true # bug 1094375
 [test_get_serviced.html]
 [test_install_event.html]
 [test_navigator.html]
 [test_scopes.html]
+skip-if = true # bug 1126470 and many others
 [test_controller.html]
 [test_workerUpdate.html]
+skip-if = true # Enable after Bug 982726 postMessage is landed.
 [test_workerUnregister.html]
+skip-if = true # Enable after Bug 982726 postMessage is landed.
 [test_post_message.html]
 [test_post_message_advanced.html]
--- a/dom/workers/test/serviceworkers/simpleregister/ready.html
+++ b/dom/workers/test/serviceworkers/simpleregister/ready.html
@@ -1,15 +1,17 @@
 <html>
   <head></head>
   <body>
     <script type="text/javascript">
 
-      window.addEventListener('message', function(evt) {
-        navigator.serviceWorker.ready.then(function() {
-          evt.ports[0].postMessage("WOW!");
-        });
-      }, false);
+       window.addEventListener('message', function(evt) {
+         navigator.serviceWorker.ready.then(function() {
+           navigator.serviceWorker.oncontrollerchange = function(e) {
+             evt.ports[0].postMessage("WOW!");
+           }
+         });
+       }, false);
 
     </script>
   </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_install_event.html
+++ b/dom/workers/test/serviceworkers/test_install_event.html
@@ -33,20 +33,19 @@
       });
     }, function(e) {
       ok(false, "Unexpected Error in nextRegister! " + e);
     });
   }
 
   function installError() {
     // Silence worker errors so they don't cause the test to fail.
-    window.onerror = function(e) {}
+    window.onerror = function() { }
     return navigator.serviceWorker.register("install_event_error_worker.js", { scope: "./install_event" })
       .then(function(swr) {
-        ok(swr.installing instanceof ServiceWorker, "There should be an installing worker if promise resolves.");
         ok(swr.installing.state == "installing", "Installing worker's state should be 'installing'");
         return new Promise(function(resolve, reject) {
           swr.installing.onstatechange = function(e) {
             ok(e.target.state == "redundant", "Installation of worker with error should fail.");
             resolve();
           }
         });
       }).then(function() {
@@ -81,18 +80,17 @@
 
   function unregister() {
     return navigator.serviceWorker.getRegistration("./install_event").then(function(reg) {
       return reg.unregister();
     });
   }
 
   function runTest() {
-    Promise.resolve()
-      .then(simpleRegister)
+    simpleRegister()
       .then(nextRegister)
       .then(installError)
       .then(activateError)
       .then(unregister)
       .then(function() {
         SimpleTest.finish();
       }).catch(function(e) {
         ok(false, "Some test failed with error " + e);
--- a/dom/workers/test/serviceworkers/test_installation_simple.html
+++ b/dom/workers/test/serviceworkers/test_installation_simple.html
@@ -70,16 +70,35 @@
       ok(worker && wr.scope.match(/realworker$/) &&
          worker.scriptURL.match(/worker.js$/), "Valid worker instance should be available.");
     }, function(e) {
       info("Error: " + e.name);
       ok(false, "realWorker Registration should have succeeded!");
     });
   }
 
+  function abortPrevious() {
+    var p = navigator.serviceWorker.register("worker2.js", { scope: "foo/" });
+    var q = navigator.serviceWorker.register("worker3.js", { scope: "foo/" });
+
+    return Promise.all([
+      p.then(function(wr) {
+        ok(false, "First registration should fail with AbortError");
+      }, function(e) {
+        ok(e.name === "AbortError", "First registration should fail with AbortError");
+      }),
+
+      q.then(function(wr) {
+        ok(wr instanceof ServiceWorkerRegistration, "Second registration should succeed");
+      }, function(e) {
+        ok(false, "Second registration should succeed");
+      })
+    ]);
+  }
+
   function networkError404() {
     return navigator.serviceWorker.register("404.js", { scope: "network_error/"}).then(function(w) {
         ok(false, "Should fail with NetworkError");
       }, function(e) {
         ok(e.name === "NetworkError", "Should fail with NetworkError");
       });
   }
 
@@ -87,16 +106,17 @@
     var p = navigator.serviceWorker.register("parse_error_worker.js", { scope: "parse_error/" });
     return p.then(function(wr) {
       ok(false, "Registration should fail with parse error");
       return navigator.serviceWorker.getRegistration("parse_error/").then(function(swr) {
         // See https://github.com/slightlyoff/ServiceWorker/issues/547
         is(swr, undefined, "A failed registration for a scope with no prior controllers should clear itself");
       });
     }, function(e) {
+    info("NSM " + e.name);
       ok(e instanceof Error, "Registration should fail with parse error");
     });
   }
 
   // FIXME(nsm): test for parse error when Update step doesn't happen (directly from register).
 
   function updatefound() {
     var frame = document.createElement("iframe");
@@ -157,16 +177,17 @@
 
   function runTest() {
     simpleRegister()
       .then(readyPromise)
       .then(sameOriginWorker)
       .then(sameOriginScope)
       .then(httpsOnly)
       .then(realWorker)
+      .then(abortPrevious)
       .then(networkError404)
       .then(parseError)
       .then(updatefound)
       .then(checkReadyPromise)
       // put more tests here.
       .then(function() {
         SimpleTest.finish();
       }).catch(function(e) {
--- a/dom/workers/test/serviceworkers/test_scopes.html
+++ b/dom/workers/test/serviceworkers/test_scopes.html
@@ -57,17 +57,17 @@
       ok(getScope(p("sua.html")) === p(""), "Scope should match");
       ok(getScope(p("sub.html")) === p("sub"), "Scope should match");
       ok(getScope(p("sub/dir.html")) === p("sub/dir.html"), "Scope should match");
       ok(getScope(p("sub/dir")) === p("sub/dir"), "Scope should match");
       ok(getScope(p("sub/dir/foo")) === p("sub/dir/"), "Scope should match");
       ok(getScope(p("sub/dir/afoo")) === p("sub/dir/a"), "Scope should match");
       ok(getScope(p("star*wars")) === p("star*"), "Scope should match");
       ok(getScope(p("star/a.html")) === p(""), "Scope should match");
-      resolve();
+      resolve(true);
     });
   }
 
   function runTest() {
     registerWorkers()
       .then(testScopes)
       .then(unregisterWorkers)
       .then(function() {
--- a/dom/workers/test/serviceworkers/unregister/index.html
+++ b/dom/workers/test/serviceworkers/unregister/index.html
@@ -14,28 +14,31 @@
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
 <script class="testbody" type="text/javascript">
 
   if (!parent) {
     info("unregister/index.html should not to be launched directly!");
   }
 
-  SimpleTest.requestFlakyTimeout("Unfortunately we have no way to test for a page being uncontrolled except waiting for ready to not resolve");
   var tId = setTimeout(function() {
+    info("tId timeout!");
     parent.postMessage({ controlled: false }, "*");
     tId = null;
   }, 2000);
 
   navigator.serviceWorker.ready.then(function() {
+  info("Got ready");
     if (tId == null) {
+    info("tId was null");
       parent.postMessage("FAIL!!!", "*");
       return;
     }
 
     clearTimeout(tId);
+    info("tId was non-null");
     parent.postMessage({ controlled: true }, "*");
   });
 
 </script>
 </pre>
 </body>
 </html>