Bug 1257401 - Remove the worker descriptor for `PushManager`. r=khuey
authorKit Cambridge <kcambridge@mozilla.com>
Fri, 01 Apr 2016 15:25:49 -0700
changeset 291440 e1a56a0d9db2880d37b650989bbeeb780a30dc60
parent 291439 f07da9ef1c673e1c02d458e5a98feaf135e56df7
child 291441 c7b00e87d6443d3e232fe4042f7fd7f111c6ddbf
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs1257401
milestone48.0a1
Bug 1257401 - Remove the worker descriptor for `PushManager`. r=khuey MozReview-Commit-ID: 4nZElH1K3W5
dom/bindings/Bindings.conf
dom/push/Push.js
dom/push/PushManager.cpp
dom/push/PushManager.h
dom/push/PushSubscription.cpp
dom/push/PushSubscription.h
dom/webidl/PushManager.webidl
dom/workers/ServiceWorkerRegistration.cpp
dom/workers/ServiceWorkerRegistration.h
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -950,26 +950,16 @@ DOMInterfaces = {
     'nativeType': 'mozilla::dom::workers::PushEvent',
 },
 
 'PushMessageData': {
     'headerFile': 'ServiceWorkerEvents.h',
     'nativeType': 'mozilla::dom::workers::PushMessageData',
 },
 
