Bug 1201127 - Return the same ServiceWorkerRegistration object from service worker APIs dealing with the same underlying registration object; r=jdm
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 23 Nov 2015 13:38:08 -0500
changeset 308534 98fb5254f78840c629f9c4d603a0c9de3555aada
parent 308533 a93ced84ec3e6d54069a827880b7474dd5145f03
child 308535 beb1ebb12542681c7ad55f8ba1f69e26a01d1d25
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs1201127
milestone45.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 1201127 - Return the same ServiceWorkerRegistration object from service worker APIs dealing with the same underlying registration object; r=jdm
dom/base/nsGlobalWindow.cpp
dom/base/nsPIDOMWindow.h
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
dom/workers/ServiceWorkerRegistration.cpp
dom/workers/ServiceWorkerRegistration.h
dom/workers/test/serviceworkers/test_install_event.html
testing/web-platform/mozilla/tests/service-workers/service-worker/getregistration.https.html
testing/web-platform/mozilla/tests/service-workers/service-worker/getregistrations.https.html
testing/web-platform/mozilla/tests/service-workers/service-worker/multiple-register.https.html
testing/web-platform/mozilla/tests/service-workers/service-worker/unregister-then-register.https.html
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -220,16 +220,17 @@
 #include "mozilla/dom/PopupBlockedEvent.h"
 #include "mozilla/dom/PrimitiveConversions.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
+#include "mozilla/dom/ServiceWorkerRegistration.h"
 #ifdef HAVE_SIDEBAR
 #include "mozilla/dom/ExternalBinding.h"
 #endif
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesis.h"
 #endif
 
@@ -1571,16 +1572,18 @@ nsGlobalWindow::CleanUp()
   mAudioContexts.Clear();
 
   if (mIdleTimer) {
     mIdleTimer->Cancel();
     mIdleTimer = nullptr;
   }
 
   DisableTimeChangeNotifications();
+
+  mServiceWorkerRegistrationTable.Clear();
 }
 
 void
 nsGlobalWindow::ClearControllers()
 {
   if (mControllers) {
     uint32_t count;
     mControllers->GetControllerCount(&count);
@@ -1783,16 +1786,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
 
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerRegistrationTable)
+
 #ifdef MOZ_WEBSPEECH
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
 #endif
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
 
@@ -1854,16 +1859,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
 
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mServiceWorkerRegistrationTable)
+
 #ifdef MOZ_WEBSPEECH
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
 #endif
 
   if (tmp->mOuterWindow) {
     static_cast<nsGlobalWindow*>(tmp->mOuterWindow.get())->MaybeClearInnerWindow(tmp);
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
   }
@@ -10227,16 +10234,34 @@ nsGlobalWindow::GetCaches(ErrorResult& a
                                                      storageBlocked,
                                                      forceTrustedOrigin, aRv);
   }
 
   RefPtr<CacheStorage> ref = mCacheStorage;
   return ref.forget();
 }
 
