Backed out 3 changesets (bug 1191495, bug 1153499, bug 1144660) for mochitest-push crashes
authorWes Kocher <wkocher@mozilla.com>
Wed, 05 Aug 2015 15:18:05 -0700
changeset 288119 15512b2f6f41e1b749a0097e3360d0ee7f9f7cfe
parent 288118 65767c78adb12f5ff35f7e56b2fe573aba86d85f
child 288120 d98d82f9981b2602c94e2b1e40e4916931f67ae0
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1191495, 1153499, 1144660
milestone42.0a1
backs out8917e73233991b25abf4bd9053cc3742eda85b4f
13ed6f60d3f9569b56d3d18584573d4481c500b9
95bd6642e4b1c4facd070773d91d00317e74deea
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
Backed out 3 changesets (bug 1191495, bug 1153499, bug 1144660) for mochitest-push crashes Backed out changeset 8917e7323399 (bug 1191495) Backed out changeset 13ed6f60d3f9 (bug 1144660) Backed out changeset 95bd6642e4b1 (bug 1153499)
browser/app/profile/firefox.js
browser/base/content/content.js
browser/base/content/tabbrowser.xml
dom/push/PushManager.cpp
dom/tests/mochitest/general/test_interfaces.html
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerWindowClient.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/WorkerScope.cpp
dom/workers/WorkerScope.h
dom/workers/test/serviceworkers/client_focus_worker.js
dom/workers/test/serviceworkers/mochitest.ini
dom/workers/test/serviceworkers/notificationclick_focus.html
dom/workers/test/serviceworkers/notificationclick_focus.js
dom/workers/test/serviceworkers/periodic_update_test.js
dom/workers/test/serviceworkers/sw_clients/focus_stealing_client.html
dom/workers/test/serviceworkers/test_app_installation.html
dom/workers/test/serviceworkers/test_client_focus.html
dom/workers/test/serviceworkers/test_eval_allowed.html
dom/workers/test/serviceworkers/test_eval_not_allowed.html
dom/workers/test/serviceworkers/test_fetch_event_client_postmessage.html
dom/workers/test/serviceworkers/test_gzip_redirect.html
dom/workers/test/serviceworkers/test_https_origin_after_redirect.html
dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html
dom/workers/test/serviceworkers/test_notificationclick.html
dom/workers/test/serviceworkers/test_notificationclick_focus.html
dom/workers/test/serviceworkers/test_opaque_intercept.html
dom/workers/test/serviceworkers/test_origin_after_redirect.html
dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
dom/workers/test/serviceworkers/test_third_party_iframes.html
dom/workers/test/test_worker_interfaces.js
modules/libpref/init/all.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1937,14 +1937,13 @@ pref("browser.pocket.enabled", true);
 pref("browser.pocket.api", "api.getpocket.com");
 pref("browser.pocket.site", "getpocket.com");
 pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
 pref("browser.pocket.useLocaleList", true);
 pref("browser.pocket.enabledLocales", "cs de en-GB en-US en-ZA es-ES es-MX fr hu it ja ja-JP-mac ko nl pl pt-BR pt-PT ru zh-CN zh-TW");
 
 pref("view_source.tab", true);
 
-// Enable ServiceWorkers for Push API consumers.
-// Interception is still disabled.
+// Enable Service Workers for desktop on non-release builds
+#ifndef RELEASE_BUILD
 pref("dom.serviceWorkers.enabled", true);
-
-// Enable Push API.
-pref("dom.push.enabled", true);
+pref("dom.serviceWorkers.interception.enabled", true);
+#endif
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -515,20 +515,16 @@ ContentLinkHandler.init(this);
 
 // TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
 let pluginContent = new PluginContent(global);
 
 addEventListener("DOMWebNotificationClicked", function(event) {
   sendAsyncMessage("DOMWebNotificationClicked", {});
 }, false);
 
