DELETE: 1 patches - 984048-swm-update+ default tip
authorNikhil Marathe <nsm.nikhil@gmail.com>
Wed, 23 Jul 2014 17:34:08 -0700
changeset 1118 67e92f92ff03262ed112f4f16ea226beef84ad64
parent 1117 813230c769c67f7310d8cc5b41c222879820bc8e
push id32
push usernsm.nikhil@gmail.com
push dateThu, 24 Jul 2014 00:32:46 +0000
bugs984048
DELETE: 1 patches - 984048-swm-update+ DELETE: 984048-swm-update+ qparent: 666e5e6b3abf qtip: 666e5e6b3abf top: (none)
984048-swm-update+
series
deleted file mode 100644
--- a/984048-swm-update+
+++ /dev/null
@@ -1,1226 +0,0 @@
-# HG changeset patch
-# Parent 7316a53f3eacf0cc66817a304c472958e1020d75
-# User Nikhil Marathe <nsm.nikhil@gmail.com>
-Bug 984048: Part 3 - Update algorithm. r=ehsan,khuey
-
-diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp
---- a/dom/workers/ServiceWorkerManager.cpp
-+++ b/dom/workers/ServiceWorkerManager.cpp
-@@ -6,30 +6,228 @@
- 
- #include "nsIDocument.h"
- #include "nsPIDOMWindow.h"
- 
- #include "jsapi.h"
- 
- #include "mozilla/Preferences.h"
- #include "mozilla/dom/BindingUtils.h"
-+#include "mozilla/dom/DOMError.h"
-+
- #include "nsContentUtils.h"
- #include "nsCxPusher.h"
- #include "nsNetUtil.h"
-+#include "nsProxyRelease.h"
- #include "nsTArray.h"
- 
- #include "RuntimeService.h"
- #include "ServiceWorker.h"
-+#include "ServiceWorkerUtil.h"
- #include "WorkerInlines.h"
-+#include "WorkerPrivate.h"
-+#include "WorkerRunnable.h"
- 
- using namespace mozilla;
- using namespace mozilla::dom;
- 
- BEGIN_WORKERS_NAMESPACE
- 
-+NS_IMPL_ISUPPORTS0(ServiceWorkerRegistration)
-+
-+UpdatePromise::UpdatePromise()
-+  : mState(Pending)
-+{
-+  MOZ_COUNT_CTOR(UpdatePromise);
-+}
-+
-+UpdatePromise::~UpdatePromise()
-+{
-+  MOZ_COUNT_DTOR(UpdatePromise);
-+}
-+
-+void
-+UpdatePromise::AddPromise(Promise* aPromise)
-+{
-+  MOZ_ASSERT(mState == Pending);
-+  mPromises.AppendElement(aPromise);
-+}
-+
-+void
-+UpdatePromise::ResolveAllPromises(const nsACString& aScriptSpec, const nsACString& aScope)
-+{
-+  AssertIsOnMainThread();
-+  MOZ_ASSERT(mState == Pending);
-+  mState = Resolved;
-+  RuntimeService* rs = RuntimeService::GetOrCreateService();
-+  MOZ_ASSERT(rs);
-+
-+  nsTArray<nsTWeakRef<Promise>> array;
-+  array.SwapElements(mPromises);
-+  for (uint32_t i = 0; i < array.Length(); ++i) {
-+    nsTWeakRef<Promise>& pendingPromise = array.ElementAt(i);
-+    if (pendingPromise) {
-+      nsCOMPtr<nsIGlobalObject> go =
-+        do_QueryInterface(pendingPromise->GetParentObject());
-+      MOZ_ASSERT(go);
-+
-+      AutoSafeJSContext cx;
-+      JS::Rooted<JSObject*> global(cx, go->GetGlobalJSObject());
-+      JSAutoCompartment ac(cx, global);
-+
-+      GlobalObject domGlobal(cx, global);
-+
-+      nsRefPtr<ServiceWorker> serviceWorker;
-+      nsresult rv = rs->CreateServiceWorker(domGlobal,
-+                                            NS_ConvertUTF8toUTF16(aScriptSpec),
-+                                            aScope,
-+                                            getter_AddRefs(serviceWorker));
-+      if (NS_WARN_IF(NS_FAILED(rv))) {
-+        pendingPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
-+        continue;
-+      }
-+
-+      pendingPromise->MaybeResolve(serviceWorker);
-+    }
-+  }
-+}
-+
-+void
-+UpdatePromise::RejectAllPromises(nsresult aRv)
-+{
-+  AssertIsOnMainThread();
-+  MOZ_ASSERT(mState == Pending);
-+  mState = Rejected;
-+
-+  nsTArray<nsTWeakRef<Promise>> array;
-+  array.SwapElements(mPromises);
-+  for (uint32_t i = 0; i < array.Length(); ++i) {
-+    nsTWeakRef<Promise>& pendingPromise = array.ElementAt(i);
-+    if (pendingPromise) {
-+      // Since ServiceWorkerContainer is only exposed to windows we can be
-+      // certain about this cast.
-+      nsCOMPtr<nsPIDOMWindow> window =
-+        do_QueryInterface(pendingPromise->GetParentObject());
-+      MOZ_ASSERT(window);
-+      nsRefPtr<DOMError> domError = new DOMError(window, aRv);
-+      pendingPromise->MaybeReject(domError);
-+    }
-+  }
-+}
-+
-+ServiceWorkerRegistration::ServiceWorkerRegistration(const nsACString& aScope)
-+  : mScope(aScope),
-+    mPendingUninstall(false)
-+{ }
-+
-+ServiceWorkerRegistration::~ServiceWorkerRegistration()
-+{ }
-+
-+//////////////////////////////////////////
-+// All the Runnables
-+//////////////////////////////////////////
-+
-+class CallInstallRunnable : public nsRunnable
-+{
-+  nsRefPtr<ServiceWorkerRegistration> mRegistration;
-+  ServiceWorkerInfo mInfo;
-+public:
-+  CallInstallRunnable(ServiceWorkerRegistration* aRegistration,
-+                      const ServiceWorkerInfo& aInfo)
-+    : mRegistration(aRegistration), mInfo(aInfo)
-+  {
-+    AssertIsOnMainThread();
-+  }
-+
-+  NS_IMETHODIMP
-+  Run()
-+  {
-+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-+    if (swm) {
-+      swm->Install(mRegistration, mInfo);
-+    }
-+
-+    return NS_OK;
-+  }
-+};
-+
-+/*
-+ * This class and ResolvePromisesOnWorkerParseRunnable are used to only resolve
-+ * the register() promises after the Worker has been parsed successfully and
-+ * started running.
-+ *
-+ * If an error occurs during the parse, the ScriptLoader will notify the swm
-+ * before terminating the worker. The instance of
-+ * ResolvePromisesOnWorkerParseRunnable will not be run.
-+ *
-+ * FIXME(nsm): Actually implement the parse error handling (in the same bug),
-+ * right now it will just fire onerror up to the window instead of rejecting
-+ * the promises.
-+ */
-+class ResolvePromisesOnMainThreadRunnable : public nsRunnable
-+{
-+  nsMainThreadPtrHandle<ServiceWorkerRegistration> mRegistration;
-+  const nsCString mScriptSpec;
-+public:
-+  ResolvePromisesOnMainThreadRunnable
-+    (const nsMainThreadPtrHandle<ServiceWorkerRegistration>& aRegistration,
-+     const nsACString& aScriptSpec)
-+    : mRegistration(aRegistration),
-+      mScriptSpec(aScriptSpec)
-+  { }
-+
-+  NS_IMETHODIMP
-+  Run()
-+  {
-+    AssertIsOnMainThread();
-+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-+    if (!swm) {
-+      // We may be shutting down. Even then this shouldn't happen.
-+      return NS_OK;
-+    }
-+
-+    swm->ResolveRegisterPromises(mRegistration, mScriptSpec);
-+    ServiceWorkerInfo info(mScriptSpec);
-+
-+    nsCOMPtr<nsIRunnable> r = new CallInstallRunnable(mRegistration, info);
-+    NS_DispatchToCurrentThread(r);
-+
-+    return NS_OK;
-+  }
-+};
-+
-+class ResolvePromisesOnWorkerParseRunnable : public WorkerRunnable
-+{
-+  nsMainThreadPtrHandle<ServiceWorkerRegistration> mRegistration;
-+public:
-+  ResolvePromisesOnWorkerParseRunnable(WorkerPrivate* aWorkerPrivate,
-+                                       const nsMainThreadPtrHandle<ServiceWorkerRegistration>& aRegistration)
-+    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
-+      mRegistration(aRegistration)
-+  {
-+    AssertIsOnMainThread();
-+  }
-+
-+  bool
-+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-+  {
-+    aWorkerPrivate->AssertIsOnWorkerThread();
-+    if (!aWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
-+      return true;
-+    }
-+
-+    nsRefPtr<ResolvePromisesOnMainThreadRunnable> r =
-+      new ResolvePromisesOnMainThreadRunnable(
-+        mRegistration,
-+        NS_ConvertUTF16toUTF8(aWorkerPrivate->ScriptURL()));
-+    NS_DispatchToMainThread(r);
-+    return true;
-+  }
-+};
-+
- //////////////////////////
- // ServiceWorkerManager //
- //////////////////////////
- 
- NS_IMPL_ADDREF(ServiceWorkerManager)
- NS_IMPL_RELEASE(ServiceWorkerManager)
- 
- NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
-@@ -84,46 +282,45 @@ public:
-     if (NS_WARN_IF(NS_FAILED(rv))) {
-       mPromise->MaybeReject(rv);
-       return NS_OK;
-     }
- 
-     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-     ServiceWorkerManager::ServiceWorkerDomainInfo* domainInfo =
-       swm->mDomainMap.Get(domain);
--    // FIXME(nsm): Refactor this pattern.
-+    // XXXnsm: This pattern can be refactored if we end up using it
-+    // often enough.
-     if (!swm->mDomainMap.Get(domain, &domainInfo)) {
-       domainInfo = new ServiceWorkerManager::ServiceWorkerDomainInfo;
-       swm->mDomainMap.Put(domain, domainInfo);
-     }
- 
--    nsRefPtr<ServiceWorkerRegistration> registration = domainInfo->GetRegistration(mScope);
-+    nsRefPtr<ServiceWorkerRegistration> registration =
-+      domainInfo->GetRegistration(mScope);
- 
-     nsCString spec;
-     rv = mScriptURI->GetSpec(spec);
-     if (NS_WARN_IF(NS_FAILED(rv))) {
-       mPromise->MaybeReject(rv);
-       return NS_OK;
-     }
- 
-     if (registration) {
-       registration->mPendingUninstall = false;
-       if (spec.Equals(registration->mScriptSpec)) {
--        // FIXME(nsm): Force update on Shift+Reload. Algorithm not specified for
--        // that yet.
--
-         // There is an existing update in progress. Resolve with whatever it
-         // results in.
-         if (registration->HasUpdatePromise()) {
-           registration->AddUpdatePromiseObserver(mPromise);
-           return NS_OK;
-         }
- 
--        // There is no update in progress and since SW updating is upto the UA, we
--        // will not update right now. Simply resolve with whatever worker we
-+        // There is no update in progress and since SW updating is upto the UA,
-+        // we will not update right now. Simply resolve with whatever worker we
-         // have.
-         ServiceWorkerInfo info = registration->Newest();
-         if (info.IsValid()) {
-           nsRefPtr<ServiceWorker> serviceWorker;
-           nsresult rv =
-             swm->CreateServiceWorkerForWindow(mWindow,
-                                               info.GetScriptSpec(),
-                                               registration->mScope,
-@@ -138,28 +335,35 @@ public:
-         }
-       }
-     } else {
-       registration = domainInfo->CreateNewRegistration(mScope);
-     }
- 
-     registration->mScriptSpec = spec;
- 
--    // FIXME(nsm): Call Update. Same bug, different patch.
--    // For now if the registration reaches this spot, the promise remains
--    // unresolved.
--    return NS_OK;
-+    rv = swm->Update(registration, mWindow);
-+    MOZ_ASSERT(registration->HasUpdatePromise());
-+
-+    // We append this register() call's promise after calling Update() because
-+    // we don't want this one to be aborted when the others (existing updates
-+    // for the same registration) are aborted. Update() sets a new
-+    // UpdatePromise on the registration.
-+    registration->mUpdatePromise->AddPromise(mPromise);
-+
-+    return rv;
-   }
- };
- 
- // If we return an error code here, the ServiceWorkerContainer will
- // automatically reject the Promise.
- NS_IMETHODIMP
- ServiceWorkerManager::Register(nsIDOMWindow* aWindow, const nsAString& aScope,
--                               const nsAString& aScriptURL, nsISupports** aPromise)
-+                               const nsAString& aScriptURL,
-+                               nsISupports** aPromise)
- {
-   AssertIsOnMainThread();
-   MOZ_ASSERT(aWindow);
- 
-   // XXXnsm Don't allow chrome callers for now, we don't support chrome
-   // ServiceWorkers.
-   MOZ_ASSERT(!nsContentUtils::IsCallerChrome());
- 
-@@ -201,20 +405,19 @@ ServiceWorkerManager::Register(nsIDOMWin
-   }
- 
-   nsCOMPtr<nsIURI> scriptURI;
-   rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, documentURI);
-   if (NS_WARN_IF(NS_FAILED(rv))) {
-     return rv;
-   }
- 
--  // https://github.com/slightlyoff/ServiceWorker/issues/262
--  // allowIfInheritsPrincipal: allow data: URLs for now.
-+  // Data URLs are not allowed.
-   rv = documentPrincipal->CheckMayLoad(scriptURI, true /* report */,
--                                       true /* allowIfInheritsPrincipal */);
-+                                       false /* allowIfInheritsPrincipal */);
-   if (NS_FAILED(rv)) {
-     return NS_ERROR_DOM_SECURITY_ERR;
-   }
- 
-   nsCOMPtr<nsIURI> scopeURI;
-   rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, documentURI);
-   if (NS_WARN_IF(NS_FAILED(rv))) {
-     return NS_ERROR_DOM_SECURITY_ERR;
-@@ -233,21 +436,64 @@ ServiceWorkerManager::Register(nsIDOMWin
-   }
- 
-   nsRefPtr<nsIRunnable> registerRunnable =
-     new RegisterRunnable(window, cleanedScope, scriptURI, promise);
-   promise.forget(aPromise);
-   return NS_DispatchToCurrentThread(registerRunnable);
- }
- 
-+void
-+ServiceWorkerManager::RejectUpdatePromiseObservers(ServiceWorkerRegistration* aRegistration,
-+                                                   nsresult aRv)
-+{
-+  AssertIsOnMainThread();
-+  MOZ_ASSERT(aRegistration->HasUpdatePromise());
-+  aRegistration->mUpdatePromise->RejectAllPromises(aRv);
-+  aRegistration->mUpdatePromise = nullptr;
-+}
-+
-+/*
-+ * Update() does not return the Promise that the spec says it should. Callers
-+ * may access the registration's (new) Promise after calling this method.
-+ */
- NS_IMETHODIMP
- ServiceWorkerManager::Update(ServiceWorkerRegistration* aRegistration,
-                              nsPIDOMWindow* aWindow)
- {
--  // FIXME(nsm): Same bug, different patch.
-+  if (aRegistration->HasUpdatePromise()) {
-+    NS_WARNING("Already had a UpdatePromise. Aborting that one!");
-+    RejectUpdatePromiseObservers(aRegistration, NS_ERROR_DOM_ABORT_ERR);
-+    MOZ_ASSERT(aRegistration->mFetcher);
-+    aRegistration->mFetcher->Abort();
-+    aRegistration->mFetcher = nullptr;
-+  }
-+
-+  if (aRegistration->mInstallingWorker.IsValid()) {
-+    // FIXME(nsm): Terminate the worker. We still haven't figured out worker
-+    // instance ownership when not associated with a window, so let's wait on
-+    // this.
-+    // FIXME(nsm): We should be setting the state on the actual worker
-+    // instance.
-+    // FIXME(nsm): Fire "statechange" on installing worker instance.
-+    aRegistration->mInstallingWorker.Invalidate();
-+  }
-+
-+  aRegistration->mUpdatePromise = new UpdatePromise();
-+  // FIXME(nsm): Bug 931249. If we don't need to fetch & install, resolve
-+  // promise and skip this.
-+  // FIXME(nsm): Bug 931249. Force cache update if > 1 day.
-+
-+  aRegistration->mFetcher = new ServiceWorkerFetcher();
-+  nsresult rv = aRegistration->mFetcher->asyncFetch(aRegistration, aWindow);
-+  if (NS_WARN_IF(NS_FAILED(rv))) {
-+    RejectUpdatePromiseObservers(aRegistration, rv);
-+    return rv;
-+  }
-+
-   return NS_OK;
- }
- 
- // If we return an error, ServiceWorkerContainer will reject the Promise.
- NS_IMETHODIMP
- ServiceWorkerManager::Unregister(nsIDOMWindow* aWindow, const nsAString& aScope,
-                                  nsISupports** aPromise)
- {
-@@ -261,21 +507,129 @@ ServiceWorkerManager::Unregister(nsIDOMW
- 
-   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
- }
- 
- /* static */
- already_AddRefed<ServiceWorkerManager>
- ServiceWorkerManager::GetInstance()
- {
--  nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
-+  nsCOMPtr<nsIServiceWorkerManager> swm =
-+    do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
-   nsRefPtr<ServiceWorkerManager> concrete = do_QueryObject(swm);
-   return concrete.forget();
- }
- 
-+void
-+ServiceWorkerManager::ResolveRegisterPromises(ServiceWorkerRegistration* aRegistration,
-+                                              const nsACString& aWorkerScriptSpec)
-+{
-+  AssertIsOnMainThread();
-+  MOZ_ASSERT(aRegistration->HasUpdatePromise());
-+  if (aRegistration->mUpdatePromise->IsRejected()) {
-+    aRegistration->mUpdatePromise = nullptr;
-+    return;
-+  }
-+
-+  aRegistration->mUpdatePromise->ResolveAllPromises(aWorkerScriptSpec,
-+                                                    aRegistration->mScope);
-+  aRegistration->mUpdatePromise = nullptr;
-+}
-+
-+// Must NS_Free() aString
-+void
-+ServiceWorkerManager::FinishFetch(ServiceWorkerFetcher* aFetcher,
-+                                  nsresult aStatus, uint32_t aStringLen,
-+                                  const uint8_t* aString)
-+{
-+  AssertIsOnMainThread();
-+
-+  MOZ_ASSERT(aFetcher);
-+
-+  nsRefPtr<ServiceWorkerRegistration> registration = aFetcher->mRegistration;
-+  MOZ_ASSERT(registration);
-+  MOZ_ASSERT(registration->mFetcher == aFetcher);
-+
-+  // We want to free the fetcher when this function exits, irrespective of how
-+  // it does so, so we steal it from the registration. But we do want to use it below.
-+  nsRefPtr<ServiceWorkerFetcher> fetcher = registration->mFetcher.forget();
-+
-+  MOZ_ASSERT(registration->HasUpdatePromise());
-+  if (registration->mUpdatePromise->IsRejected()) {
-+    registration->mUpdatePromise = nullptr;
-+    return;
-+  }
-+
-+  nsresult promiseRejectError = NS_OK;
-+  if (aStatus == NS_ERROR_REDIRECT_LOOP) {
-+    promiseRejectError = NS_ERROR_DOM_SECURITY_ERR;
-+  } else if (NS_FAILED(aStatus)) {
-+    promiseRejectError = NS_ERROR_DOM_NETWORK_ERR;
-+  }
-+
-+  if (NS_FAILED(promiseRejectError)) {
-+    RejectUpdatePromiseObservers(registration, promiseRejectError);
-+    registration->mUpdatePromise = nullptr;
-+    return;
-+  }
-+
-+  ServiceWorkerInfo newest = registration->Newest();
-+  if (newest.IsValid() &&
-+      newest.GetScriptSpec().Equals(registration->mScriptSpec)) {
-+    // FIXME(nsm): Bug 931249 Check byte wise
-+    // This should be async since equality checks of large files may
-+    // lead to jank! In which case everything from this point on will become
-+    // async.
-+  }
-+
-+  nsRefPtr<ServiceWorker> worker;
-+  nsresult rv = CreateServiceWorkerForWindow(fetcher->mWindow,
-+                                             registration->mScriptSpec,
-+                                             registration->mScope,
-+                                             getter_AddRefs(worker));
-+
-+  // FIXME(nsm): Once Bug 931249 is fixed, re-add the check!
-+  // The spec says to check mUpdatePromiseRejected again, but this whole
-+  // function is synchronous for now, so we don't bother. Once the byte
-+  // equality checks are added above, this will need to change.
-+  if (NS_WARN_IF(NS_FAILED(rv))) {
-+    RejectUpdatePromiseObservers(registration, rv);
-+    return;
-+  }
-+
-+  // FIXME(nsm): The promise has to be rejected if parse errors occur. Same
-+  // bug, different patch.
-+
-+  nsMainThreadPtrHandle<ServiceWorkerRegistration> handle =
-+    new nsMainThreadPtrHolder<ServiceWorkerRegistration>(registration);
-+
-+  // If parse succeeds, promises will be resolved with this worker, otherwise
-+  // they'll be rejected by error handler.
-+  // We don't pass the instance of ServiceWorker because each Promise will need
-+  // to create one in its own window.
-+  nsRefPtr<WorkerRunnable> r =
-+    new ResolvePromisesOnWorkerParseRunnable(worker->GetWorkerPrivate(),
-+                                             handle);
-+  AutoSafeJSContext cx;
-+  if (!r->Dispatch(cx)) {
-+    RejectUpdatePromiseObservers(registration,
-+                                 NS_ERROR_DOM_INVALID_STATE_ERR);
-+    return;
-+  }
-+
-+  // The call to Install is queued by ResolvePromisesOnMainThreadRunnable.
-+}
-+
-+void
-+ServiceWorkerManager::Install(ServiceWorkerRegistration* aRegistration,
-+                              ServiceWorkerInfo aServiceWorkerInfo)
-+{
-+  // FIXME(nsm): Same bug, different patch.
-+}
-+
- NS_IMETHODIMP
- ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
-                                                    const nsACString& aScriptSpec,
-                                                    const nsACString& aScope,
-                                                    ServiceWorker** aServiceWorker)
- {
-   AssertIsOnMainThread();
-   MOZ_ASSERT(aWindow);
-diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h
---- a/dom/workers/ServiceWorkerManager.h
-+++ b/dom/workers/ServiceWorkerManager.h
-@@ -18,87 +18,135 @@
- #include "nsTArrayForwardDeclare.h"
- #include "nsTWeakRef.h"
- 
- namespace mozilla {
- namespace dom {
- namespace workers {
- 
- class ServiceWorker;
-+class ServiceWorkerFetcher;
-+
-+/**
-+ * UpdatePromise is a utility class that sort of imitates Promise, but not
-+ * completely. Using DOM Promise from C++ is a pain when we know the precise types
-+ * we're dealing with since it involves dealing with JSAPI. In this case we
-+ * also don't (yet) need the 'thenables added after resolution should trigger
-+ * immediately' support and other things like that. All we want is something
-+ * that works reasonably Promise like and can resolve real DOM Promises added
-+ * pre-emptively.
-+ */
-+class UpdatePromise MOZ_FINAL
-+{
-+public:
-+  UpdatePromise();
-+  ~UpdatePromise();
-+
-+  void AddPromise(Promise* aPromise);
-+  void ResolveAllPromises(const nsACString& aScriptSpec, const nsACString& aScope);
-+  void RejectAllPromises(nsresult aRv);
-+
-+  bool
-+  IsRejected() const
-+  {
-+    return mState == Rejected;
-+  }
-+
-+private:
-+  enum {
-+    Pending,
-+    Resolved,
-+    Rejected
-+  } mState;
-+
-+  // XXXnsm: Right now we don't need to support AddPromise() after
-+  // already being resolved (i.e. true Promise-like behaviour).
-+  nsTArray<nsTWeakRef<Promise>> mPromises;
-+};
- 
- /*
-  * Wherever the spec treats a worker instance and a description of said worker
-  * as the same thing; i.e. "Resolve foo with
-  * _GetNewestWorker(serviceWorkerRegistration)", we represent the description
-  * by this class and spawn a ServiceWorker in the right global when required.
-  */
- class ServiceWorkerInfo
- {
--  const nsCString mScriptSpec;
-+  nsCString mScriptSpec;
- public:
- 
-   bool
-   IsValid() const
-   {
-     return !mScriptSpec.IsVoid();
-   }
- 
-+  void
-+  Invalidate()
-+  {
-+    mScriptSpec.SetIsVoid(true);
-+  }
-+
-   const nsCString&
-   GetScriptSpec() const
-   {
-     MOZ_ASSERT(IsValid());
-     return mScriptSpec;
-   }
- 
-   ServiceWorkerInfo()
--  { }
-+  {
-+    Invalidate();
-+  }
- 
-   explicit ServiceWorkerInfo(const nsACString& aScriptSpec)
-     : mScriptSpec(aScriptSpec)
-   { }
- };
- 
--class ServiceWorkerRegistration
-+// Needs to inherit from nsISupports because NS_ProxyRelease() does not support
-+// non-ISupports classes.
-+class ServiceWorkerRegistration : public nsISupports
- {
- public:
--  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistration)
-+  NS_DECL_ISUPPORTS
- 
-   nsCString mScope;
-   // The scriptURL for the registration. This may be completely different from
-   // the URLs of the following three workers.
-   nsCString mScriptSpec;
- 
-   ServiceWorkerInfo mCurrentWorker;
-   ServiceWorkerInfo mWaitingWorker;
-   ServiceWorkerInfo mInstallingWorker;
- 
--  bool mHasUpdatePromise;
-+  nsAutoPtr<UpdatePromise> mUpdatePromise;
-+  nsRefPtr<ServiceWorkerFetcher> mFetcher;
- 
-   void
-   AddUpdatePromiseObserver(Promise* aPromise)
-   {
--    // FIXME(nsm): Same bug, different patch.
-+    MOZ_ASSERT(HasUpdatePromise());
-+    mUpdatePromise->AddPromise(aPromise);
-   }
- 
-   bool
-   HasUpdatePromise()
-   {
--    return mHasUpdatePromise;
-+    return mUpdatePromise;
-   }
- 
-   // 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;
- 
--  explicit ServiceWorkerRegistration(const nsACString& aScope)
--    : mScope(aScope),
--      mHasUpdatePromise(false),
--      mPendingUninstall(false)
--  { }
-+  // Not inlined to avoid including ServiceWorkerFetcher.
-+  explicit ServiceWorkerRegistration(const nsACString& aScope);
-+  virtual ~ServiceWorkerRegistration();
- 
-   ServiceWorkerInfo
-   Newest() const
-   {
-     if (mInstallingWorker.IsValid()) {
-       return mInstallingWorker;
-     } else if (mWaitingWorker.IsValid()) {
-       return mWaitingWorker;
-@@ -119,16 +167,17 @@ 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
- {
-   friend class RegisterRunnable;
-+  friend class CallInstallRunnable;
- 
- public:
-   NS_DECL_ISUPPORTS
-   NS_DECL_NSISERVICEWORKERMANAGER
-   NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID)
- 
-   static ServiceWorkerManager* FactoryCreate()
-   {
-@@ -167,33 +216,43 @@ public:
-       // mServiceWorkerRegistrations.
-       mServiceWorkerRegistrations.Put(aScope, registration);
-       return registration;
-     }
-   };
- 
-   nsClassHashtable<nsCStringHashKey, ServiceWorkerDomainInfo> mDomainMap;
- 
--  // FIXME(nsm): What do we do if a page calls for register("/foo_worker.js", { scope: "/*"
--  // }) and then another page calls register("/bar_worker.js", { scope: "/*" })
--  // while the install is in progress. The async install steps for register
--  // bar_worker.js could finish before foo_worker.js, but bar_worker still has
--  // to be the winning controller.
--  // FIXME(nsm): Move this into per domain?
-+  void
-+  ResolveRegisterPromises(ServiceWorkerRegistration* aRegistration,
-+                          const nsACString& aWorkerScriptSpec);
-+
-+  void
-+  RejectUpdatePromiseObservers(ServiceWorkerRegistration* aRegistration,
-+                               nsresult aResult);
-+
-+  void
-+  FinishFetch(ServiceWorkerFetcher* aFetcher,
-+              nsresult aStatus, uint32_t aStringLen,
-+              const uint8_t* aString);
- 
-   static already_AddRefed<ServiceWorkerManager>
-   GetInstance();
- 
- private:
-   ServiceWorkerManager();
-   ~ServiceWorkerManager();
- 
-   NS_IMETHOD
-   Update(ServiceWorkerRegistration* aRegistration, nsPIDOMWindow* aWindow);
- 
-+  void
-+  Install(ServiceWorkerRegistration* aRegistration,
-+          ServiceWorkerInfo aServiceWorkerInfo);
-+
-   NS_IMETHODIMP
-   CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
-                                const nsACString& aScriptSpec,
-                                const nsACString& aScope,
-                                ServiceWorker** aServiceWorker);
- 
-   static PLDHashOperator
-   CleanupServiceWorkerInformation(const nsACString& aDomain,
-diff --git a/dom/workers/ServiceWorkerUtil.cpp b/dom/workers/ServiceWorkerUtil.cpp
---- a/dom/workers/ServiceWorkerUtil.cpp
-+++ b/dom/workers/ServiceWorkerUtil.cpp
-@@ -1,23 +1,27 @@
- /* This Source Code Form is subject to the terms of the Mozilla Public
-  * License, v. 2.0. If a copy of the MPL was not distributed with this
-  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- 
- #include "ServiceWorkerUtil.h"
- 
-+#include "nsICachingChannel.h"
-+#include "nsICacheEntry.h"
- #include "nsIChannel.h"
- #include "nsIContentPolicy.h"
- #include "nsIDocument.h"
-+#include "nsIHttpChannel.h"
- #include "nsIIOService.h"
- #include "nsIURI.h"
- 
- #include "nsContentPolicyUtils.h"
- #include "nsNetUtil.h"
- #include "nsNetCID.h"
-+#include "nsXULAppAPI.h"
- #include "ServiceWorkerManager.h"
- 
- BEGIN_WORKERS_NAMESPACE
- 
- NS_IMETHODIMP
- ServiceWorkerFetcher::asyncFetch(ServiceWorkerRegistration* aRegistration,
-                                  nsPIDOMWindow* aWindow)
- {
-@@ -83,93 +87,192 @@ ServiceWorkerFetcher::asyncFetch(Service
-   // creator.)
-   rv = doc->NodePrincipal()->CheckMayLoad(uri, false, true);
-   if (NS_WARN_IF(NS_FAILED(rv))) {
-     return NS_ERROR_DOM_SECURITY_ERR;
-   }
- 
-   // FIXME(nsm): Verify CSP.
- 
--  uint32_t flags = nsIRequest::INHIBIT_CACHING | nsIRequest::LOAD_BYPASS_CACHE;
-+  uint32_t flags = 0; //nsIRequest::INHIBIT_CACHING | nsIRequest::LOAD_BYPASS_CACHE;
- 
-   nsCOMPtr<nsIChannel> channel;
-   rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, loadGroup, nullptr,
-                      flags, nullptr);
-   if (NS_WARN_IF(NS_FAILED(rv))) {
-     return rv;
-   }
- 
-   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
-   if (!httpChannel) {
-     return NS_ERROR_FAILURE;
-   }
- 
-   // Prevent redirects.
-   httpChannel->SetRedirectionLimit(0);
- 
--  nsCOMPtr<nsIStreamLoader> loader;
--  rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
-+  return channel->AsyncOpen(this, nullptr);
-+}
-+
-+NS_IMETHODIMP
-+ServiceWorkerFetcher::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
-+{
-+  nsresult rv;
-+
-+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
-+    // FIXME(nsm): For e10s.
-+    return NS_OK;
-+  }
-+
-+  mScriptHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
-   if (NS_WARN_IF(NS_FAILED(rv))) {
-     return rv;
-   }
- 
--  return channel->AsyncOpen(loader, nullptr);
-+  rv = mScriptHash->Init(nsICryptoHash::SHA1);
-+  if (NS_WARN_IF(NS_FAILED(rv))) {
-+    return rv;
-+  }
-+
-+  nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
-+  // FIXME(nsm): Error checking and request success checking.
-+  nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(channel);
-+  MOZ_ASSERT(cachingChannel);
-+
-+  nsCOMPtr<nsISupports> cacheToken;
-+  cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
-+  if (cacheToken) {
-+    nsCOMPtr<nsICacheEntry> cacheDescriptor(do_QueryInterface(cacheToken));
-+
-+    nsCString key;
-+    cacheDescriptor->GetKey(key);
-+    fprintf(stderr, "NSM OnStartRequest cache key %s\n", key.get());
-+
-+    uint32_t expiry;
-+    cacheDescriptor->GetExpirationTime(&expiry);
-+    fprintf(stderr, "NSM OnStartRequest cache expiry %d\n", expiry);
-+
-+    rv = cacheDescriptor->GetMetaDataElement("serviceworker-script-hash", getter_Copies(mOldHashValue));
-+    if (NS_WARN_IF(NS_FAILED(rv))) {
-+      mOldHashValue.Truncate();
-+    }
-+  }
-+  return NS_OK;
- }
- 
- NS_IMETHODIMP
--ServiceWorkerFetcher::OnStreamComplete(nsIStreamLoader* aLoader,
--                                       nsISupports* aContext, nsresult aStatus,
--                                       uint32_t aStringLen,
--                                       const uint8_t* aString)
-+ServiceWorkerFetcher::CheckNewHash(nsIRequest* aRequest)
- {
--  if (mAborted) {
--    NS_WARNING("Aborted");
-+  if (!mScriptHash) {
-     return NS_OK;
-   }
- 
--  // If we get a 404 or something, indicate failure to SWM.
--  nsCOMPtr<nsIRequest> request;
--  nsresult rv = aLoader->GetRequest(getter_AddRefs(request));
-+  nsresult rv = mScriptHash->Finish(true /* ASCII */, mNewHashValue);
-+  mScriptHash = nullptr;
-+
-   if (NS_WARN_IF(NS_FAILED(rv))) {
--    // ServiceWorkerManager::GetInstance()->FinishFetch(this, rv, 0, nullptr);
--    return NS_OK;
-+    return rv;
-   }
- 
--  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
--  if (httpChannel) {
--    bool requestSucceeded;
--    rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
-+  fprintf(stderr, "NSM Old hash %s\n", mOldHashValue.get());
-+  fprintf(stderr, "NSM New hash %s\n", mNewHashValue.get());
-+
-+  fprintf(stderr, "NSM Old and new hash values equal? %d\n", (mOldHashValue == mNewHashValue));
-+  
-+  // Store the manifest content hash value to the new
-+  // offline cache token
-+  nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(aRequest, &rv);
-+  NS_ENSURE_SUCCESS(rv, rv);
-+
-+  nsCOMPtr<nsISupports> cacheToken;
-+  cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
-+  if (cacheToken) {
-+    nsCOMPtr<nsICacheEntry> cacheDescriptor(do_QueryInterface(cacheToken, &rv));
-+    NS_ENSURE_SUCCESS(rv, rv);
-+
-+    rv = cacheDescriptor->SetMetaDataElement("serviceworker-script-hash", mNewHashValue.get());
-     if (NS_WARN_IF(NS_FAILED(rv))) {
--      // ServiceWorkerManager::GetInstance()->FinishFetch(this, rv, 0, nullptr);
--      return NS_OK;
--    }
--
--    if (requestSucceeded) {
--      // ServiceWorkerManager::GetInstance()->FinishFetch(this, aStatus,
--      //                                                  aStringLen, aString);
--    } else {
--      // ServiceWorkerManager::GetInstance()->FinishFetch(this,
--      //                                                  NS_ERROR_NOT_AVAILABLE,
--      //                                                  aStringLen, aString);
-+      return rv;
-     }
-   }
- 
-   return NS_OK;
- }
- 
- NS_IMETHODIMP
--ServiceWorkerFetcher::OnCacheEntryCheck(nsICacheEntry *aEntry, nsIApplicationCache *aApplicationCache, uint32_t* aEntryState)
-+ServiceWorkerFetcher::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatusCode)
- {
--  fprintf(stderr, "NSM OnCacheEntryAvailable %p %p\n", aEntry, aApplicationCache);
--  *aEntryState = nsICacheEntryOpenCallback::ENTRY_WANTED;
-+  if (mAborted) {
-+    NS_WARNING("Aborted");
-+    return NS_OK;
-+  }
-+
-+  nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-+  // We can assert this because a Fetcher can only be created from the SWM.
-+  MOZ_ASSERT(swm);
-+
-+  nsresult rv = CheckNewHash(aRequest);
-+  if (NS_WARN_IF(NS_FAILED(rv))) {
-+    swm->FinishFetch(this, rv, 0, nullptr);
-+  }
-+
-+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
-+  if (httpChannel) {
-+    bool requestSucceeded;
-+    rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
-+    if (NS_WARN_IF(NS_FAILED(rv))) {
-+      swm->FinishFetch(this, rv, 0, nullptr);
-+      return NS_OK;
-+    }
-+
-+    if (requestSucceeded) {
-+      swm->FinishFetch(this, aStatusCode, 0, nullptr);
-+    } else {
-+      swm->FinishFetch(this, NS_ERROR_NOT_AVAILABLE, 0, nullptr);
-+    }
-+  }
-+
-+  return NS_OK;
-+}
-+
-+/* static */ NS_IMETHODIMP
-+ServiceWorkerFetcher::UpdateHash(nsIInputStream* aInputStream,
-+                                 void* aClosure,
-+                                 const char* aFromSegment,
-+                                 uint32_t aOffset,
-+                                 uint32_t aCount,
-+                                 uint32_t* aBytesConsumed)
-+{
-+  ServiceWorkerFetcher* fetcher =
-+    static_cast<ServiceWorkerFetcher*>(aClosure);
-+  
-+  *aBytesConsumed = aCount;
-+
-+  nsresult rv;
-+  if (!fetcher->mScriptHash) {
-+    return NS_OK;
-+  }
-+
-+  rv = fetcher->mScriptHash->Update(
-+         reinterpret_cast<const uint8_t*>(aFromSegment),
-+         aCount);
-+
-+  if (NS_WARN_IF(NS_FAILED(rv))) {
-+    return rv;
-+  }
-+
-   return NS_OK;
- }
- 
- NS_IMETHODIMP
--ServiceWorkerFetcher::OnCacheEntryAvailable(nsICacheEntry *aEntry, bool aNew, nsIApplicationCache *aApplicationCache, nsresult aResult)
-+ServiceWorkerFetcher::OnDataAvailable(nsIRequest* aRequest,
-+                                      nsISupports* aContext,
-+                                      nsIInputStream* aInputStream,
-+                                      uint64_t aOffset, uint32_t aCount)
- {
--  fprintf(stderr, "NSM OnCacheEntryAvailable %p %d %p %d\n", aEntry, aNew, aApplicationCache, aResult);
-+  uint32_t bytesRead = 0;
-+  aInputStream->ReadSegments(UpdateHash, this, aCount, &bytesRead);
-   return NS_OK;
- }
- 
--NS_IMPL_ISUPPORTS(ServiceWorkerFetcher, nsIStreamLoaderObserver, nsICacheEntryOpenCallback);
-+NS_IMPL_ISUPPORTS(ServiceWorkerFetcher, nsIStreamListener)
- 
- END_WORKERS_NAMESPACE
-diff --git a/dom/workers/ServiceWorkerUtil.h b/dom/workers/ServiceWorkerUtil.h
---- a/dom/workers/ServiceWorkerUtil.h
-+++ b/dom/workers/ServiceWorkerUtil.h
-@@ -1,17 +1,17 @@
- /* This Source Code Form is subject to the terms of the Mozilla Public
-  * License, v. 2.0. If a copy of the MPL was not distributed with this
-  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- 
- #ifndef mozilla_dom_workers_serviceworkerutil_h
- #define mozilla_dom_workers_serviceworkerutil_h
- 
--#include "nsIStreamLoader.h"
--#include "nsICacheEntryOpenCallback.h"
-+#include "nsICryptoHash.h"
-+#include "nsIStreamListener.h"
- 
- #include "nsCOMPtr.h"
- 
- #include "Workers.h"
- 
- class nsIDocument;
- 
- BEGIN_WORKERS_NAMESPACE
-@@ -22,35 +22,38 @@ class ServiceWorkerRegistration;
- /*
-  * FIXME(nsm)
-  * This class pretends to implement the ServiceWorker update logic for now; stuff like
-  * "byte-wise equal" and "more than 24 hours".
-  *
-  * The intention is that it will actually do those things a little later once
-  * the other registration bits have landed.
-  */
--class ServiceWorkerFetcher MOZ_FINAL : public nsIStreamLoaderObserver,
--                                       public nsICacheEntryOpenCallback
-+class ServiceWorkerFetcher MOZ_FINAL : public nsIStreamListener
- {
-   // Owned by SWM's hashtable. The registration will maintain a refptr to this,
-   // so we don't have to worry about mRegistration being freed.
-   ServiceWorkerRegistration* mRegistration;
-   // The window from where a register() call triggered the current fetch.
-   // This may be null on SoftUpdate().
-   nsCOMPtr<nsPIDOMWindow> mWindow;
- 
-   // Don't inform the SWM of a finished transaction if it doesn't care anymore.
-   bool mAborted;
- 
-+  nsCOMPtr<nsICryptoHash> mScriptHash;
-+  nsCString mOldHashValue;
-+  nsCString mNewHashValue;
-+
-   // The fetcher is a one-time use class.
-   DebugOnly<bool> mUsed;
- public:
-   NS_DECL_ISUPPORTS
--  NS_DECL_NSISTREAMLOADEROBSERVER
--  NS_DECL_NSICACHEENTRYOPENCALLBACK
-+  NS_DECL_NSIREQUESTOBSERVER
-+  NS_DECL_NSISTREAMLISTENER
- 
-   friend class ServiceWorkerManager;
- 
-   ServiceWorkerFetcher()
-     : mRegistration(nullptr), mAborted(false)
- #ifdef DEBUG
-     , mUsed(false)
- #endif
-@@ -62,13 +65,25 @@ public:
-   void
-   Abort()
-   {
-     mAborted = true;
-   }
- 
-   NS_IMETHOD
-   asyncFetch(ServiceWorkerRegistration* aRegistration, nsPIDOMWindow* aWindow);
-+
-+private:
-+  NS_IMETHOD
-+  CheckNewHash(nsIRequest* aRequest);
-+
-+  static nsresult
-+  UpdateHash(nsIInputStream* aInputStream,
-+             void* aClosure,
-+             const char* aFromSegment,
-+             uint32_t aOffset,
-+             uint32_t aCount,
-+             uint32_t* aBytesConsumed);
- };
- 
- END_WORKERS_NAMESPACE
- 
- #endif // mozilla_dom_workers_serviceworkerutil_h
-diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini
---- a/dom/workers/test/serviceworkers/mochitest.ini
-+++ b/dom/workers/test/serviceworkers/mochitest.ini
-@@ -1,2 +1,8 @@
-+[DEFAULT]
-+support-files =
-+  worker.js
-+  worker2.js
-+  worker3.js
-+
- [test_installation_simple.html]
- [test_navigator.html]
-diff --git a/dom/workers/test/serviceworkers/test_installation_simple.html b/dom/workers/test/serviceworkers/test_installation_simple.html
---- a/dom/workers/test/serviceworkers/test_installation_simple.html
-+++ b/dom/workers/test/serviceworkers/test_installation_simple.html
-@@ -45,24 +45,70 @@
-     });
- 
-     return promise.then(function() {
-       return navigator.serviceWorker.register("/worker.js");
-     }).then(function(w) {
-       ok(false, "non-HTTPS pages cannot register ServiceWorkers");
-     }, function(e) {
-       ok(e.name === "SecurityError", "Should fail with a SecurityError");
-+    }).then(function() {
-+      return new Promise((resolve) => SpecialPowers.popPrefEnv(resolve));
-     });
-   }
- 
-+  function realWorker() {
-+    var p = navigator.serviceWorker.register("worker.js");
-+    return p.then(function(w) {
-+      ok(w instanceof ServiceWorker, "Register a ServiceWorker");
-+      info(w.scope);
-+      ok(w.scope == (new URL("*", document.baseURI)).href, "Scope should match");
-+      ok(w.url == (new URL("worker.js", document.baseURI)).href, "URL should be of the worker");
-+      ok(w.state == "parsed", "Should have been parsed");
-+    }, function(e) {
-+      info(e.name);
-+      ok(false, "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(w) {
-+        ok(false, "First registration should fail with AbortError");
-+      }, function(e) {
-+        ok(e.name === "AbortError", "First registration should fail with AbortError");
-+      }),
-+
-+      q.then(function(w) {
-+        ok(w instanceof ServiceWorker, "Second registration should succeed");
-+      }, function(e) {
-+        ok(false, "Second registration should succeed");
-+      })
-+    ]);
-+  }
-+
-+  function networkError404() {
-+    return navigator.serviceWorker.register("404.js").then(function(w) {
-+        ok(false, "Should fail with NetworkError");
-+      }, function(e) {
-+        ok(e.name === "NetworkError", "Should fail with NetworkError");
-+      });
-+  }
-+
-   function runTest() {
-     simpleRegister()
-       .then(sameOriginWorker)
-       .then(sameOriginScope)
-       .then(httpsOnly)
-+      .then(realWorker)
-+      .then(abortPrevious)
-+      .then(networkError404)
-       // put more tests here.
-       .then(function() {
-         SimpleTest.finish();
-       }).catch(function(e) {
-         ok(false, "Some test failed with error " + e);
-         SimpleTest.finish();
-       });
-   }
-diff --git a/dom/workers/test/serviceworkers/worker.js b/dom/workers/test/serviceworkers/worker.js
-new file mode 100644
---- /dev/null
-+++ b/dom/workers/test/serviceworkers/worker.js
-@@ -0,0 +1,1 @@
-+// empty worker, always succeed!
-diff --git a/dom/workers/test/serviceworkers/worker2.js b/dom/workers/test/serviceworkers/worker2.js
-new file mode 100644
---- /dev/null
-+++ b/dom/workers/test/serviceworkers/worker2.js
-@@ -0,0 +1,1 @@
-+// worker2.js
-diff --git a/dom/workers/test/serviceworkers/worker3.js b/dom/workers/test/serviceworkers/worker3.js
-new file mode 100644
---- /dev/null
-+++ b/dom/workers/test/serviceworkers/worker3.js
-@@ -0,0 +1,1 @@
-+// worker3.js
--- a/series
+++ b/series
@@ -12,9 +12,8 @@ 903441-navigation-event
 903441-setup-request
 903441-intercepts
 903441-sw-nointercept
 foo
 898524-cache
 sw-install-tests2
 sw-more-install-tests
 943704-importscripts
-984048-swm-update+