+already_AddRefed<ServiceWorkerRegistrationMainThread>
+nsPIDOMWindow::GetServiceWorkerRegistration(const nsAString& aScope)
+{
+  RefPtr<ServiceWorkerRegistrationMainThread> registration;
+  if (!mServiceWorkerRegistrationTable.Get(aScope,
+                                           getter_AddRefs(registration))) {
+    registration = new ServiceWorkerRegistrationMainThread(this, aScope);
+    mServiceWorkerRegistrationTable.Put(aScope, registration);
+  }
+  return registration.forget();
+}
+
+void
+nsPIDOMWindow::InvalidateServiceWorkerRegistration(const nsAString& aScope)
+{
+  mServiceWorkerRegistrationTable.Remove(aScope);
+}
+
 void
 nsGlobalWindow::FireOfflineStatusEventIfChanged()
 {
   if (!IsCurrentInnerWindow())
     return;
 
   bool isOffline = NS_IsOffline() || NS_IsAppOffline(GetPrincipal());
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -10,16 +10,17 @@
 
 #include "nsIDOMWindow.h"
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "mozilla/dom/EventTarget.h"
 #include "js/TypeDecls.h"
+#include "nsRefPtrHashtable.h"
 
 #define DOM_WINDOW_DESTROYED_TOPIC "dom-window-destroyed"
 #define DOM_WINDOW_FROZEN_TOPIC "dom-window-frozen"
 #define DOM_WINDOW_THAWED_TOPIC "dom-window-thawed"
 
 class nsIArray;
 class nsIContent;
 class nsICSSDeclaration;
@@ -32,16 +33,17 @@ class nsPerformance;
 class nsPIWindowRoot;
 class nsXBLPrototypeHandler;
 struct nsTimeout;
 
 namespace mozilla {
 namespace dom {
 class AudioContext;
 class Element;
+class ServiceWorkerRegistrationMainThread;
 } // namespace dom
 namespace gfx {
 class VRHMDInfo;
 } // namespace gfx
 } // namespace mozilla
 
 // Popup control state enum. The values in this enum must go from most
 // permissive to least permissive so that it's safe to push state in
@@ -213,16 +215,20 @@ public:
   }
 
   bool GetServiceWorkersTestingEnabled()
   {
     MOZ_ASSERT(IsOuterWindow());
     return mServiceWorkersTestingEnabled;
   }
 
+  already_AddRefed<mozilla::dom::ServiceWorkerRegistrationMainThread>
+    GetServiceWorkerRegistration(const nsAString& aScope);
+  void InvalidateServiceWorkerRegistration(const nsAString& aScope);
+
 protected:
   // Lazily instantiate an about:blank document if necessary, and if
   // we have what it takes to do so.
   void MaybeCreateDoc();
 
   float GetAudioGlobalVolumeInternal(float aVolume);
   void RefreshMediaElements();
 
@@ -850,16 +856,21 @@ protected:
   // This reference is used by the subclass nsGlobalWindow, and cleared in it's
   // DetachFromDocShell() method. This method is called by nsDocShell::Destroy(),
   // which is called before the nsDocShell is destroyed.
   nsIDocShell* MOZ_NON_OWNING_REF mDocShell;  // Weak Reference
 
   // mPerformance is only used on inner windows.
   RefPtr<nsPerformance>       mPerformance;
 
+  typedef nsRefPtrHashtable<nsStringHashKey,
+                            mozilla::dom::ServiceWorkerRegistrationMainThread>
+          ServiceWorkerRegistrationTable;
+  ServiceWorkerRegistrationTable mServiceWorkerRegistrationTable;
+
   uint32_t               mModalStateDepth;
 
   // These variables are only used on inner windows.
   nsTimeout             *mRunningTimeout;
 
   uint32_t               mMutationBits;
 
   bool                   mIsDocumentLoaded;
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -675,18 +675,17 @@ public:
     : mWindow(aWindow)
     , mPromise(aPromise)
   {}
 
   void
   UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override
   {
     RefPtr<ServiceWorkerRegistrationMainThread> swr =
-      new ServiceWorkerRegistrationMainThread(mWindow,
-                                              NS_ConvertUTF8toUTF16(aInfo->mScope));
+      mWindow->GetServiceWorkerRegistration(NS_ConvertUTF8toUTF16(aInfo->mScope));
     mPromise->MaybeResolve(swr);
   }
 
   void
   UpdateFailed(ErrorResult& aStatus) override
   {
     mPromise->MaybeReject(aStatus);
   }
@@ -1855,17 +1854,17 @@ public:
 
       rv = principal->CheckMayLoad(scopeURI, true /* report */,
                                    false /* allowIfInheritsPrincipal */);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         continue;
       }
 
       RefPtr<ServiceWorkerRegistrationMainThread> swr =