-addEventListener("DOMServiceWorkerFocusClient", function(event) {
-  sendAsyncMessage("DOMServiceWorkerFocusClient", {});
-}, false);
-
 ContentWebRTC.init();
 addMessageListener("webrtc:Allow", ContentWebRTC);
 addMessageListener("webrtc:Deny", ContentWebRTC);
 addMessageListener("webrtc:StopSharing", ContentWebRTC);
 addMessageListener("webrtc:StartBrowserSharing", () => {
   let windowID = content.QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
   sendAsyncMessage("webrtc:response:StartBrowserSharing", {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3978,17 +3978,16 @@
                                           selectionInfo: aMessage.data.selectionInfo,
                                           disableSetDesktopBackground: aMessage.data.disableSetDesktopBg,
                                         };
               let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
               let event = gContextMenuContentData.event;
               popup.openPopupAtScreen(event.screenX, event.screenY, true);
               break;
             }
-            case "DOMServiceWorkerFocusClient":
             case "DOMWebNotificationClicked": {
               let tab = this.getTabForBrowser(browser);
               if (!tab)
                 return;
               this.selectedTab = tab;
               window.focus();
               break;
             }
@@ -4075,17 +4074,16 @@
             // If this window has remote tabs, switch to our tabpanels fork
             // which does asynchronous tab switching.
             this.mPanelContainer.classList.add("tabbrowser-tabpanels");
           } else {
             this._outerWindowIDBrowserMap.set(this.mCurrentBrowser.outerWindowID,
                                               this.mCurrentBrowser);
           }
           messageManager.addMessageListener("DOMWebNotificationClicked", this);
-          messageManager.addMessageListener("DOMServiceWorkerFocusClient", this);
           messageManager.addMessageListener("Findbar:Keypress", this);
         ]]>
       </constructor>
 
       <method name="_generateUniquePanelID">
         <body><![CDATA[
           if (!this._uniquePanelIDCounter) {
             this._uniquePanelIDCounter = 0;
--- a/dom/push/PushManager.cpp
+++ b/dom/push/PushManager.cpp
@@ -281,24 +281,26 @@ private:
 
 class WorkerUnsubscribeResultCallback final : public nsIUnsubscribeResultCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
   explicit WorkerUnsubscribeResultCallback(PromiseWorkerProxy* aProxy)
     : mProxy(aProxy)
+    , mCallbackCalled(false)
   {
     AssertIsOnMainThread();
   }
 
   NS_IMETHOD
   OnUnsubscribe(nsresult aStatus, bool aSuccess) override
   {
     AssertIsOnMainThread();
+    mCallbackCalled = true;
     if (!mProxy) {
       return NS_OK;
     }
 
     MutexAutoLock lock(mProxy->GetCleanUpLock());
     if (mProxy->IsClean()) {
       return NS_OK;
     }
@@ -312,19 +314,24 @@ public:
       ReleasePromiseWorkerProxy(mProxy.forget());
     }
 
     return NS_OK;
   }
 
 private:
   ~WorkerUnsubscribeResultCallback()
-  {}
+  {
+    // Enforces that UnsubscribeRunnable uses the callback for error
+    // reporting once it creates the callback.
+    MOZ_ASSERT(mCallbackCalled);
+  }
 
   nsRefPtr<PromiseWorkerProxy> mProxy;
+  DebugOnly<bool> mCallbackCalled;
 };
 
 NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback)
 
 class UnsubscribeRunnable final : public nsRunnable
 {
 public:
   UnsubscribeRunnable(PromiseWorkerProxy* aProxy,
@@ -465,22 +472,24 @@ class GetSubscriptionCallback final : pu
 {
 public:
   NS_DECL_ISUPPORTS
 
   explicit GetSubscriptionCallback(PromiseWorkerProxy* aProxy,
                                    const nsAString& aScope)
     : mProxy(aProxy)
     , mScope(aScope)
+    , mCallbackCalled(false)
   {}
 
   NS_IMETHOD
   OnPushEndpoint(nsresult aStatus, const nsAString& aEndpoint) override
   {
     AssertIsOnMainThread();
+    mCallbackCalled = true;
 
     if (!mProxy) {
       return NS_OK;
     }
 
     MutexAutoLock lock(mProxy->GetCleanUpLock());
     if (mProxy->IsClean()) {
       return NS_OK;
@@ -494,21 +503,26 @@ public:
     if (!r->Dispatch(jsapi.cx())) {
       ReleasePromiseWorkerProxy(mProxy.forget());
     }
     return NS_OK;
   }
 
 protected:
   ~GetSubscriptionCallback()
-  {}
+  {
+    // Enforces that GetSubscriptionRunnable uses the callback for error
+    // reporting once it creates the callback.
+    MOZ_ASSERT(mCallbackCalled);
+  }
 
 private:
   nsRefPtr<PromiseWorkerProxy> mProxy;
   nsString mScope;
+  DebugOnly<bool> mCallbackCalled;
 };
 
 NS_IMPL_ISUPPORTS(GetSubscriptionCallback, nsIPushEndpointCallback)
 
 class GetSubscriptionRunnable final : public nsRunnable
 {
 public:
   GetSubscriptionRunnable(PromiseWorkerProxy* aProxy,
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -927,19 +927,19 @@ var interfaceNamesInGlobalScope =
     "ProcessingInstruction",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ProgressEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Promise",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PropertyNodeList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PushManager", b2g: false, android: false},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "PushSubscription", b2g: false, android: false},
+    {name: "PushManager", b2g: false, android: false, release: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "PushSubscription", b2g: false, android: false, release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "RadioNodeList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Range",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "RecordErrorEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Rect",
@@ -965,21 +965,21 @@ var interfaceNamesInGlobalScope =
     "Screen",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ScriptProcessorNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ScrollAreaEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Selection",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "ServiceWorker", b2g: false, android: false},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "ServiceWorkerContainer", b2g: false, android: false},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "ServiceWorkerRegistration", b2g: false, android: false},
+    {name: "ServiceWorker", release: false, b2g: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "ServiceWorkerContainer", release: false, b2g: false},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "ServiceWorkerRegistration", release: false, b2g: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SettingsLock",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SettingsManager",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ShadowRoot", // Bogus, but the test harness forces it on.  See bug 1159768.
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SharedWorker",
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -91,26 +91,16 @@ static_assert(nsIHttpChannelInternal::CO
               "RequestMode enumeration value should match Necko CORS mode value.");
 static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(RequestMode::Cors),
               "RequestMode enumeration value should match Necko CORS mode value.");
 static_assert(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT == static_cast<uint32_t>(RequestMode::Cors_with_forced_preflight),
               "RequestMode enumeration value should match Necko CORS mode value.");
 
 static StaticRefPtr<ServiceWorkerManager> gInstance;
 
-// Tracks the "dom.disable_open_click_delay" preference.  Modified on main
-// thread, read on worker threads. This is set once in the ServiceWorkerManager
-// constructor before any service workers are spawned.
-// It is updated every time a "notificationclick" event is dispatched. While
-// this is done without synchronization, at the worst, the thread will just get
-// an older value within which a popup is allowed to be displayed, which will
-// still be a valid value since it was set in the constructor. I (:nsm) don't
-// think this needs to be synchronized.
-Atomic<uint32_t> gDOMDisableOpenClickDelay(0);
-
 struct ServiceWorkerManager::RegistrationDataPerPrincipal
 {
   // Ordered list of scopes for glob matching.
   // Each entry is an absolute URL representing the scope.
   // Each value of the hash table is an array of an absolute URLs representing
   // the scopes.
   //
   // An array is used for now since the number of controlled scopes per
@@ -391,18 +381,16 @@ NS_INTERFACE_MAP_BEGIN(ServiceWorkerMana
 NS_INTERFACE_MAP_END
 
 ServiceWorkerManager::ServiceWorkerManager()
   : mActor(nullptr)
   , mShuttingDown(false)
 {
   // Register this component to PBackground.
   MOZ_ALWAYS_TRUE(BackgroundChild::GetOrCreateForCurrentThread(this));
-
-  gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
 }
 
 ServiceWorkerManager::~ServiceWorkerManager()
 {
   // The map will assert if it is not empty when destroyed.
   mRegistrationInfos.Clear();
   MOZ_ASSERT(!mActor);
 }
@@ -1621,21 +1609,20 @@ ServiceWorkerManager::AppendPendingOpera
   if (!mShuttingDown) {
     PendingOperation* opt = mPendingOperations.AppendElement();
     opt->mRunnable = aRunnable;
   }
 }
 
 namespace {
 // Just holds a ref to a ServiceWorker until the Promise is fulfilled.
-class KeepAliveHandler : public PromiseNativeHandler
+class KeepAliveHandler final : public PromiseNativeHandler
 {
   nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
 
-protected:
   virtual ~KeepAliveHandler()
   {}
 
 public:
   NS_DECL_ISUPPORTS
 
   explicit KeepAliveHandler(const nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
     : mServiceWorker(aServiceWorker)
@@ -1659,160 +1646,16 @@ public:
     MOZ_ASSERT(workerPrivate);
     workerPrivate->AssertIsOnWorkerThread();
 #endif
   }
 };
 
 NS_IMPL_ISUPPORTS0(KeepAliveHandler)
 
-void
-DummyCallback(nsITimer* aTimer, void* aClosure)
-{
-  // Nothing.
-}
-
-class AllowWindowInteractionKeepAliveHandler;
-
-class ClearWindowAllowedRunnable final : public WorkerRunnable
-{
-public:
-  ClearWindowAllowedRunnable(WorkerPrivate* aWorkerPrivate,
-                             AllowWindowInteractionKeepAliveHandler* aHandler)
-  : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
-  , mHandler(aHandler)
-  { }
-
-private:
-  bool
-  PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    // WorkerRunnable asserts that the dispatch is from parent thread if
-    // the busy count modification is WorkerThreadUnchangedBusyCount.
-    // Since this runnable will be dispatched from the timer thread, we override
-    // PreDispatch and PostDispatch to skip the check.
-    return true;
-  }
-
-  void
-  PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
-               bool aDispatchResult) override
-  {
-    // Silence bad assertions.
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
-
-  nsRefPtr<AllowWindowInteractionKeepAliveHandler> mHandler;
-};
-
-class AllowWindowInteractionKeepAliveHandler final : public KeepAliveHandler
-{
-  friend class ClearWindowAllowedRunnable;
-  nsCOMPtr<nsITimer> mTimer;
-
-  ~AllowWindowInteractionKeepAliveHandler()
-  {
-    MOZ_ASSERT(!mTimer);
-  }
-
-  void
-  ClearWindowAllowed(WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-
-    if (mTimer) {
-      aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
-      mTimer->Cancel();
-      mTimer = nullptr;
-      MOZ_ALWAYS_TRUE(aWorkerPrivate->ModifyBusyCountFromWorker(aWorkerPrivate->GetJSContext(), false));
-    }
-  }
-
-  void
-  StartClearWindowTimer(WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-    MOZ_ASSERT(!mTimer);
-
-    nsresult rv;
-    nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-
-    nsRefPtr<ClearWindowAllowedRunnable> r =
-      new ClearWindowAllowedRunnable(aWorkerPrivate, this);
-
-    nsRefPtr<TimerThreadEventTarget> target =
-      new TimerThreadEventTarget(aWorkerPrivate, r);
-
-    rv = timer->SetTarget(target);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-
-    // The important stuff that *has* to be reversed.
-    if (NS_WARN_IF(!aWorkerPrivate->ModifyBusyCountFromWorker(aWorkerPrivate->GetJSContext(), true))) {
-      return;
-    }
-    aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
-    timer.swap(mTimer);
-
-    // We swap first and then initialize the timer so that even if initializing
-    // fails, we still clean the busy count and interaction count correctly.
-    // The timer can't be initialized before modifying the busy count since the
-    // timer thread could run and call the timeout but the worker may
-    // already be terminating and modifying the busy count could fail.
-    rv = mTimer->InitWithFuncCallback(DummyCallback, nullptr, gDOMDisableOpenClickDelay, nsITimer::TYPE_ONE_SHOT);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      ClearWindowAllowed(aWorkerPrivate);
-      return;
-    }
-  }
-
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-
-  AllowWindowInteractionKeepAliveHandler(const nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
-                                         WorkerPrivate* aWorkerPrivate)
-    : KeepAliveHandler(aServiceWorker)
-  {
-    StartClearWindowTimer(aWorkerPrivate);
-  }
-
-  void
-  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-    WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
-    ClearWindowAllowed(worker);
-    KeepAliveHandler::ResolvedCallback(aCx, aValue);
-  }
-
-  void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-    WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
-    ClearWindowAllowed(worker);
-    KeepAliveHandler::RejectedCallback(aCx, aValue);
-  }
-};
-
-NS_IMPL_ISUPPORTS_INHERITED0(AllowWindowInteractionKeepAliveHandler, KeepAliveHandler)
-
-bool
-ClearWindowAllowedRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-{
-  mHandler->ClearWindowAllowed(aWorkerPrivate);
-  return true;
-}
-
 // Returns a Promise if the event was successfully dispatched and no exceptions
 // were raised, otherwise returns null.
 already_AddRefed<Promise>
 DispatchExtendableEventOnWorkerScope(JSContext* aCx,
                                      WorkerGlobalScope* aWorkerScope,
                                      ExtendableEvent* aEvent)
 {
   MOZ_ASSERT(aWorkerScope);
@@ -2533,27 +2376,22 @@ public:
     nei.mCancelable = true;
 
     nsRefPtr<NotificationEvent> event =
       NotificationEvent::Constructor(target, NS_LITERAL_STRING("notificationclick"), nei, result);
     if (NS_WARN_IF(result.Failed())) {
       return false;
     }
 
-    aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
     event->SetTrusted(true);
     nsRefPtr<Promise> waitUntilPromise =
       DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event);
-    // If the handler calls WaitUntil(), that will manage its own interaction
-    // 'stack'.
-    aWorkerPrivate->GlobalScope()->ConsumeWindowInteraction();
 
     if (waitUntilPromise) {
-      nsRefPtr<AllowWindowInteractionKeepAliveHandler> handler =
-        new AllowWindowInteractionKeepAliveHandler(mServiceWorker, aWorkerPrivate);
+      nsRefPtr<KeepAliveHandler> handler = new KeepAliveHandler(mServiceWorker);
       waitUntilPromise->AppendNativeHandler(handler);
     }
 
     return true;
   }
 };
 
 NS_IMETHODIMP