-'PushManager': [{
-    'workers': False,
-    'headerFile': 'mozilla/dom/PushManager.h',
-    'nativeType': 'mozilla::dom::PushManager',
-}, {
-    'workers': True,
-    'headerFile': 'mozilla/dom/PushManager.h',
-    'nativeType': 'mozilla::dom::WorkerPushManager',
-}],
-
 'Range': {
     'nativeType': 'nsRange',
     'binaryNames': {
         '__stringifier': 'ToString'
     }
 },
 
 'Rect': {
@@ -1003,16 +993,17 @@ DOMInterfaces = {
 'ServiceWorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
     'workers': True,
 },
 
 'ServiceWorkerRegistration': [{
     'nativeType': 'mozilla::dom::ServiceWorkerRegistrationMainThread',
     'headerFile': 'mozilla/dom/ServiceWorkerRegistration.h',
+    'implicitJSContext': [ 'pushManager' ],
 }, {
     'workers': True,
     'nativeType': 'mozilla::dom::ServiceWorkerRegistrationWorkerThread',
     'headerFile': 'mozilla/dom/ServiceWorkerRegistration.h',
 }],
 
 'SharedWorker': {
     'nativeType': 'mozilla::dom::workers::SharedWorker',
--- a/dom/push/Push.js
+++ b/dom/push/Push.js
@@ -51,18 +51,17 @@ Push.prototype = {
 
     this._window = aWindow;
 
     this.initDOMRequestHelper(aWindow);
 
     this._principal = aWindow.document.nodePrincipal;
   },
 
-  setScope: function(scope){
-    console.debug("setScope()", scope);
+  __init: function(scope) {
     this._scope = scope;
   },
 
   askPermission: function (aAllowCallback, aCancelCallback) {
     console.debug("askPermission()");
 
     return this.createPromise((resolve, reject) => {
       let permissionDenied = () => {
--- a/dom/push/PushManager.cpp
+++ b/dom/push/PushManager.cpp
@@ -1,50 +1,46 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/dom/PushManager.h"
 
-#include "mozilla/Base64.h"
-#include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/PushManagerBinding.h"
 #include "mozilla/dom/PushSubscription.h"
-#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
 
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
 
 #include "nsIGlobalObject.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIPushService.h"
 
 #include "nsComponentManagerUtils.h"
-#include "nsFrameMessageManager.h"
-#include "nsContentCID.h"
+#include "nsContentUtils.h"
 
 #include "WorkerRunnable.h"
 #include "WorkerPrivate.h"
 #include "WorkerScope.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace workers;
 
 namespace {
 
 nsresult
 GetPermissionState(nsIPrincipal* aPrincipal,
-                            PushPermissionState& aState)
+                   PushPermissionState& aState)
 {
   nsCOMPtr<nsIPermissionManager> permManager =
     mozilla::services::GetPermissionManager();
 
   if (!permManager) {
     return NS_ERROR_FAILURE;
   }
   uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
@@ -58,127 +54,60 @@ GetPermissionState(nsIPrincipal* aPrinci
 
   if (permission == nsIPermissionManager::ALLOW_ACTION) {
     aState = PushPermissionState::Granted;
   } else if (permission == nsIPermissionManager::DENY_ACTION) {
     aState = PushPermissionState::Denied;
   } else {
     aState = PushPermissionState::Prompt;
   }
+
   return NS_OK;
 }
 
-} // anonymous namespace
-
-PushManager::PushManager(nsIGlobalObject* aGlobal, const nsAString& aScope)
-  : mGlobal(aGlobal), mScope(aScope)
-{
-  AssertIsOnMainThread();
-}
-
-PushManager::~PushManager()
-{}
-
-JSObject*
-PushManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  // XXXnsm I don't know if this is the right way to do it, but I want to assert
-  // that an implementation has been set before this object gets exposed to JS.
-  MOZ_ASSERT(mImpl);
-  return PushManagerBinding::Wrap(aCx, this, aGivenProto);
-}
-
-void
-PushManager::SetPushManagerImpl(PushManagerImpl& foo, ErrorResult& aRv)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mImpl);
-  mImpl = &foo;
-}
-
-already_AddRefed<Promise>
-PushManager::Subscribe(ErrorResult& aRv)
-{
-  MOZ_ASSERT(mImpl);
-  return mImpl->Subscribe(aRv);
-}
-
-already_AddRefed<Promise>
-PushManager::GetSubscription(ErrorResult& aRv)
-{
-  MOZ_ASSERT(mImpl);
-  return mImpl->GetSubscription(aRv);
-}
-
-already_AddRefed<Promise>
-PushManager::PermissionState(ErrorResult& aRv)
-{
-  MOZ_ASSERT(mImpl);
-  return mImpl->PermissionState(aRv);
-}
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushManager, mGlobal, mImpl)
-NS_IMPL_CYCLE_COLLECTING_ADDREF(PushManager)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(PushManager)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushManager)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-// WorkerPushManager
-
-WorkerPushManager::WorkerPushManager(const nsAString& aScope)
-  : mScope(aScope)
-{
-}
-
-JSObject*
-WorkerPushManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return PushManagerBinding_workers::Wrap(aCx, this, aGivenProto);
-}
-
 class GetSubscriptionResultRunnable final : public WorkerRunnable
 {
 public:
-  GetSubscriptionResultRunnable(PromiseWorkerProxy* aProxy,
+  GetSubscriptionResultRunnable(WorkerPrivate* aWorkerPrivate,
+                                already_AddRefed<PromiseWorkerProxy>&& aProxy,
                                 nsresult aStatus,
                                 const nsAString& aEndpoint,
                                 const nsAString& aScope,
-                                const nsTArray<uint8_t>& aRawP256dhKey,
-                                const nsTArray<uint8_t>& aAuthSecret)
-    : WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
-    , mProxy(aProxy)
+                                nsTArray<uint8_t>&& aRawP256dhKey,
+                                nsTArray<uint8_t>&& aAuthSecret)
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+    , mProxy(Move(aProxy))
     , mStatus(aStatus)
     , mEndpoint(aEndpoint)
     , mScope(aScope)
-    , mRawP256dhKey(aRawP256dhKey)
-    , mAuthSecret(aAuthSecret)
+    , mRawP256dhKey(Move(aRawP256dhKey))
+    , mAuthSecret(Move(aAuthSecret))
   { }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     RefPtr<Promise> promise = mProxy->WorkerPromise();
     if (NS_SUCCEEDED(mStatus)) {
       if (mEndpoint.IsEmpty()) {
         promise->MaybeResolve(JS::NullHandleValue);
       } else {
         RefPtr<PushSubscription> sub =
-            new PushSubscription(nullptr, mEndpoint, mScope, mRawP256dhKey,
-                                 mAuthSecret);
+            new PushSubscription(nullptr, mEndpoint, mScope,
+                                 Move(mRawP256dhKey), Move(mAuthSecret));
         promise->MaybeResolve(sub);
       }
     } else if (NS_ERROR_GET_MODULE(mStatus) == NS_ERROR_MODULE_DOM_PUSH ) {
       promise->MaybeReject(mStatus);
     } else {
       promise->MaybeReject(NS_ERROR_DOM_PUSH_ABORT_ERR);
     }
 
     mProxy->CleanUp();
+
     return true;
   }
 private:
   ~GetSubscriptionResultRunnable()
   {}
 
   RefPtr<PromiseWorkerProxy> mProxy;
   nsresult mStatus;
@@ -201,38 +130,39 @@ public:
 
   NS_IMETHOD
   OnPushSubscription(nsresult aStatus,
                      nsIPushSubscription* aSubscription) override
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(mProxy, "OnPushSubscription() called twice?");
 
-    RefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
-
-    MutexAutoLock lock(proxy->Lock());
-    if (proxy->CleanedUp()) {
+    MutexAutoLock lock(mProxy->Lock());
+    if (mProxy->CleanedUp()) {
       return NS_OK;
     }
 
     nsAutoString endpoint;
     nsTArray<uint8_t> rawP256dhKey, authSecret;
     if (NS_SUCCEEDED(aStatus)) {
       aStatus = GetSubscriptionParams(aSubscription, endpoint, rawP256dhKey,
                                       authSecret);
     }
 
+    WorkerPrivate* worker = mProxy->GetWorkerPrivate();
     RefPtr<GetSubscriptionResultRunnable> r =
-      new GetSubscriptionResultRunnable(proxy,
+      new GetSubscriptionResultRunnable(worker,
+                                        mProxy.forget(),
                                         aStatus,
                                         endpoint,
                                         mScope,
-                                        rawP256dhKey,
-                                        authSecret);
-    r->Dispatch();
+                                        Move(rawP256dhKey),
+                                        Move(authSecret));
+    MOZ_ALWAYS_TRUE(r->Dispatch());
+
     return NS_OK;
   }
 
   // Convenience method for use in this file.
   void
   OnPushSubscriptionError(nsresult aStatus)
   {
     Unused << NS_WARN_IF(NS_FAILED(
@@ -244,16 +174,17 @@ protected:
   {}
 
 private:
   inline nsresult
   FreeKeys(nsresult aStatus, uint8_t* aKey, uint8_t* aAuthSecret)
   {
     NS_Free(aKey);
     NS_Free(aAuthSecret);
+
     return aStatus;
   }
 
   nsresult
   GetSubscriptionParams(nsIPushSubscription* aSubscription,
                         nsAString& aEndpoint,
                         nsTArray<uint8_t>& aRawP256dhKey,
                         nsTArray<uint8_t>& aAuthSecret)
@@ -301,69 +232,71 @@ private:
 
 NS_IMPL_ISUPPORTS(GetSubscriptionCallback, nsIPushSubscriptionCallback)
 
 class GetSubscriptionRunnable final : public nsRunnable
 {
 public:
   GetSubscriptionRunnable(PromiseWorkerProxy* aProxy,
                           const nsAString& aScope,
-                          WorkerPushManager::SubscriptionAction aAction)
+                          PushManager::SubscriptionAction aAction)
     : mProxy(aProxy)
     , mScope(aScope), mAction(aAction)
   {}
 
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
 
     nsCOMPtr<nsIPrincipal> principal;
+
     {
       // Bug 1228723: If permission is revoked or an error occurs, the
       // subscription callback will be called synchronously. This causes
       // `GetSubscriptionCallback::OnPushSubscription` to deadlock when
       // it tries to acquire the lock.
       MutexAutoLock lock(mProxy->Lock());
       if (mProxy->CleanedUp()) {
         return NS_OK;
       }
       principal = mProxy->GetWorkerPrivate()->GetPrincipal();
     }
+
     MOZ_ASSERT(principal);
 
     RefPtr<GetSubscriptionCallback> callback = new GetSubscriptionCallback(mProxy, mScope);
 
     PushPermissionState state;
     nsresult rv = GetPermissionState(principal, state);
     if (NS_FAILED(rv)) {
       callback->OnPushSubscriptionError(NS_ERROR_FAILURE);
       return NS_OK;
     }
 
     if (state != PushPermissionState::Granted) {
-      if (mAction == WorkerPushManager::GetSubscriptionAction) {
+      if (mAction == PushManager::GetSubscriptionAction) {
         callback->OnPushSubscriptionError(NS_OK);
         return NS_OK;
       }
       callback->OnPushSubscriptionError(NS_ERROR_DOM_PUSH_DENIED_ERR);
       return NS_OK;
     }
 
     nsCOMPtr<nsIPushService> service =
       do_GetService("@mozilla.org/push/Service;1");
-    if (!service) {
+    if (NS_WARN_IF(!service)) {
       callback->OnPushSubscriptionError(NS_ERROR_FAILURE);
       return NS_OK;
     }
 
-    if (mAction == WorkerPushManager::SubscribeAction) {
+    if (mAction == PushManager::SubscribeAction) {
       rv = service->Subscribe(mScope, principal, callback);
     } else {
-      MOZ_ASSERT(mAction == WorkerPushManager::GetSubscriptionAction);
+      MOZ_ASSERT(mAction == PushManager::GetSubscriptionAction);
       rv = service->GetSubscription(mScope, principal, callback);
     }
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
       callback->OnPushSubscriptionError(NS_ERROR_FAILURE);
       return NS_OK;
     }
 
@@ -371,57 +304,19 @@ public:
   }
 
 private:
   ~GetSubscriptionRunnable()
   {}
 
   RefPtr<PromiseWorkerProxy> mProxy;
   nsString mScope;
-  WorkerPushManager::SubscriptionAction mAction;
+  PushManager::SubscriptionAction mAction;
 };
 
-already_AddRefed<Promise>
-WorkerPushManager::PerformSubscriptionAction(SubscriptionAction aAction, ErrorResult& aRv)
-{
-  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
-  MOZ_ASSERT(worker);
-  worker->AssertIsOnWorkerThread();
-
-  nsCOMPtr<nsIGlobalObject> global = worker->GlobalScope();
-  RefPtr<Promise> p = Promise::Create(global, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, p);
-  if (!proxy) {
-    p->MaybeReject(NS_ERROR_DOM_PUSH_ABORT_ERR);
-    return p.forget();
-  }
-
-  RefPtr<GetSubscriptionRunnable> r =
-    new GetSubscriptionRunnable(proxy, mScope, aAction);
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
-
-  return p.forget();
-}
-
-already_AddRefed<Promise>
-WorkerPushManager::Subscribe(ErrorResult& aRv)
-{
-  return PerformSubscriptionAction(SubscribeAction, aRv);
-}
-
-already_AddRefed<Promise>
-WorkerPushManager::GetSubscription(ErrorResult& aRv)
-{
-  return PerformSubscriptionAction(GetSubscriptionAction, aRv);
-}
-
 class PermissionResultRunnable final : public WorkerRunnable
 {
 public:
   PermissionResultRunnable(PromiseWorkerProxy *aProxy,
                            nsresult aStatus,
                            PushPermissionState aState)
     : WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
     , mProxy(aProxy)
@@ -440,16 +335,17 @@ public:
     RefPtr<Promise> promise = mProxy->WorkerPromise();
     if (NS_SUCCEEDED(mStatus)) {
       promise->MaybeResolve(mState);
     } else {
       promise->MaybeReject(aCx, JS::UndefinedHandleValue);
     }
 
     mProxy->CleanUp();
+
     return true;
   }
 
 private:
   ~PermissionResultRunnable()
   {}
 
   RefPtr<PromiseWorkerProxy> mProxy;
@@ -476,30 +372,121 @@ public:
     PushPermissionState state;
     nsresult rv = GetPermissionState(
       mProxy->GetWorkerPrivate()->GetPrincipal(),
       state
     );
 
     RefPtr<PermissionResultRunnable> r =
       new PermissionResultRunnable(mProxy, rv, state);
-    r->Dispatch();
+    MOZ_ALWAYS_TRUE(r->Dispatch());
+
     return NS_OK;
   }
 
 private:
   ~PermissionStateRunnable()
   {}
 
   RefPtr<PromiseWorkerProxy> mProxy;
 };
 
+} // anonymous namespace
+
+PushManager::PushManager(nsIGlobalObject* aGlobal, PushManagerImpl* aImpl)
+  : mGlobal(aGlobal)
+  , mImpl(aImpl)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aImpl);
+}
+
+PushManager::PushManager(const nsAString& aScope)
+  : mScope(aScope)
+{
+#ifdef DEBUG
+  // There's only one global on a worker, so we don't need to pass a global
+  // object to the constructor.
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+#endif
+}
+
+PushManager::~PushManager()
+{}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushManager, mGlobal, mImpl)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(PushManager)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(PushManager)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushManager)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+JSObject*
+PushManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return PushManagerBinding::Wrap(aCx, this, aGivenProto);
+}
+
+// static
+already_AddRefed<PushManager>
+PushManager::Constructor(GlobalObject& aGlobal,
+                         const nsAString& aScope,
+                         ErrorResult& aRv)
+{
+  if (!NS_IsMainThread()) {
+    RefPtr<PushManager> ret = new PushManager(aScope);
+    return ret.forget();
+  }
+
+  RefPtr<PushManagerImpl> impl = PushManagerImpl::Constructor(aGlobal,
+                                                              aGlobal.Context(),
+                                                              aScope, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  RefPtr<PushManager> ret = new PushManager(global, impl);
+
+  return ret.forget();
+}
+
 already_AddRefed<Promise>
-WorkerPushManager::PermissionState(ErrorResult& aRv)
+PushManager::Subscribe(ErrorResult& aRv)
+{
+  if (mImpl) {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mImpl->Subscribe(aRv);
+  }
+
+  return PerformSubscriptionActionFromWorker(SubscribeAction, aRv);
+}
+
+already_AddRefed<Promise>
+PushManager::GetSubscription(ErrorResult& aRv)
 {
+  if (mImpl) {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mImpl->GetSubscription(aRv);
+  }
+
+  return PerformSubscriptionActionFromWorker(GetSubscriptionAction, aRv);
+}
+
+already_AddRefed<Promise>
+PushManager::PermissionState(ErrorResult& aRv)
+{
+  if (mImpl) {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mImpl->PermissionState(aRv);
+  }
+
   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
 
   nsCOMPtr<nsIGlobalObject> global = worker->GlobalScope();
   RefPtr<Promise> p = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
@@ -513,20 +500,37 @@ WorkerPushManager::PermissionState(Error
 
   RefPtr<PermissionStateRunnable> r =
     new PermissionStateRunnable(proxy);
   NS_DispatchToMainThread(r);
 
   return p.forget();
 }
 
-WorkerPushManager::~WorkerPushManager()
-{}
+already_AddRefed<Promise>
+PushManager::PerformSubscriptionActionFromWorker(
+  SubscriptionAction aAction, ErrorResult& aRv)
+{
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(worker);
+  worker->AssertIsOnWorkerThread();
+
+  nsCOMPtr<nsIGlobalObject> global = worker->GlobalScope();
+  RefPtr<Promise> p = Promise::Create(global, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerPushManager)
-NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkerPushManager)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkerPushManager)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerPushManager)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
+  RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, p);
+  if (!proxy) {
+    p->MaybeReject(NS_ERROR_DOM_PUSH_ABORT_ERR);
+    return p.forget();
+  }
+
+  RefPtr<GetSubscriptionRunnable> r =
+    new GetSubscriptionRunnable(proxy, mScope, aAction);
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+
+  return p.forget();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/push/PushManager.h
+++ b/dom/push/PushManager.h
@@ -1,33 +1,28 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 /**
- * We would like to expose PushManager and PushSubscription on window and
- * workers. Parts of the Push API is implemented in JS out of necessity due to:
- * 1) Using frame message managers, in which
- *    nsIMessageListener::receiveMessage() must be in JS.
- * 2) It is easier to use certain APIs like the permission prompt and Promises
- *    from JS.
+ * PushManager and PushSubscription are exposed on the main and worker threads.
+ * The main thread version is implemented in Push.js. The JS implementation
+ * makes it easier to use certain APIs like the permission prompt and Promises.
  *
- * Unfortunately, JS-implemented WebIDL is not supported off the main thread. To
- * aid in fixing this, the nsIPushClient is introduced which deals with part (1)
- * above. Part (2) is handled by PushManagerImpl on the main thread. PushManager
- * wraps this in C++ since our bindings code cannot accomodate "JS-implemented
- * on the main thread, C++ on the worker" bindings. PushManager simply forwards
- * the calls to the JS component.
+ * Unfortunately, JS-implemented WebIDL is not supported off the main thread.
+ * To work around this, we use a chain of runnables to query the JS-implemented
+ * nsIPushService component for subscription information, and return the
+ * results to the worker. We don't have to deal with permission prompts, since
+ * we just reject calls if the principal does not have permission.
  *
- * On the worker threads, we don't have to deal with permission prompts, instead
- * we just reject calls if the principal does not have permission. On workers
- * WorkerPushManager dispatches runnables to the main thread which directly call
- * nsIPushClient.
+ * On the main thread, PushManager wraps a JS-implemented PushManagerImpl
+ * instance. The C++ wrapper is necessary because our bindings code cannot
+ * accomodate "JS-implemented on the main thread, C++ on the worker" bindings.
  *
  * PushSubscription is in C++ on both threads since it isn't particularly
  * verbose to implement in C++ compared to JS.
  */
 
 #ifndef mozilla_dom_PushManager_h
 #define mozilla_dom_PushManager_h
 