-        new ServiceWorkerRegistrationMainThread(mWindow, scope);
+        mWindow->GetServiceWorkerRegistration(scope);
 
       array.AppendElement(swr);
     }
 
     mPromise->MaybeResolve(array);
     return NS_OK;
   }
 };
@@ -1962,17 +1961,17 @@ public:
 
     if (!registration) {
       mPromise->MaybeResolve(JS::UndefinedHandleValue);
       return NS_OK;
     }
 
     NS_ConvertUTF8toUTF16 scope(registration->mScope);
     RefPtr<ServiceWorkerRegistrationMainThread> swr =
-      new ServiceWorkerRegistrationMainThread(mWindow, scope);
+      mWindow->GetServiceWorkerRegistration(scope);
     mPromise->MaybeResolve(swr);
 
     return NS_OK;
   }
 };
 
 // If we return an error code here, the ServiceWorkerContainer will
 // automatically reject the Promise.
@@ -2224,17 +2223,17 @@ ServiceWorkerManager::CheckReadyPromise(
   MOZ_ASSERT(principal);
 
   RefPtr<ServiceWorkerRegistrationInfo> registration =
     GetServiceWorkerRegistrationInfo(principal, aURI);
 
   if (registration && registration->mActiveWorker) {
     NS_ConvertUTF8toUTF16 scope(registration->mScope);
     RefPtr<ServiceWorkerRegistrationMainThread> swr =
-      new ServiceWorkerRegistrationMainThread(aWindow, scope);
+      aWindow->GetServiceWorkerRegistration(scope);
     aPromise->MaybeResolve(swr);
     return true;
   }
 
   return false;
 }
 
 ServiceWorkerInfo*
@@ -3026,16 +3025,17 @@ ServiceWorkerManager::RemoveScopeAndRegi
   RefPtr<ServiceWorkerRegistrationInfo> info;
   data->mInfos.Get(aRegistration->mScope, getter_AddRefs(info));
 
   data->mInfos.Remove(aRegistration->mScope);
   data->mOrderedScopes.RemoveElement(aRegistration->mScope);
   swm->NotifyListenersOnUnregister(info);
 
   swm->MaybeRemoveRegistrationInfo(scopeKey);
+  swm->NotifyServiceWorkerRegistrationRemoved(aRegistration);
 }
 
 void
 ServiceWorkerManager::MaybeRemoveRegistrationInfo(const nsACString& aScopeKey)
 {
   RegistrationDataPerPrincipal* data;
   if (!mRegistrationInfos.Get(aScopeKey, &data)) {
     return;
@@ -3538,16 +3538,35 @@ ServiceWorkerManager::InvalidateServiceW
 
     if (utf8Scope.Equals(aRegistration->mScope)) {
       target->InvalidateWorkers(aWhichOnes);
     }
   }
 }
 
 void
+ServiceWorkerManager::NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration)
+{
+  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->RegistrationRemoved();
+    }
+  }
+}
+
+void
 ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes,
                                  const nsACString& aScope)
 {
   AssertIsOnMainThread();
   nsAutoCString scopeKey;
   aOriginAttributes.CreateSuffix(scopeKey);
 
   RefPtr<ServiceWorkerRegistrationInfo> registration =
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -513,16 +513,19 @@ private:
   ServiceWorkerInfo*
   GetActiveWorkerInfoForDocument(nsIDocument* aDocument);
 
   void
   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                             WhichServiceWorker aWhichOnes);
 
   void
+  NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
+
+  void
   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
                             nsIDocument* aDoc);
 
   void
   StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration);
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
   GetServiceWorkerRegistrationInfo(nsPIDOMWindow* aWindow);
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -245,16 +245,29 @@ ServiceWorkerRegistrationMainThread::Inv
 
   if (aWhichOnes & WhichServiceWorker::WAITING_WORKER) {
     mWaitingWorker = nullptr;
   }
 
   if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) {
     mActiveWorker = nullptr;
   }