@@ -2569,18 +2407,16 @@ ServiceWorkerManager::SendNotificationCl
                                                  const nsAString& aData,
                                                  const nsAString& aBehavior)
 {
   OriginAttributes attrs;
   if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  gDOMDisableOpenClickDelay = Preferences::GetInt("dom.disable_open_click_delay");
-
   nsRefPtr<ServiceWorker> serviceWorker =
     CreateServiceWorkerForScope(attrs, aScope, nullptr);
   if (!serviceWorker) {
     return NS_ERROR_FAILURE;
   }
   nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
     new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
 
--- a/dom/workers/ServiceWorkerWindowClient.cpp
+++ b/dom/workers/ServiceWorkerWindowClient.cpp
@@ -85,17 +85,19 @@ public:
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
     nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
     UniquePtr<ServiceWorkerClientInfo> clientInfo;
 
     if (window) {
-      nsContentUtils::DispatchChromeEvent(window->GetExtantDoc(), window->GetOuterWindow(), NS_LITERAL_STRING("DOMServiceWorkerFocusClient"), true, true);
+      mozilla::ErrorResult result;
+      //FIXME(catalinb): Bug 1144660 - check if we are allowed to focus here.
+      window->Focus(result);
       clientInfo.reset(new ServiceWorkerClientInfo(window->GetDocument(),
                                                    window->GetOuterWindow()));
     }
 
     DispatchResult(Move(clientInfo));
     return NS_OK;
   }
 