@@ -35,109 +30,81 @@
 
 #include "mozilla/AlreadyAddRefed.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/TypedArray.h"
 
 #include "nsCOMPtr.h"
 #include "mozilla/RefPtr.h"
-#include "jsapi.h"
 
 class nsIGlobalObject;
 class nsIPrincipal;
 
 namespace mozilla {
 namespace dom {
 
 namespace workers {
 class WorkerPrivate;
 }
 
 class Promise;
 class PushManagerImpl;
 
-
-
 class PushManager final : public nsISupports
                         , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushManager)
 
-  explicit PushManager(nsIGlobalObject* aGlobal, const nsAString& aScope);
+  enum SubscriptionAction {
+    SubscribeAction,
+    GetSubscriptionAction,
+  };
+
+  // The main thread constructor.
+  PushManager(nsIGlobalObject* aGlobal, PushManagerImpl* aImpl);
+
+  // The worker thread constructor.
+  explicit PushManager(const nsAString& aScope);
 
   nsIGlobalObject*
   GetParentObject() const
   {
     return mGlobal;
   }
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  already_AddRefed<Promise>
-  Subscribe(ErrorResult& aRv);
-
-  already_AddRefed<Promise>
-  GetSubscription(ErrorResult& aRv);
+  static already_AddRefed<PushManager>
+  Constructor(GlobalObject& aGlobal, const nsAString& aScope,
+              ErrorResult& aRv);
 
   already_AddRefed<Promise>
-  PermissionState(ErrorResult& aRv);
-
-  void
-  SetPushManagerImpl(PushManagerImpl& foo, ErrorResult& aRv);
-
-protected:
-  ~PushManager();
-
-private:
-  nsCOMPtr<nsIGlobalObject> mGlobal;
-  RefPtr<PushManagerImpl> mImpl;
-  nsString mScope;
-};
-
-class WorkerPushManager final : public nsISupports
-                              , public nsWrapperCache
-{
-public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WorkerPushManager)
-
-  enum SubscriptionAction {
-    SubscribeAction,
-    GetSubscriptionAction,
-  };
-
-  explicit WorkerPushManager(const nsAString& aScope);
-
-  nsIGlobalObject*
-  GetParentObject() const
-  {
-    return nullptr;
-  }
-
-  JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
-  already_AddRefed<Promise>
-  PerformSubscriptionAction(SubscriptionAction aAction, ErrorResult& aRv);
+  PerformSubscriptionActionFromWorker(SubscriptionAction aAction,
+                                      ErrorResult& aRv);
 
   already_AddRefed<Promise>
   Subscribe(ErrorResult& aRv);
 
   already_AddRefed<Promise>
   GetSubscription(ErrorResult& aRv);
 
   already_AddRefed<Promise>
   PermissionState(ErrorResult& aRv);
 
 protected:
-  ~WorkerPushManager();
+  ~PushManager();
 
 private:
+  // The following are only set and accessed on the main thread.
+  nsCOMPtr<nsIGlobalObject> mGlobal;
+  RefPtr<PushManagerImpl> mImpl;
+
+  // Only used on the worker thread.
   nsString mScope;
 };
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PushManager_h
--- a/dom/push/PushSubscription.cpp
+++ b/dom/push/PushSubscription.cpp
@@ -186,27 +186,40 @@ public:
 private:
   ~UnsubscribeRunnable()
   {}
 
   RefPtr<PromiseWorkerProxy> mProxy;
   nsString mScope;
 };
 
+bool
+CopyArrayBufferToArray(const ArrayBuffer& aBuffer,
+                       nsTArray<uint8_t>& aArray)
+{
+  aBuffer.ComputeLengthAndData();
+  if (!aArray.SetLength(aBuffer.Length(), fallible) ||
+      !aArray.ReplaceElementsAt(0, aBuffer.Length(), aBuffer.Data(),
+                                aBuffer.Length(), fallible)) {
+    return false;
+  }
+  return true;
+}
+
 } // anonymous namespace
 
 PushSubscription::PushSubscription(nsIGlobalObject* aGlobal,
                                    const nsAString& aEndpoint,
                                    const nsAString& aScope,
-                                   const nsTArray<uint8_t>& aRawP256dhKey,
-                                   const nsTArray<uint8_t>& aAuthSecret)
+                                   nsTArray<uint8_t>&& aRawP256dhKey,
+                                   nsTArray<uint8_t>&& aAuthSecret)
   : mEndpoint(aEndpoint)
   , mScope(aScope)