+
+}
+
+void
+ServiceWorkerRegistrationMainThread::RegistrationRemoved()
+{
+  // If the registration is being removed completely, remove it from the
+  // window registration hash table so that a new registration would get a new
+  // wrapper JS object.
+  nsCOMPtr<nsPIDOMWindow> window = GetOwner();
+  if (window) {
+    window->InvalidateServiceWorkerRegistration(mScope);
+  }
 }
 
 namespace {
 
 void
 UpdateInternal(nsIPrincipal* aPrincipal,
                const nsAString& aScope,
                ServiceWorkerUpdateFinishCallback* aCallback)
@@ -860,16 +873,22 @@ public:
   void
   InvalidateWorkers(WhichServiceWorker aWhichOnes) override
   {
     AssertIsOnMainThread();
     // FIXME(nsm);
   }
 
   void
+  RegistrationRemoved() override
+  {
+    AssertIsOnMainThread();
+  }
+
+  void
   GetScope(nsAString& aScope) const override
   {
     aScope = mScope;
   }
 
   ServiceWorkerRegistrationWorkerThread*
   GetRegistration() const
   {
--- a/dom/workers/ServiceWorkerRegistration.h
+++ b/dom/workers/ServiceWorkerRegistration.h
@@ -61,16 +61,19 @@ public:
 
   virtual void
   UpdateFound() = 0;
 
   virtual void
   InvalidateWorkers(WhichServiceWorker aWhichOnes) = 0;
 
   virtual void
+  RegistrationRemoved() = 0;
+
+  virtual void
   GetScope(nsAString& aScope) const = 0;
 };
 
 class ServiceWorkerRegistrationBase : public DOMEventTargetHelper
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
@@ -101,19 +104,16 @@ protected:
 class ServiceWorkerRegistrationMainThread final : public ServiceWorkerRegistrationBase,
                                                   public ServiceWorkerRegistrationListener
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationMainThread,
                                            ServiceWorkerRegistrationBase)
 
-  ServiceWorkerRegistrationMainThread(nsPIDOMWindow* aWindow,
-                                      const nsAString& aScope);
-
   already_AddRefed<Promise>
   Update(ErrorResult& aRv);
 
   already_AddRefed<Promise>
   Unregister(ErrorResult& aRv);
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -150,22 +150,28 @@ public:
   // ServiceWorkerRegistrationListener
   void
   UpdateFound() override;
 
   void
   InvalidateWorkers(WhichServiceWorker aWhichOnes) override;
 
   void
+  RegistrationRemoved() override;
+
+  void
   GetScope(nsAString& aScope) const override
   {
     aScope = mScope;
   }
 
 private:
+  friend nsPIDOMWindow;
+  ServiceWorkerRegistrationMainThread(nsPIDOMWindow* aWindow,
+                                      const nsAString& aScope);
   ~ServiceWorkerRegistrationMainThread();
 
   already_AddRefed<workers::ServiceWorker>
   GetWorkerReference(WhichServiceWorker aWhichOne);
 
   void
   StartListeningForEvents();
 