@@ -135,28 +137,24 @@ ServiceWorkerWindowClient::Focus(ErrorRe
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   MOZ_ASSERT(global);
 
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
-    nsRefPtr<PromiseWorkerProxy> promiseProxy =
-      PromiseWorkerProxy::Create(workerPrivate, promise);
-    if (!promiseProxy->GetWorkerPromise()) {
-      // Don't dispatch if adding the worker feature failed.
-      return promise.forget();
-    }
+  nsRefPtr<PromiseWorkerProxy> promiseProxy =
+    PromiseWorkerProxy::Create(workerPrivate, promise);
+  if (!promiseProxy->GetWorkerPromise()) {
+    // Don't dispatch if adding the worker feature failed.
+    return promise.forget();
+  }
 
-    nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
-                                                              promiseProxy);
-    aRv = NS_DispatchToMainThread(r);
-    if (NS_WARN_IF(aRv.Failed())) {
-      promise->MaybeReject(aRv);
-    }
-  } else {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+  nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
+                                                            promiseProxy);
+  aRv = NS_DispatchToMainThread(r);
+  if (NS_WARN_IF(aRv.Failed())) {
+    promise->MaybeReject(aRv);
   }
 
   return promise.forget();
 }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1861,16 +1861,78 @@ private:
 };
 
 void
 DummyCallback(nsITimer* aTimer, void* aClosure)
 {
   // Nothing!
 }
 