-  , mRawP256dhKey(aRawP256dhKey)
-  , mAuthSecret(aAuthSecret)
+  , mRawP256dhKey(Move(aRawP256dhKey))
+  , mAuthSecret(Move(aAuthSecret))
 {
   if (NS_IsMainThread()) {
     mGlobal = aGlobal;
   } else {
 #ifdef DEBUG
     // There's only one global on a worker, so we don't need to pass a global
     // object to the constructor.
     WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
@@ -240,37 +253,30 @@ PushSubscription::Constructor(GlobalObje
                               const nsAString& aEndpoint,
                               const nsAString& aScope,
                               const Nullable<ArrayBuffer>& aP256dhKey,
                               const Nullable<ArrayBuffer>& aAuthSecret,
                               ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 
-  nsTArray<uint8_t> rawKey;
-  if (!aP256dhKey.IsNull()) {
-    const ArrayBuffer& key = aP256dhKey.Value();
-    key.ComputeLengthAndData();
-    rawKey.SetLength(key.Length());
-    rawKey.ReplaceElementsAt(0, key.Length(), key.Data(), key.Length());
+  nsTArray<uint8_t> rawKey, authSecret;
+  if ((!aP256dhKey.IsNull() && !CopyArrayBufferToArray(aP256dhKey.Value(),
+                                                       rawKey)) ||
+      (!aAuthSecret.IsNull() && !CopyArrayBufferToArray(aAuthSecret.Value(),
+                                                        authSecret))) {
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return nullptr;
   }
 
-  nsTArray<uint8_t> authSecret;
-  if (!aAuthSecret.IsNull()) {
-    const ArrayBuffer& sekrit = aAuthSecret.Value();
-    sekrit.ComputeLengthAndData();
-    authSecret.SetLength(sekrit.Length());
-    authSecret.ReplaceElementsAt(0, sekrit.Length(),
-                                 sekrit.Data(), sekrit.Length());
-  }
   RefPtr<PushSubscription> sub = new PushSubscription(global,
                                                       aEndpoint,
                                                       aScope,
-                                                      rawKey,
-                                                      authSecret);
+                                                      Move(rawKey),
+                                                      Move(authSecret));
 
   return sub.forget();
 }
 
 already_AddRefed<Promise>
 PushSubscription::Unsubscribe(ErrorResult& aRv)
 {
   if (!NS_IsMainThread()) {
--- a/dom/push/PushSubscription.h
+++ b/dom/push/PushSubscription.h
@@ -33,18 +33,18 @@ class PushSubscription final : public ns
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushSubscription)
 
   PushSubscription(nsIGlobalObject* aGlobal,
                    const nsAString& aEndpoint,
                    const nsAString& aScope,
-                   const nsTArray<uint8_t>& aP256dhKey,
-                   const nsTArray<uint8_t>& aAuthSecret);
+                   nsTArray<uint8_t>&& aP256dhKey,
+                   nsTArray<uint8_t>&& aAuthSecret);
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   nsIGlobalObject*
   GetParentObject() const
   {
     return mGlobal;
--- a/dom/webidl/PushManager.webidl
+++ b/dom/webidl/PushManager.webidl
@@ -2,40 +2,33 @@
 /* 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/.
 *
 * The origin of this IDL file is
 * https://w3c.github.io/push-api/
 */
 
-// Please see comments in dom/push/PushManager.h for the split between
-// PushManagerImpl and PushManager.
+// The main thread JS implementation. Please see comments in
+// dom/push/PushManager.h for the split between PushManagerImpl and PushManager.
 [JSImplementation="@mozilla.org/push/PushManager;1",
- NoInterfaceObject]
+ ChromeOnly, Constructor(DOMString scope)]
 interface PushManagerImpl {
-    Promise<PushSubscription>     subscribe();
-    Promise<PushSubscription?>    getSubscription();
-    Promise<PushPermissionState> permissionState();
-
-    // We need a setter in the bindings so that the C++ can use it,
-    // but we don't want it exposed to client JS.  WebPushMethodHider
-    // always returns false.
-    [Func="ServiceWorkerRegistration::WebPushMethodHider"] void setScope(DOMString scope);
+  Promise<PushSubscription>    subscribe();
+  Promise<PushSubscription?>   getSubscription();
+  Promise<PushPermissionState> permissionState();
 };
 
-[Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled"]
+[Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled",
+ ChromeConstructor(DOMString scope)]
 interface PushManager {
-  [ChromeOnly, Throws, Exposed=Window]
-  void setPushManagerImpl(PushManagerImpl store);
-
   [Throws, UseCounter]
-  Promise<PushSubscription>     subscribe();
+  Promise<PushSubscription>    subscribe();
   [Throws]
-  Promise<PushSubscription?>    getSubscription();
+  Promise<PushSubscription?>   getSubscription();
   [Throws]
   Promise<PushPermissionState> permissionState();
 };
 
 enum PushPermissionState
 {
     "granted",
     "denied",
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -747,58 +747,44 @@ ServiceWorkerRegistrationMainThread::Get
   if (NS_WARN_IF(!window)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
   return Notification::Get(window, aOptions, mScope, aRv);
 }
 
 already_AddRefed<PushManager>
-ServiceWorkerRegistrationMainThread::GetPushManager(ErrorResult& aRv)
+ServiceWorkerRegistrationMainThread::GetPushManager(JSContext* aCx,
+                                                    ErrorResult& aRv)
 {
   AssertIsOnMainThread();
 
 #ifdef MOZ_SIMPLEPUSH
   return nullptr;
 #else
 
   if (!mPushManager) {
     nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(GetOwner());
 
     if (!globalObject) {
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
-    // TODO: bug 1148117.  This will fail when swr is exposed on workers
-    JS::Rooted<JSObject*> jsImplObj(nsContentUtils::RootingCxForThread());
-    ConstructJSImplementation("@mozilla.org/push/PushManager;1",
-                              globalObject, &jsImplObj, aRv);
+    GlobalObject global(aCx, globalObject->GetGlobalJSObject());
+    mPushManager = PushManager::Constructor(global, mScope, aRv);
     if (aRv.Failed()) {
       return nullptr;
     }
-    mPushManager = new PushManager(globalObject, mScope);
-
-    RefPtr<PushManagerImpl> impl = new PushManagerImpl(jsImplObj, globalObject);
-    impl->SetScope(mScope, aRv);
-    if (aRv.Failed()) {
-      mPushManager = nullptr;
-      return nullptr;
-    }
-    mPushManager->SetPushManagerImpl(*impl, aRv);
-    if (aRv.Failed()) {
-      mPushManager = nullptr;
-      return nullptr;
-    }
   }
 
   RefPtr<PushManager> ret = mPushManager;
   return ret.forget();
 
-  #endif /* ! MOZ_SIMPLEPUSH */
+#endif /* ! MOZ_SIMPLEPUSH */
 }
 
 ////////////////////////////////////////////////////
 // Worker Thread implementation
 class WorkerListener final : public ServiceWorkerRegistrationListener
 {
   // Accessed on the main thread.
   WorkerPrivate* mWorkerPrivate;
@@ -1212,27 +1198,27 @@ ServiceWorkerRegistrationWorkerThread::S
 }
 
 already_AddRefed<Promise>
 ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv)
 {
   return Notification::WorkerGet(mWorkerPrivate, aOptions, mScope, aRv);
 }
 
-already_AddRefed<WorkerPushManager>
+already_AddRefed<PushManager>
 ServiceWorkerRegistrationWorkerThread::GetPushManager(ErrorResult& aRv)
 {
 #ifdef MOZ_SIMPLEPUSH
   return nullptr;
 #else
 
   if (!mPushManager) {
-    mPushManager = new WorkerPushManager(mScope);
+    mPushManager = new PushManager(mScope);
   }
 
-  RefPtr<WorkerPushManager> ret = mPushManager;
+  RefPtr<PushManager> ret = mPushManager;
   return ret.forget();
 
-  #endif /* ! MOZ_SIMPLEPUSH */
+#endif /* ! MOZ_SIMPLEPUSH */
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/workers/ServiceWorkerRegistration.h
+++ b/dom/workers/ServiceWorkerRegistration.h
@@ -17,45 +17,29 @@
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 class PushManager;
-class WorkerPushManager;
 class WorkerListener;
 
 namespace workers {
 class ServiceWorker;
 class WorkerPrivate;
 } // namespace workers
 
 bool
 ServiceWorkerRegistrationVisible(JSContext* aCx, JSObject* aObj);
 
 bool
 ServiceWorkerNotificationAPIVisible(JSContext* aCx, JSObject* aObj);
 
-// This class exists solely so that we can satisfy some WebIDL Func= attribute
-// constraints. Func= converts the function name to a header file to include, in
-// this case "ServiceWorkerRegistration.h".
-class ServiceWorkerRegistration final
-{
-public:
-  // Something that we can feed into the Func webidl property to ensure that
-  // SetScope is never exposed to the user.
-  static bool
-  WebPushMethodHider(JSContext* unusedContext, JSObject* unusedObject) {
-    return false;
-  }
-
-};
-
 // Used by ServiceWorkerManager to notify ServiceWorkerRegistrations of
 // updatefound event and invalidating ServiceWorker instances.
 class ServiceWorkerRegistrationListener
 {
 public:
   NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
   NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
 
@@ -134,17 +118,17 @@ public:
 
   already_AddRefed<workers::ServiceWorker>
   GetWaiting() override;
 
   already_AddRefed<workers::ServiceWorker>
   GetActive() override;
 
   already_AddRefed<PushManager>
-  GetPushManager(ErrorResult& aRv);
+  GetPushManager(JSContext* aCx, ErrorResult& aRv);
 
   // DOMEventTargethelper
   void DisconnectFromOwner() override
   {
     StopListeningForEvents();
     ServiceWorkerRegistrationBase::DisconnectFromOwner();
   }
 
@@ -236,17 +220,17 @@ public:
   GetScope(nsAString& aScope) const
   {
     aScope = mScope;
   }
 
   bool
   Notify(workers::Status aStatus) override;
 
-  already_AddRefed<WorkerPushManager>
+  already_AddRefed<PushManager>
   GetPushManager(ErrorResult& aRv);
 
 private:
   enum Reason
   {
     RegistrationIsGoingAway = 0,
     WorkerIsGoingAway,
   };
@@ -258,16 +242,16 @@ private:
 
   void
   ReleaseListener(Reason aReason);
 
   workers::WorkerPrivate* mWorkerPrivate;
   RefPtr<WorkerListener> mListener;
 
 #ifndef MOZ_SIMPLEPUSH
-  RefPtr<WorkerPushManager> mPushManager;
+  RefPtr<PushManager> mPushManager;
 #endif
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_ServiceWorkerRegistration_h */