--- a/dom/workers/test/serviceworkers/test_install_event.html
+++ b/dom/workers/test/serviceworkers/test_install_event.html
@@ -19,17 +19,17 @@
     var p = navigator.serviceWorker.register("worker.js", { scope: "./install_event" });
     return p;
   }
 
   function nextRegister(reg) {
     ok(reg instanceof ServiceWorkerRegistration, "reg should be a ServiceWorkerRegistration");
     var p = navigator.serviceWorker.register("install_event_worker.js", { scope: "./install_event" });
     return p.then(function(swr) {
-      ok(reg.scope === swr.scope, "Scope for registrations should match.");
+      ok(reg === swr, "register should resolve to the same registration object");
       var update_found_promise = new Promise(function(resolve, reject) {
         swr.addEventListener('updatefound', function(e) {
           ok(true, "Received onupdatefound");
           resolve();
         });
       });
 
       var worker_activating = new Promise(function(res, reject) {
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/getregistration.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/getregistration.https.html
@@ -19,36 +19,38 @@ async_test(function(t) {
     var registration;
     service_worker_unregister_and_register(t, 'resources/empty-worker.js',
                                            scope)
       .then(function(r) {
           registration = r;
           return navigator.serviceWorker.getRegistration(scope);
         })
       .then(function(value) {
-          assert_equals(value.scope, registration.scope,
-                        'getRegistration should resolve with registration');
+          assert_equals(
+              value, registration,
+              'getRegistration should resolve to the same registration object');
           service_worker_unregister_and_done(t, scope);
         })
       .catch(unreached_rejection(t));
   }, 'Register then getRegistration');
 
 async_test(function(t) {
     var scope = 'resources/scope/getregistration/url-with-fragment';
     var documentURL = scope + '#ref';
     var registration;
     service_worker_unregister_and_register(t, 'resources/empty-worker.js',
                                            scope)
       .then(function(r) {
           registration = r;
           return navigator.serviceWorker.getRegistration(documentURL);
         })
       .then(function(value) {
-          assert_equals(value.scope, registration.scope,
-                        'getRegistration should resolve with registration');
+          assert_equals(
+              value, registration,
+              'getRegistration should resolve to the same registration object');
           service_worker_unregister_and_done(t, scope);
         })
       .catch(unreached_rejection(t));
   }, 'Register then getRegistration with a URL having a fragment');
 
 async_test(function(t) {
     var documentURL = 'http://example.com/';
     navigator.serviceWorker.getRegistration(documentURL)
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/getregistrations.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/getregistrations.https.html
@@ -31,45 +31,45 @@ promise_test(function(t) {
   }, 'getRegistrations');
 
 promise_test(function(t) {
     var scope = 'resources/scope/getregistrations/normal';
     var script = 'resources/empty-worker.js';
     var registrations = [];
     return service_worker_unregister_and_register(t, script, scope)
       .then(function(r) {
-          registrations.push(r.scope);
+          registrations.push(r);
           return navigator.serviceWorker.getRegistrations();
         })
       .then(function(value) {
           assert_array_equals(
-            value.map((r) => r.scope),
+            value,
             registrations,
             'getRegistrations should resolve with array of registrations.');
           return service_worker_unregister(t, scope);
         });
   }, 'Register then getRegistrations');
 
 promise_test(function(t) {
     var scope1 = 'resources/scope/getregistrations/scope1';
     var scope2 = 'resources/scope/getregistrations/scope2';
     var script = 'resources/empty-worker.js';
     var registrations = [];
     return service_worker_unregister_and_register(t, script, scope1)
       .then(function(r) {
-          registrations.push(r.scope);
+          registrations.push(r);
           return service_worker_unregister_and_register(t, script, scope2);
         })
       .then(function(r) {
-          registrations.push(r.scope);
+          registrations.push(r);
           return navigator.serviceWorker.getRegistrations();
         })
       .then(function(value) {
           assert_array_equals(
-            value.map((r) => r.scope),
+            value,
             registrations,
             'getRegistrations should resolve with array of registrations.');
           return service_worker_unregister(t, scope1);
         })
       .then(function() {
           return service_worker_unregister(t, scope2);
         });
   }, 'Register multiple times then getRegistrations');
@@ -124,22 +124,22 @@ promise_test(function(t) {
     // test. So we have to wait until the cross origin register() is done, and not
     // just until the frame loads.
     return with_iframe_ready(frame_url)
       .then(function(f) {
           frame = f;
           return service_worker_unregister_and_register(t, script, scope);
         })
       .then(function(r) {
-          registrations.push(r.scope);
+          registrations.push(r);
           return navigator.serviceWorker.getRegistrations();
         })
       .then(function(value) {
           assert_array_equals(
-            value.map((r) => r.scope),
+            value,
             registrations,
             'getRegistrations should only return same origin registrations.');
 
           var channel = new MessageChannel();
           var resolve;
           var p = new Promise(function(r) { resolve = r; });
 
           channel.port1.onmessage = function(e) {
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/multiple-register.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/multiple-register.https.html
@@ -14,24 +14,20 @@ async_test(function(t) {
     .then(function(r) {
         registration = r;
         return wait_for_state(t, r.installing, 'activated');
       })
     .then(function() {
         return navigator.serviceWorker.register(worker_url, { scope: scope });
       })
     .then(function(new_registration) {
-        // FIXME: Bug 1201127 will fix scope vs object comparisons.
-        assert_not_equals(
-          registration, new_registration,
-          'register should resolve to the different registration');
-        assert_equals(new_registration.scope, registration.scope,
-                      'registrations should have the same scope');
-        assert_equals(new_registration.active.scriptURL, registration.active.scriptURL,
-                      'active workers should have same scriptURL');
+        assert_equals(new_registration, registration,
+                      'register should resolve to the same registration');
+        assert_equals(new_registration.active, registration.active,
+                      'register should resolve to the same worker');
         assert_equals(new_registration.active.state, 'activated',
                       'the worker should be in state "activated"');
         return registration.unregister();
       })
     .then(function() { t.done(); })
     .catch(unreached_rejection(t));
 }, 'Subsequent registrations resolve to the same registration object');
 
@@ -47,20 +43,19 @@ async_test(function(t) {
       })
     .then(function() { return with_iframe('resources/404.py'); })
     .then(function(f) {
         frame = f;
         return frame.contentWindow.navigator.serviceWorker.register(
             worker_url, { scope: scope });
       })
     .then(function(new_registration) {
-        // FIXME: Bug 1201127 will fix scope vs object comparisons.
         assert_not_equals(
           registration, new_registration,
-          'register should resolve to the different registration');
+          'register should resolve to a different registration');
         assert_equals(
           registration.scope, new_registration.scope,
           'registrations should have the same scope');
 
         assert_equals(
           registration.installing, null,
           'installing worker should be null');
         assert_equals(
@@ -103,19 +98,18 @@ async_test(function(t) {
         for (var i = 0; i < 10; ++i) {
           promises.push(navigator.serviceWorker.register(worker_url,
                                                          { scope: scope }));
         }
         return Promise.all(promises);
       })
     .then(function(registrations) {
         registrations.forEach(function(registration) {
-            // FIXME: Bug 1201127 will fix scope vs object comparisons.
-            assert_equals(registration.scope, registrations[0].scope,
-                          'register should resolve to registrations with the same scope');
+            assert_equals(registration, registrations[0],
+                          'register should resolve to the same registration');
           });
         return registrations[0].unregister();
       })
     .then(function() { t.done(); })
     .catch(unreached_rejection(t));
 }, 'Concurrent registrations resolve to the same registration object');
 
 </script>
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/unregister-then-register.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/unregister-then-register.https.html
@@ -43,18 +43,18 @@ async_test(function(t) {
         })
       .then(function(frame) {
           return registration.unregister();
         })
       .then(function() {
           return navigator.serviceWorker.register(worker_url, { scope: scope });
         })
       .then(function(new_registration) {
-          assert_equals(registration.scope, new_registration.scope,
-                        'new registration should resolve to the same scope');
+          assert_equals(registration, new_registration,
+                        'new registration should resolve to the same registration');
           service_worker_unregister_and_done(t, scope);
         })
       .catch(unreached_rejection(t));
   }, 'Unregister then register resolves to the original value if the ' +
          'registration is in use.');
 
 async_test(function(t) {
     var scope = 'resources/scope/re-register-does-not-affect-existing-controllee';