+class TimerThreadEventTarget final : public nsIEventTarget
+{
+  ~TimerThreadEventTarget() {}
+
+  WorkerPrivate* mWorkerPrivate;
+  nsRefPtr<WorkerRunnable> mWorkerRunnable;
+
+public:
+  TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate,
+                         WorkerRunnable* aWorkerRunnable)
+  : mWorkerPrivate(aWorkerPrivate), mWorkerRunnable(aWorkerRunnable)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    MOZ_ASSERT(aWorkerRunnable);
+  }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+protected:
+  NS_IMETHOD
+  DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override
+  {
+    nsCOMPtr<nsIRunnable> runnable(aRunnable);
+    return Dispatch(runnable.forget(), aFlags);
+  }
+
+  NS_IMETHOD
+  Dispatch(already_AddRefed<nsIRunnable>&& aRunnable, uint32_t aFlags) override
+  {
+    // This should only happen on the timer thread.
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(aFlags == nsIEventTarget::DISPATCH_NORMAL);
+
+    nsRefPtr<TimerThreadEventTarget> kungFuDeathGrip = this;
+
+    // Run the runnable we're given now (should just call DummyCallback()),
+    // otherwise the timer thread will leak it...  If we run this after
+    // dispatch running the event can race against resetting the timer.
+    nsCOMPtr<nsIRunnable> runnable(aRunnable);
+    runnable->Run();
+
+    // This can fail if we're racing to terminate or cancel, should be handled
+    // by the terminate or cancel code.
+    mWorkerRunnable->Dispatch(nullptr);
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  IsOnCurrentThread(bool* aIsOnCurrentThread) override
+  {
+    MOZ_ASSERT(aIsOnCurrentThread);
+
+    nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+};
+
 class KillCloseEventRunnable final : public WorkerRunnable
 {
   nsCOMPtr<nsITimer> mTimer;
 
   class KillScriptRunnable final : public WorkerControlRunnable
   {
   public:
     explicit KillScriptRunnable(WorkerPrivate* aWorkerPrivate)
@@ -2314,70 +2376,16 @@ PRThreadFromThread(nsIThread* aThread)
 }
 
 } /* anonymous namespace */
 
 NS_IMPL_ISUPPORTS_INHERITED0(MainThreadReleaseRunnable, nsRunnable)
 
 NS_IMPL_ISUPPORTS_INHERITED0(TopLevelWorkerFinishedRunnable, nsRunnable)
 
-TimerThreadEventTarget::TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate,
-                                               WorkerRunnable* aWorkerRunnable)
-  : mWorkerPrivate(aWorkerPrivate), mWorkerRunnable(aWorkerRunnable)
-{
-  MOZ_ASSERT(aWorkerPrivate);
-  MOZ_ASSERT(aWorkerRunnable);
-}
-
-TimerThreadEventTarget::~TimerThreadEventTarget()
-{
-}
-
-NS_IMETHODIMP
-TimerThreadEventTarget::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
-{
-  nsCOMPtr<nsIRunnable> runnable(aRunnable);
-  return Dispatch(runnable.forget(), aFlags);
-}
-
-NS_IMETHODIMP
-TimerThreadEventTarget::Dispatch(already_AddRefed<nsIRunnable>&& aRunnable, uint32_t aFlags)
-{
-  // This should only happen on the timer thread.
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aFlags == nsIEventTarget::DISPATCH_NORMAL);
-
-  nsRefPtr<TimerThreadEventTarget> kungFuDeathGrip = this;
-
-  // Run the runnable we're given now (should just call DummyCallback()),
-  // otherwise the timer thread will leak it...  If we run this after
-  // dispatch running the event can race against resetting the timer.
-  nsCOMPtr<nsIRunnable> runnable(aRunnable);
-  runnable->Run();
-
-  // This can fail if we're racing to terminate or cancel, should be handled
-  // by the terminate or cancel code.
-  mWorkerRunnable->Dispatch(nullptr);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-TimerThreadEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread)
-{
-  MOZ_ASSERT(aIsOnCurrentThread);
-
-  nsresult rv = mWorkerPrivate->IsOnCurrentThread(aIsOnCurrentThread);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
 NS_IMPL_ISUPPORTS(TimerThreadEventTarget, nsIEventTarget)
 
 WorkerLoadInfo::WorkerLoadInfo()
   : mWindowID(UINT64_MAX)
   , mServiceWorkerID(0)
   , mFromWindow(false)
   , mEvalAllowed(false)
   , mReportCSPViolations(false)
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1570,35 +1570,11 @@ public:
 
   nsIEventTarget*
   EventTarget() const
   {
     return mTarget;
   }
 };
 
-class TimerThreadEventTarget final : public nsIEventTarget
-{
-  ~TimerThreadEventTarget();
-
-  WorkerPrivate* mWorkerPrivate;
-  nsRefPtr<WorkerRunnable> mWorkerRunnable;
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  TimerThreadEventTarget(WorkerPrivate* aWorkerPrivate,
-                         WorkerRunnable* aWorkerRunnable);
-
-protected:
-  NS_IMETHOD
-  DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override;
-
-
-  NS_IMETHOD
-  Dispatch(already_AddRefed<nsIRunnable>&& aRunnable, uint32_t aFlags) override;
-
-  NS_IMETHOD
-  IsOnCurrentThread(bool* aIsOnCurrentThread) override;
-};
-
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_workerprivate_h__ */
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -54,18 +54,17 @@ USING_WORKERS_NAMESPACE
 
 using mozilla::dom::cache::CacheStorage;
 using mozilla::dom::indexedDB::IDBFactory;
 using mozilla::ipc::PrincipalInfo;
 
 BEGIN_WORKERS_NAMESPACE
 
 WorkerGlobalScope::WorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
-: mWindowInteractionsAllowed(0)
-, mWorkerPrivate(aWorkerPrivate)
+: mWorkerPrivate(aWorkerPrivate)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 }
 
 WorkerGlobalScope::~WorkerGlobalScope()
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 }
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -54,18 +54,16 @@ class WorkerGlobalScope : public DOMEven
 
   nsRefPtr<Console> mConsole;
   nsRefPtr<WorkerLocation> mLocation;
   nsRefPtr<WorkerNavigator> mNavigator;
   nsRefPtr<Performance> mPerformance;
   nsRefPtr<IDBFactory> mIndexedDB;
   nsRefPtr<cache::CacheStorage> mCacheStorage;
 
-  uint32_t mWindowInteractionsAllowed;
-
 protected:
   WorkerPrivate* mWorkerPrivate;
 
   explicit WorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
   virtual ~WorkerGlobalScope();
 
 public:
   virtual JSObject*
@@ -159,35 +157,16 @@ public:
 
   already_AddRefed<Promise>
   CreateImageBitmap(const ImageBitmapSource& aImage, ErrorResult& aRv);
 
   already_AddRefed<Promise>
   CreateImageBitmap(const ImageBitmapSource& aImage,
                     int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh,
                     ErrorResult& aRv);
-
-  bool
-  WindowInteractionAllowed() const
-  {
-    return mWindowInteractionsAllowed > 0;
-  }
-
-  void
-  AllowWindowInteraction()
-  {
-    mWindowInteractionsAllowed++;
-  }
-
-  void
-  ConsumeWindowInteraction()
-  {
-    MOZ_ASSERT(mWindowInteractionsAllowed > 0);
-    mWindowInteractionsAllowed--;
-  }
 };
 
 class DedicatedWorkerGlobalScope final : public WorkerGlobalScope
 {
   ~DedicatedWorkerGlobalScope() { }
 
 public:
   explicit DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/client_focus_worker.js
@@ -0,0 +1,15 @@
+onmessage = function(e) {
+  if (!e.source) {
+    dump("ERROR: message doesn't have a source.");
+  }
+
+  // The client should be a window client
+  if (e.source instanceof WindowClient) {
+    // this will dispatch a focus event on the client
+    e.source.focus().then(function(client) {
+      client.postMessage(client.focused);
+    });
+  } else {
+    dump("ERROR: client should be a WindowClient");
+  }
+};
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -12,16 +12,17 @@ support-files =
   simpleregister/index.html
   simpleregister/ready.html
   controller/index.html
   unregister/index.html
   unregister/unregister.html
   workerUpdate/update.html
   sw_clients/simple.html
   sw_clients/service_worker_controlled.html
+  sw_clients/focus_stealing_client.html
   match_all_worker.js
   match_all_advanced_worker.js
   worker_unregister.js
   worker_update.js
   message_posting_worker.js
   fetch/index.html
   fetch/fetch_worker_script.js
   fetch/fetch_tests.js
@@ -84,22 +85,21 @@ support-files =
   serviceworker_not_sharedworker.js
   match_all_client/match_all_client_id.html
   match_all_client_id_worker.js
   source_message_posting_worker.js
   scope/scope_worker.js
   redirect_serviceworker.sjs
   importscript.sjs
   importscript_worker.js
+  client_focus_worker.js
   bug1151916_worker.js
   bug1151916_driver.html
   notificationclick.html
   notificationclick.js
-  notificationclick_focus.html
-  notificationclick_focus.js
   worker_updatefoundevent.js
   worker_updatefoundevent2.js
   updatefoundevent.html
   empty.js
   periodic_update_test.js
   periodic.sjs
   periodic/frame.html
   periodic/register.html
@@ -158,16 +158,17 @@ support-files =
   sw_clients/dummy.html
 
 [test_app_protocol.html]
 skip-if = release_build
 [test_bug1151916.html]
 [test_claim.html]
 [test_claim_fetch.html]
 [test_claim_oninstall.html]
+[test_client_focus.html]
 [test_close.html]
 [test_controller.html]
 [test_cross_origin_url_after_redirect.html]
 [test_empty_serviceworker.html]
 [test_eval_allowed.html]
 [test_eval_not_allowed.html]
 [test_fetch_event.html]
 [test_force_refresh.html]
@@ -221,18 +222,16 @@ skip-if = release_build
 [test_request_context_track.html]
 [test_request_context_video.html]
 [test_request_context_worker.html]
 [test_request_context_xhr.html]
 [test_request_context_xslt.html]
 [test_scopes.html]
 [test_sandbox_intercept.html]
 [test_notificationclick.html]
-[test_notificationclick_focus.html]
-skip-if = toolkit == "android" || toolkit == "gonk"
 [test_notification_constructor_error.html]
 [test_notification_get.html]
 [test_sanitize.html]
 [test_sanitize_domain.html]
 [test_service_worker_allowed.html]
 [test_serviceworker_interfaces.html]
 [test_serviceworker_not_sharedworker.html]
 [test_skip_waiting.html]
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/notificationclick_focus.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Bug 1144660 - controlled page</title>
-<script class="testbody" type="text/javascript">
-  var testWindow = parent;
-  if (opener) {
-    testWindow = opener;
-  }
-
-  navigator.serviceWorker.ready.then(function(swr) {
-    swr.showNotification("Hi there. The ServiceWorker should receive a click event for this.");
-  });
-
-  navigator.serviceWorker.onmessage = function(msg) {
-    dump("GOT Message " + JSON.stringify(msg.data) + "\n");
-    testWindow.callback(msg.data.ok);
-  };
-</script>
-
-</head>
-<body>
-</body>
-</html>
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/notificationclick_focus.js
+++ /dev/null
@@ -1,40 +0,0 @@
-// Any copyright is dedicated to the Public Domain.
-// http://creativecommons.org/publicdomain/zero/1.0/
-//
-
-function promisifyTimerFocus(client, delay) {
-  return new Promise(function(resolve, reject) {
-    setTimeout(function() {
-      client.focus().then(resolve, reject);
-    }, delay);
-  });
-}
-
-onnotificationclick = function(e) {
-  e.waitUntil(self.clients.matchAll().then(function(clients) {
-    if (clients.length === 0) {
-      dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n");
-      return Promise.resolve();
-    }
-
-    var immediatePromise = clients[0].focus();
-    var withinTimeout = promisifyTimerFocus(clients[0], 100);
-
-    var afterTimeout = promisifyTimerFocus(clients[0], 2000).then(function() {
-      throw "Should have failed!";
-    }, function() {
-      return Promise.resolve();
-    });
-
-    return Promise.all([immediatePromise, withinTimeout, afterTimeout]).then(function() {
-      clients.forEach(function(client) {
-        client.postMessage({ok: true});
-      });
-    }).catch(function(e) {
-      dump("Error " + e + "\n");
-      clients.forEach(function(client) {
-        client.postMessage({ok: false});
-      });
-    });
-  }));
-}
--- a/dom/workers/test/serviceworkers/periodic_update_test.js
+++ b/dom/workers/test/serviceworkers/periodic_update_test.js
@@ -63,13 +63,12 @@ function unregisterSW() {
 function runTheTest() {
   SimpleTest.waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
   ]}, function() {
     start();
   });
 }
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/focus_stealing_client.html
@@ -0,0 +1,33 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1130686 - Test service worker client.focus: client </title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <!--
+    We load this as an iframe to blur the main test window.
+  -->
+</head>
+<body>/
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+  if (!parent) {
+    info("error: sw_clients/focus_stealing_client.html shouldn't be launched directly!");
+  }
+
+  window.onload = function() {
+    navigator.serviceWorker.ready.then(function() {
+      parent.postMessage("READY", "*");
+    });
+  }
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/workers/test/serviceworkers/test_app_installation.html
+++ b/dom/workers/test/serviceworkers/test_app_installation.html
@@ -42,18 +42,17 @@ addLoadEvent(go);
 function setup() {
   info('Setting up');
   return new Promise((resolve, reject) => {
     SpecialPowers.setAllAppsLaunchable(true);
     SpecialPowers.pushPrefEnv({'set': [
       ['dom.mozBrowserFramesEnabled', true],
       ['dom.serviceWorkers.exemptFromPerDomainMax', true],
       ['dom.serviceWorkers.enabled', true],
-      ['dom.serviceWorkers.testing.enabled', true],
-      ['dom.serviceWorkers.interception.enabled', true],
+      ['dom.serviceWorkers.testing.enabled', true]
     ]}, () => {
       SpecialPowers.pushPermissions([
         { 'type': 'webapps-manage', 'allow': 1, 'context': document },
         { 'type': 'browser', 'allow': 1, 'context': document },
         { 'type': 'embed-apps', 'allow': 1, 'context': document }
       ], () => {
         SpecialPowers.autoConfirmAppInstall(() => {
           SpecialPowers.autoConfirmAppUninstall(resolve);
--- a/dom/workers/test/serviceworkers/test_client_focus.html
+++ b/dom/workers/test/serviceworkers/test_client_focus.html
@@ -4,18 +4,18 @@
 -->
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Bug 1130686 - Test service worker client.focus </title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 <!--
-  This test checks that client.focus is available.
-  Actual focusing is tested by test_notificationclick_focus.html since only notification events have permission to change focus.
+  This test checks that client.focus is able to restore focus to the main window
+  when an iframe is holding the focus.
 -->
 </head>
 <body>
 <p id="display"></p>
 <div id="content"></div>
 <pre id="test"></pre>
 <script class="testbody" type="text/javascript">
   var registration;
--- a/dom/workers/test/serviceworkers/test_eval_allowed.html
+++ b/dom/workers/test/serviceworkers/test_eval_allowed.html
@@ -29,15 +29,14 @@
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
+    ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_eval_not_allowed.html
+++ b/dom/workers/test/serviceworkers/test_eval_not_allowed.html
@@ -32,15 +32,14 @@
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
+    ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_fetch_event_client_postmessage.html
+++ b/dom/workers/test/serviceworkers/test_fetch_event_client_postmessage.html
@@ -60,14 +60,12 @@
         }).then(SimpleTest.finish);
     }
 
     SimpleTest.waitForExplicitFinish();
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
     ]}, runTest);
   </script>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_gzip_redirect.html
+++ b/dom/workers/test/serviceworkers/test_gzip_redirect.html
@@ -71,15 +71,14 @@
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
+    ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html
+++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html
+++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_notificationclick.html
+++ b/dom/workers/test/serviceworkers/test_notificationclick.html
@@ -44,14 +44,13 @@ https://bugzilla.mozilla.org/show_bug.cg
   };
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.webnotifications.workers.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
     ["notification.prompt.testing", true],
   ]}, runTest);
 </script>
 </body>
 </html>
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/test_notificationclick_focus.html
+++ /dev/null
@@ -1,57 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=916893
--->
-<head>
-  <title>Bug 1144660 - Test client.focus() permissions on notification click</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
-  <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<pre id="test">
-</pre>
-<script type="text/javascript">
-  SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
-
-  function testFrame(src) {
-    var iframe = document.createElement("iframe");
-    iframe.src = src;
-    window.callback = function(result) {
-      window.callback = null;
-      document.body.removeChild(iframe);
-      iframe = null;
-      ok(result, "All tests passed.");
-      MockServices.unregister();
-      SimpleTest.finish();
-    };
-    document.body.appendChild(iframe);
-  }
-
-  function runTest() {
-    MockServices.register();
-    testFrame('notificationclick_focus.html');
-    navigator.serviceWorker.register("notificationclick_focus.js", { scope: "notificationclick_focus.html" }).then(function(reg) {
-    }, function(e) {
-      ok(false, "registration should have passed!");
-    });
-  };
-
-  SimpleTest.waitForExplicitFinish();
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true],
-    ["dom.webnotifications.workers.enabled", true],
-    ["notification.prompt.testing", true],
-    ["dom.disable_open_click_delay", 1000],
-  ]}, runTest);
-</script>
-</body>
-</html>
--- a/dom/workers/test/serviceworkers/test_opaque_intercept.html
+++ b/dom/workers/test/serviceworkers/test_opaque_intercept.html
@@ -72,16 +72,15 @@
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
     ["dom.serviceWorkers.interception.opaque.enabled", true],
     ["dom.caches.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -169,23 +169,23 @@ var interfaceNamesInGlobalScope =
     "PerformanceEntry",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMark",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMeasure",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Promise",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushEvent", b2g: false, android: false},
+    { name: "PushEvent", b2g: false, android: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushManager", b2g: false, android: false},
+    { name: "PushManager", b2g: false, android: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushMessageData", b2g: false, android: false},
+    { name: "PushMessageData", b2g: false, android: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushSubscription", b2g: false, android: false},
+    { name: "PushSubscription", b2g: false, android: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Request",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Response",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorker",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ServiceWorkerGlobalScope",
--- a/dom/workers/test/serviceworkers/test_third_party_iframes.html
+++ b/dom/workers/test/serviceworkers/test_third_party_iframes.html
@@ -138,17 +138,16 @@ const COOKIE_BEHAVIOR_REJECTFOREIGN = 1;
 const COOKIE_BEHAVIOR_REJECT        = 2;
 const COOKIE_BEHAVIOR_LIMITFOREIGN  = 3;
 
 let steps = [() => {
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["browser.dom.window.dump.enabled", true],
     ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT]
   ]}, next);
 }, () => {
   testShouldIntercept(next);
 }, () => {
   SpecialPowers.pushPrefEnv({"set": [
     ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECTFOREIGN]
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -161,25 +161,25 @@ var interfaceNamesInGlobalScope =
     "PerformanceEntry",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMark",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceMeasure",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Promise",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushManager", b2g: false, android: false},
+    { name: "PushManager", b2g: false, android: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "PushSubscription", b2g: false, android: false},
+    { name: "PushSubscription", b2g: false, android: false, release: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Request",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Response",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    { name: "ServiceWorkerRegistration", b2g: false },
+    { name: "ServiceWorkerRegistration", release: false, b2g: false },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextDecoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "TextEncoder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "XMLHttpRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "XMLHttpRequestEventTarget",
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -134,16 +134,17 @@ pref("dom.permissions.enabled", false);
 // Whether or not Web Workers are enabled.
 pref("dom.workers.enabled", true);
 // The number of workers per domain allowed to run concurrently.
 pref("dom.workers.maxPerDomain", 20);
 
 // Whether or not Shared Web Workers are enabled.
 pref("dom.workers.sharedWorkers.enabled", true);
 
+// Service workers
 pref("dom.serviceWorkers.enabled", false);
 
 // Allow service workers to intercept network requests using the fetch event
 pref("dom.serviceWorkers.interception.enabled", false);
 
 // Allow service workers to intercept opaque (cross origin) responses
 pref("dom.serviceWorkers.interception.opaque.enabled", false);
 
@@ -4442,22 +4443,29 @@ pref("dom.sms.maxReadAheadEntries", 0);
 // WebContacts
 pref("dom.mozContacts.enabled", false);
 
 // WebAlarms
 pref("dom.mozAlarms.enabled", false);
 
 // Push
 
+#if !defined(MOZ_B2G) && !defined(ANDROID)
+// Desktop prefs
+#ifdef RELEASE_BUILD
 pref("dom.push.enabled", false);
-
-#if !defined(RELEASE_BUILD)
-pref("dom.push.debug", true);
+#else
+pref("dom.push.enabled", true);
 #endif
-
+#else
+// Mobile prefs
+pref("dom.push.enabled", false);
+#endif
+
+pref("dom.push.debug", false);
 pref("dom.push.serverURL", "wss://push.services.mozilla.com/");
 pref("dom.push.userAgentID", "");
 
 // The maximum number of notifications that a service worker can receive
 // without user interaction.
 pref("dom.push.maxQuotaPerSubscription", 16);
 
 // Is the network connection allowed to be up?