Bug 1257401 - Remove the worker descriptor for `PushSubscription`. r=khuey
authorKit Cambridge <kcambridge@mozilla.com>
Thu, 31 Mar 2016 10:49:07 -0700
changeset 291439 f07da9ef1c673e1c02d458e5a98feaf135e56df7
parent 291438 30d51057b6f51ba19a21217362758b6c4363d668
child 291440 e1a56a0d9db2880d37b650989bbeeb780a30dc60
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 `PushSubscription`. r=khuey MozReview-Commit-ID: 9rTJn4KU2Es
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/push/moz.build
dom/webidl/PushSubscription.webidl
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -960,25 +960,16 @@ DOMInterfaces = {
     'headerFile': 'mozilla/dom/PushManager.h',
     'nativeType': 'mozilla::dom::PushManager',
 }, {
     'workers': True,
     'headerFile': 'mozilla/dom/PushManager.h',
     'nativeType': 'mozilla::dom::WorkerPushManager',
 }],
 
-'PushSubscription': [{
-    'workers': False,
-    'headerFile': 'mozilla/dom/PushManager.h',
-}, {
-    'workers': True,
-    'headerFile': 'mozilla/dom/PushManager.h',
-    'nativeType': 'mozilla::dom::WorkerPushSubscription',
-}],
-
 'Range': {
     'nativeType': 'nsRange',
     'binaryNames': {
         '__stringifier': 'ToString'
     }
 },
 
 'Rect': {
--- a/dom/push/Push.js
+++ b/dom/push/Push.js
@@ -204,17 +204,16 @@ PushSubscriptionCallback.prototype = {
     }
 
     let publicKey = this._getKey(subscription, "p256dh");
     let authSecret = this._getKey(subscription, "auth");
     let sub = new pushManager._window.PushSubscription(subscription.endpoint,
                                                        pushManager._scope,
                                                        publicKey,
                                                        authSecret);
-    sub.setPrincipal(pushManager._principal);
     this.resolve(sub);
   },
 
   _getKey: function(subscription, name) {
     let outKeyLen = {};
     let rawKey = subscription.getKey(name, outKeyLen);
     if (!outKeyLen.value) {
       return null;
--- a/dom/push/PushManager.cpp
+++ b/dom/push/PushManager.cpp
@@ -6,17 +6,17 @@
 
 #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/PushSubscriptionBinding.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"
@@ -61,193 +61,18 @@ GetPermissionState(nsIPrincipal* aPrinci
   } else if (permission == nsIPermissionManager::DENY_ACTION) {
     aState = PushPermissionState::Denied;
   } else {
     aState = PushPermissionState::Prompt;
   }
   return NS_OK;
 }
 
-void
-SubscriptionToJSON(PushSubscriptionJSON& aJSON, const nsString& aEndpoint,
-                   const nsTArray<uint8_t>& aRawP256dhKey,
-                   const nsTArray<uint8_t>& aAuthSecret)
-{
-  aJSON.mEndpoint.Construct();
-  aJSON.mEndpoint.Value() = aEndpoint;
-
-  aJSON.mKeys.mP256dh.Construct();
-  nsresult rv = Base64URLEncode(aRawP256dhKey.Length(),
-                                aRawP256dhKey.Elements(),
-                                aJSON.mKeys.mP256dh.Value());
-  Unused << NS_WARN_IF(NS_FAILED(rv));
-
-  aJSON.mKeys.mAuth.Construct();
-  rv = Base64URLEncode(aAuthSecret.Length(), aAuthSecret.Elements(),
-                       aJSON.mKeys.mAuth.Value());
-  Unused << NS_WARN_IF(NS_FAILED(rv));
-}
-
 } // anonymous namespace
 
-class UnsubscribeResultCallback final : public nsIUnsubscribeResultCallback
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  explicit UnsubscribeResultCallback(Promise* aPromise)
-    : mPromise(aPromise)
-  {
-    AssertIsOnMainThread();
-  }
-
-  NS_IMETHOD
-  OnUnsubscribe(nsresult aStatus, bool aSuccess) override
-  {
-    if (NS_SUCCEEDED(aStatus)) {
-      mPromise->MaybeResolve(aSuccess);
-    } else {
-      mPromise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
-    }
-
-    return NS_OK;
-  }
-
-private:
-  ~UnsubscribeResultCallback()
-  {}
-
-  RefPtr<Promise> mPromise;
-};
-
-NS_IMPL_ISUPPORTS(UnsubscribeResultCallback, nsIUnsubscribeResultCallback)
-
-already_AddRefed<Promise>
-PushSubscription::Unsubscribe(ErrorResult& aRv)
-{
-  MOZ_ASSERT(mPrincipal);
-
-  nsCOMPtr<nsIPushService> service =
-    do_GetService("@mozilla.org/push/Service;1");
-  if (NS_WARN_IF(!service)) {
-    aRv = NS_ERROR_FAILURE;
-    return nullptr;
-  }
-
-  RefPtr<Promise> p = Promise::Create(mGlobal, aRv);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
-  RefPtr<UnsubscribeResultCallback> callback =
-    new UnsubscribeResultCallback(p);
-  Unused << NS_WARN_IF(NS_FAILED(
-    service->Unsubscribe(mScope, mPrincipal, callback)));
-  return p.forget();
-}
-
-void
-PushSubscription::ToJSON(PushSubscriptionJSON& aJSON)
-{
-  SubscriptionToJSON(aJSON, mEndpoint, mRawP256dhKey, mAuthSecret);
-}
-
-PushSubscription::PushSubscription(nsIGlobalObject* aGlobal,
-                                   const nsAString& aEndpoint,
-                                   const nsAString& aScope,
-                                   const nsTArray<uint8_t>& aRawP256dhKey,
-                                   const nsTArray<uint8_t>& aAuthSecret)
-  : mGlobal(aGlobal)
-  , mEndpoint(aEndpoint)
-  , mScope(aScope)
-  , mRawP256dhKey(aRawP256dhKey)
-  , mAuthSecret(aAuthSecret)
-{
-}
-
-PushSubscription::~PushSubscription()
-{
-}
-
-JSObject*
-PushSubscription::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return PushSubscriptionBinding::Wrap(aCx, this, aGivenProto);
-}
-
-void
-PushSubscription::GetKey(JSContext* aCx,
-                         PushEncryptionKeyName aType,
-                         JS::MutableHandle<JSObject*> aKey)
-{
-  if (aType == PushEncryptionKeyName::P256dh && !mRawP256dhKey.IsEmpty()) {
-    aKey.set(ArrayBuffer::Create(aCx,
-                                 mRawP256dhKey.Length(),
-                                 mRawP256dhKey.Elements()));
-  } else if (aType == PushEncryptionKeyName::Auth && !mAuthSecret.IsEmpty()) {
-    aKey.set(ArrayBuffer::Create(aCx,
-                                 mAuthSecret.Length(),
-                                 mAuthSecret.Elements()));
-  } else {
-    aKey.set(nullptr);
-  }
-}
-
-void
-PushSubscription::SetPrincipal(nsIPrincipal* aPrincipal)
-{
-  MOZ_ASSERT(!mPrincipal);
-  mPrincipal = aPrincipal;
-}
-
-// static
-already_AddRefed<PushSubscription>
-PushSubscription::Constructor(GlobalObject& aGlobal,
-                              const nsAString& aEndpoint,
-                              const nsAString& aScope,
-                              const Nullable<ArrayBuffer>& aP256dhKey,
-                              const Nullable<ArrayBuffer>& aAuthSecret,
-                              ErrorResult& aRv)
-{
-  MOZ_ASSERT(!aEndpoint.IsEmpty());
-  MOZ_ASSERT(!aScope.IsEmpty());
-
-  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
-
-  nsTArray<uint8_t> rawKey;
-  if (!aP256dhKey.IsNull()) {
-    const ArrayBuffer& key = aP256dhKey.Value();
-    key.ComputeLengthAndData();
-    rawKey.InsertElementsAt(0, key.Data(), key.Length());
-  }
-
-  nsTArray<uint8_t> authSecret;
-  if (!aAuthSecret.IsNull()) {
-    const ArrayBuffer& sekrit = aAuthSecret.Value();
-    sekrit.ComputeLengthAndData();
-    authSecret.InsertElementsAt(0, sekrit.Data(), sekrit.Length());
-  }
-  RefPtr<PushSubscription> sub = new PushSubscription(global,
-                                                      aEndpoint,
-                                                      aScope,
-                                                      rawKey,
-                                                      authSecret);
-
-  return sub.forget();
-}
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushSubscription, mGlobal, mPrincipal)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(PushSubscription)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(PushSubscription)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushSubscription)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
 PushManager::PushManager(nsIGlobalObject* aGlobal, const nsAString& aScope)
   : mGlobal(aGlobal), mScope(aScope)
 {
   AssertIsOnMainThread();
 }
 
 PushManager::~PushManager()
 {}
@@ -293,269 +118,16 @@ PushManager::PermissionState(ErrorResult
 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
 
-// WorkerPushSubscription
-
-WorkerPushSubscription::WorkerPushSubscription(const nsAString& aEndpoint,
-                                               const nsAString& aScope,
-                                               const nsTArray<uint8_t>& aRawP256dhKey,
-                                               const nsTArray<uint8_t>& aAuthSecret)
-  : mEndpoint(aEndpoint)
-  , mScope(aScope)
-  , mRawP256dhKey(aRawP256dhKey)
-  , mAuthSecret(aAuthSecret)
-{
-  MOZ_ASSERT(!aScope.IsEmpty());
-  MOZ_ASSERT(!aEndpoint.IsEmpty());
-}
-
-WorkerPushSubscription::~WorkerPushSubscription()
-{}
-
-JSObject*
-WorkerPushSubscription::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return PushSubscriptionBinding_workers::Wrap(aCx, this, aGivenProto);
-}
-
-// static
-already_AddRefed<WorkerPushSubscription>
-WorkerPushSubscription::Constructor(GlobalObject& aGlobal,
-                                    const nsAString& aEndpoint,
-                                    const nsAString& aScope,
-                                    const Nullable<ArrayBuffer>& aP256dhKey,
-                                    const Nullable<ArrayBuffer>& aAuthSecret,
-                                    ErrorResult& aRv)
-{
-  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
-  MOZ_ASSERT(worker);
-  worker->AssertIsOnWorkerThread();
-
-  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> 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<WorkerPushSubscription> sub = new WorkerPushSubscription(aEndpoint,
-                                                                  aScope,
-                                                                  rawKey,
-                                                                  authSecret);
-
-  return sub.forget();
-}
-
-void
-WorkerPushSubscription::GetKey(JSContext* aCx,
-                               PushEncryptionKeyName aType,
-                               JS::MutableHandle<JSObject*> aKey)
-{
-  if (aType == mozilla::dom::PushEncryptionKeyName::P256dh &&
-      !mRawP256dhKey.IsEmpty()) {
-    aKey.set(ArrayBuffer::Create(aCx,
-                                 mRawP256dhKey.Length(),
-                                 mRawP256dhKey.Elements()));
-  } else if (aType == mozilla::dom::PushEncryptionKeyName::Auth &&
-             !mAuthSecret.IsEmpty()) {
-    aKey.set(ArrayBuffer::Create(aCx,
-                                 mAuthSecret.Length(),
-                                 mAuthSecret.Elements()));
-  } else {
-    aKey.set(nullptr);
-  }
-}
-
-class UnsubscribeResultRunnable final : public WorkerRunnable
-{
-public:
-  UnsubscribeResultRunnable(PromiseWorkerProxy* aProxy,
-                            nsresult aStatus,
-                            bool aSuccess)
-    : WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
-    , mProxy(aProxy)
-    , mStatus(aStatus)
-    , mSuccess(aSuccess)
-  {
-    AssertIsOnMainThread();
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    aWorkerPrivate->AssertIsOnWorkerThread();
-
-    RefPtr<Promise> promise = mProxy->WorkerPromise();
-    if (NS_SUCCEEDED(mStatus)) {
-      promise->MaybeResolve(mSuccess);
-    } else {
-      promise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
-    }
-
-    mProxy->CleanUp();
-    return true;
-  }
-private:
-  ~UnsubscribeResultRunnable()
-  {}
-
-  RefPtr<PromiseWorkerProxy> mProxy;
-  nsresult mStatus;
-  bool mSuccess;
-};
-
-class WorkerUnsubscribeResultCallback final : public nsIUnsubscribeResultCallback
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  explicit WorkerUnsubscribeResultCallback(PromiseWorkerProxy* aProxy)
-    : mProxy(aProxy)
-  {
-    AssertIsOnMainThread();
-  }
-
-  NS_IMETHOD
-  OnUnsubscribe(nsresult aStatus, bool aSuccess) override
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(mProxy, "OnUnsubscribe() called twice?");
-
-    RefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
-
-    MutexAutoLock lock(proxy->Lock());
-    if (proxy->CleanedUp()) {
-      return NS_OK;
-    }
-
-    RefPtr<UnsubscribeResultRunnable> r =
-      new UnsubscribeResultRunnable(proxy, aStatus, aSuccess);
-    r->Dispatch();
-    return NS_OK;
-  }
-
-private:
-  ~WorkerUnsubscribeResultCallback()
-  {
-  }
-
-  RefPtr<PromiseWorkerProxy> mProxy;
-};
-
-NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback)
-
-class UnsubscribeRunnable final : public nsRunnable
-{
-public:
-  UnsubscribeRunnable(PromiseWorkerProxy* aProxy,
-                      const nsAString& aScope)
-    : mProxy(aProxy)
-    , mScope(aScope)
-  {
-    MOZ_ASSERT(aProxy);
-    MOZ_ASSERT(!aScope.IsEmpty());
-  }
-
-  NS_IMETHOD
-  Run() override
-  {
-    AssertIsOnMainThread();
-
-    nsCOMPtr<nsIPrincipal> principal;
-    {
-      MutexAutoLock lock(mProxy->Lock());
-      if (mProxy->CleanedUp()) {
-        return NS_OK;
-      }
-      principal = mProxy->GetWorkerPrivate()->GetPrincipal();
-    }
-    MOZ_ASSERT(principal);
-
-    RefPtr<WorkerUnsubscribeResultCallback> callback =
-      new WorkerUnsubscribeResultCallback(mProxy);
-
-    nsCOMPtr<nsIPushService> service =
-      do_GetService("@mozilla.org/push/Service;1");
-    if (!service) {
-      callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
-      return NS_OK;
-    }
-
-    if (NS_WARN_IF(NS_FAILED(service->Unsubscribe(mScope, principal, callback)))) {
-      callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
-      return NS_OK;
-    }
-    return NS_OK;
-  }
-
-private:
-  ~UnsubscribeRunnable()
-  {}
-
-  RefPtr<PromiseWorkerProxy> mProxy;
-  nsString mScope;
-};
-
-already_AddRefed<Promise>
-WorkerPushSubscription::Unsubscribe(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_SERVICE_UNREACHABLE);
-    return p.forget();
-  }
-
-  RefPtr<UnsubscribeRunnable> r =
-    new UnsubscribeRunnable(proxy, mScope);
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
-
-  return p.forget();
-}
-
-void
-WorkerPushSubscription::ToJSON(PushSubscriptionJSON& aJSON)
-{
-  SubscriptionToJSON(aJSON, mEndpoint, mRawP256dhKey, mAuthSecret);
-}
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerPushSubscription)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(WorkerPushSubscription)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(WorkerPushSubscription)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerPushSubscription)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
 // WorkerPushManager
 
 WorkerPushManager::WorkerPushManager(const nsAString& aScope)
   : mScope(aScope)
 {
 }
 
 JSObject*
@@ -585,19 +157,19 @@ public:
   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<WorkerPushSubscription> sub =
-            new WorkerPushSubscription(mEndpoint, mScope,
-                                       mRawP256dhKey, mAuthSecret);
+        RefPtr<PushSubscription> sub =
+            new PushSubscription(nullptr, mEndpoint, mScope, mRawP256dhKey,
+                                 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);
     }
 
--- a/dom/push/PushManager.h
+++ b/dom/push/PushManager.h
@@ -40,89 +40,27 @@
 
 #include "nsCOMPtr.h"
 #include "mozilla/RefPtr.h"
 #include "jsapi.h"
 
 class nsIGlobalObject;
 class nsIPrincipal;
 
-#include "mozilla/dom/PushSubscriptionBinding.h"
-
 namespace mozilla {
 namespace dom {
 
 namespace workers {
 class WorkerPrivate;
 }
 
 class Promise;
 class PushManagerImpl;
 
-class PushSubscription final : public nsISupports
-                             , public nsWrapperCache
-{
-public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushSubscription)
 
-  explicit PushSubscription(nsIGlobalObject* aGlobal,
-                            const nsAString& aEndpoint,
-                            const nsAString& aScope,
-                            const nsTArray<uint8_t>& aP256dhKey,
-                            const nsTArray<uint8_t>& aAuthSecret);
-
-  JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
-  nsIGlobalObject*
-  GetParentObject() const
-  {
-    return mGlobal;
-  }
-
-  void
-  GetEndpoint(nsAString& aEndpoint) const
-  {
-    aEndpoint = mEndpoint;
-  }
-
-  void
-  GetKey(JSContext* cx,
-         PushEncryptionKeyName aType,
-         JS::MutableHandle<JSObject*> aKey);
-
-  static already_AddRefed<PushSubscription>
-  Constructor(GlobalObject& aGlobal,
-              const nsAString& aEndpoint,
-              const nsAString& aScope,
-              const Nullable<ArrayBuffer>& aP256dhKey,
-              const Nullable<ArrayBuffer>& aAuthSecret,
-              ErrorResult& aRv);
-
-  void
-  SetPrincipal(nsIPrincipal* aPrincipal);
-
-  already_AddRefed<Promise>
-  Unsubscribe(ErrorResult& aRv);
-
-  void
-  ToJSON(PushSubscriptionJSON& aJSON);
-
-protected:
-  ~PushSubscription();
-
-private:
-  nsCOMPtr<nsIGlobalObject> mGlobal;
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsString mEndpoint;
-  nsString mScope;
-  nsTArray<uint8_t> mRawP256dhKey;
-  nsTArray<uint8_t> mAuthSecret;
-};
 
 class PushManager final : public nsISupports
                         , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushManager)
 
@@ -153,71 +91,16 @@ protected:
   ~PushManager();
 
 private:
   nsCOMPtr<nsIGlobalObject> mGlobal;
   RefPtr<PushManagerImpl> mImpl;
   nsString mScope;
 };
 
-class WorkerPushSubscription final : public nsISupports
-                                   , public nsWrapperCache
-{
-public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WorkerPushSubscription)
-
-  explicit WorkerPushSubscription(const nsAString& aEndpoint,
-                                  const nsAString& aScope,
-                                  const nsTArray<uint8_t>& aRawP256dhKey,
-                                  const nsTArray<uint8_t>& aAuthSecret);
-
-  nsIGlobalObject*
-  GetParentObject() const
-  {
-    return nullptr;
-  }
-
-  JSObject*
-  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
-  static already_AddRefed<WorkerPushSubscription>
-  Constructor(GlobalObject& aGlobal,
-              const nsAString& aEndpoint,
-              const nsAString& aScope,
-              const Nullable<ArrayBuffer>& aP256dhKey,
-              const Nullable<ArrayBuffer>& aAuthSecret,
-              ErrorResult& aRv);
-
-  void
-  GetEndpoint(nsAString& aEndpoint) const
-  {
-    aEndpoint = mEndpoint;
-  }
-
-  void
-  GetKey(JSContext* cx, PushEncryptionKeyName aType,
-         JS::MutableHandle<JSObject*> aP256dhKey);
-
-  already_AddRefed<Promise>
-  Unsubscribe(ErrorResult& aRv);
-
-  void
-  ToJSON(PushSubscriptionJSON& aJSON);
-
-protected:
-  ~WorkerPushSubscription();
-
-private:
-  nsString mEndpoint;
-  nsString mScope;
-  nsTArray<uint8_t> mRawP256dhKey;
-  nsTArray<uint8_t> mAuthSecret;
-};
-
 class WorkerPushManager final : public nsISupports
                               , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WorkerPushManager)
 
   enum SubscriptionAction {
new file mode 100644
--- /dev/null
+++ b/dom/push/PushSubscription.cpp
@@ -0,0 +1,372 @@
+/* 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/PushSubscription.h"
+
+#include "nsIPushService.h"
+#include "nsIScriptObjectPrincipal.h"
+
+#include "mozilla/Base64.h"
+#include "mozilla/unused.h"
+
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseWorkerProxy.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerScope.h"
+#include "mozilla/dom/workers/Workers.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace workers;
+
+namespace {
+
+class UnsubscribeResultCallback final : public nsIUnsubscribeResultCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit UnsubscribeResultCallback(Promise* aPromise)
+    : mPromise(aPromise)
+  {
+    AssertIsOnMainThread();
+  }
+
+  NS_IMETHOD
+  OnUnsubscribe(nsresult aStatus, bool aSuccess) override
+  {
+    if (NS_SUCCEEDED(aStatus)) {
+      mPromise->MaybeResolve(aSuccess);
+    } else {
+      mPromise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
+    }
+
+    return NS_OK;
+  }
+
+private:
+  ~UnsubscribeResultCallback()
+  {}
+
+  RefPtr<Promise> mPromise;
+};
+
+NS_IMPL_ISUPPORTS(UnsubscribeResultCallback, nsIUnsubscribeResultCallback)
+
+class UnsubscribeResultRunnable final : public WorkerRunnable
+{
+public:
+  UnsubscribeResultRunnable(WorkerPrivate* aWorkerPrivate,
+                            already_AddRefed<PromiseWorkerProxy>&& aProxy,
+                            nsresult aStatus,
+                            bool aSuccess)
+    : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+    , mProxy(Move(aProxy))
+    , mStatus(aStatus)
+    , mSuccess(aSuccess)
+  {
+    AssertIsOnMainThread();
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    RefPtr<Promise> promise = mProxy->WorkerPromise();
+    if (NS_SUCCEEDED(mStatus)) {
+      promise->MaybeResolve(mSuccess);
+    } else {
+      promise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
+    }
+
+    mProxy->CleanUp();
+
+    return true;
+  }
+private:
+  ~UnsubscribeResultRunnable()
+  {}
+
+  RefPtr<PromiseWorkerProxy> mProxy;
+  nsresult mStatus;
+  bool mSuccess;
+};
+
+class WorkerUnsubscribeResultCallback final : public nsIUnsubscribeResultCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  explicit WorkerUnsubscribeResultCallback(PromiseWorkerProxy* aProxy)
+    : mProxy(aProxy)
+  {
+    AssertIsOnMainThread();
+  }
+
+  NS_IMETHOD
+  OnUnsubscribe(nsresult aStatus, bool aSuccess) override
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(mProxy, "OnUnsubscribe() called twice?");
+
+    MutexAutoLock lock(mProxy->Lock());
+    if (mProxy->CleanedUp()) {
+      return NS_OK;
+    }
+
+    WorkerPrivate* worker = mProxy->GetWorkerPrivate();
+    RefPtr<UnsubscribeResultRunnable> r =
+      new UnsubscribeResultRunnable(worker, mProxy.forget(), aStatus, aSuccess);
+    MOZ_ALWAYS_TRUE(r->Dispatch());
+
+    return NS_OK;
+  }
+
+private:
+  ~WorkerUnsubscribeResultCallback()
+  {
+  }
+
+  RefPtr<PromiseWorkerProxy> mProxy;
+};
+
+NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback)
+
+class UnsubscribeRunnable final : public nsRunnable
+{
+public:
+  UnsubscribeRunnable(PromiseWorkerProxy* aProxy,
+                      const nsAString& aScope)
+    : mProxy(aProxy)
+    , mScope(aScope)
+  {
+    MOZ_ASSERT(aProxy);
+    MOZ_ASSERT(!aScope.IsEmpty());
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    AssertIsOnMainThread();
+
+    nsCOMPtr<nsIPrincipal> principal;
+
+    {
+      MutexAutoLock lock(mProxy->Lock());
+      if (mProxy->CleanedUp()) {
+        return NS_OK;
+      }
+      principal = mProxy->GetWorkerPrivate()->GetPrincipal();
+    }
+
+    MOZ_ASSERT(principal);
+
+    RefPtr<WorkerUnsubscribeResultCallback> callback =
+      new WorkerUnsubscribeResultCallback(mProxy);
+
+    nsCOMPtr<nsIPushService> service =
+      do_GetService("@mozilla.org/push/Service;1");
+    if (NS_WARN_IF(!service)) {
+      callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
+      return NS_OK;
+    }
+
+    if (NS_WARN_IF(NS_FAILED(service->Unsubscribe(mScope, principal, callback)))) {
+      callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
+      return NS_OK;
+    }
+
+    return NS_OK;
+  }
+
+private:
+  ~UnsubscribeRunnable()
+  {}
+
+  RefPtr<PromiseWorkerProxy> mProxy;
+  nsString mScope;
+};
+
+} // anonymous namespace
+
+PushSubscription::PushSubscription(nsIGlobalObject* aGlobal,
+                                   const nsAString& aEndpoint,
+                                   const nsAString& aScope,
+                                   const nsTArray<uint8_t>& aRawP256dhKey,
+                                   const nsTArray<uint8_t>& aAuthSecret)
+  : mEndpoint(aEndpoint)
+  , mScope(aScope)
+  , mRawP256dhKey(aRawP256dhKey)
+  , mAuthSecret(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();
+    MOZ_ASSERT(worker);
+    worker->AssertIsOnWorkerThread();
+#endif
+  }
+}
+
+PushSubscription::~PushSubscription()
+{}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushSubscription, mGlobal)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(PushSubscription)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(PushSubscription)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushSubscription)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+JSObject*
+PushSubscription::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return PushSubscriptionBinding::Wrap(aCx, this, aGivenProto);
+}
+
+// static
+already_AddRefed<PushSubscription>
+PushSubscription::Constructor(GlobalObject& aGlobal,
+                              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> 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);
+
+  return sub.forget();
+}
+
+already_AddRefed<Promise>
+PushSubscription::Unsubscribe(ErrorResult& aRv)
+{
+  if (!NS_IsMainThread()) {
+    RefPtr<Promise> p = UnsubscribeFromWorker(aRv);
+    return p.forget();
+  }
+
+  MOZ_ASSERT(mGlobal);
+
+  nsCOMPtr<nsIPushService> service =
+    do_GetService("@mozilla.org/push/Service;1");
+  if (NS_WARN_IF(!service)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mGlobal);
+  if (!sop) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  RefPtr<Promise> p = Promise::Create(mGlobal, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  RefPtr<UnsubscribeResultCallback> callback =
+    new UnsubscribeResultCallback(p);
+  Unused << NS_WARN_IF(NS_FAILED(
+    service->Unsubscribe(mScope, sop->GetPrincipal(), callback)));
+
+  return p.forget();
+}
+
+void
+PushSubscription::GetKey(JSContext* aCx,
+                         PushEncryptionKeyName aType,
+                         JS::MutableHandle<JSObject*> aKey)
+{
+  if (aType == PushEncryptionKeyName::P256dh && !mRawP256dhKey.IsEmpty()) {
+    aKey.set(ArrayBuffer::Create(aCx,
+                                 mRawP256dhKey.Length(),
+                                 mRawP256dhKey.Elements()));
+  } else if (aType == PushEncryptionKeyName::Auth && !mAuthSecret.IsEmpty()) {
+    aKey.set(ArrayBuffer::Create(aCx,
+                                 mAuthSecret.Length(),
+                                 mAuthSecret.Elements()));
+  } else {
+    aKey.set(nullptr);
+  }
+}
+
+void
+PushSubscription::ToJSON(PushSubscriptionJSON& aJSON)
+{
+  aJSON.mEndpoint.Construct();
+  aJSON.mEndpoint.Value() = mEndpoint;
+
+  aJSON.mKeys.mP256dh.Construct();
+  nsresult rv = Base64URLEncode(mRawP256dhKey.Length(),
+                                mRawP256dhKey.Elements(),
+                                aJSON.mKeys.mP256dh.Value());
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+
+  aJSON.mKeys.mAuth.Construct();
+  rv = Base64URLEncode(mAuthSecret.Length(), mAuthSecret.Elements(),
+                       aJSON.mKeys.mAuth.Value());
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+}
+
+already_AddRefed<Promise>
+PushSubscription::UnsubscribeFromWorker(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_SERVICE_UNREACHABLE);
+    return p.forget();
+  }
+
+  RefPtr<UnsubscribeRunnable> r =
+    new UnsubscribeRunnable(proxy, mScope);
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+
+  return p.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/push/PushSubscription.h
@@ -0,0 +1,95 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_PushSubscription_h
+#define mozilla_dom_PushSubscription_h
+
+#include "jsapi.h"
+#include "nsCOMPtr.h"
+#include "nsWrapperCache.h"
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/RefPtr.h"
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/PushSubscriptionBinding.h"
+#include "mozilla/dom/TypedArray.h"
+
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace dom {
+
+namespace workers {
+class WorkerPrivate;
+}
+
+class Promise;
+
+class PushSubscription final : public nsISupports
+                             , public nsWrapperCache
+{
+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);
+
+  JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+  nsIGlobalObject*
+  GetParentObject() const
+  {
+    return mGlobal;
+  }
+
+  void
+  GetEndpoint(nsAString& aEndpoint) const
+  {
+    aEndpoint = mEndpoint;
+  }
+
+  void
+  GetKey(JSContext* cx,
+         PushEncryptionKeyName aType,
+         JS::MutableHandle<JSObject*> aKey);
+
+  static already_AddRefed<PushSubscription>
+  Constructor(GlobalObject& aGlobal,
+              const nsAString& aEndpoint,
+              const nsAString& aScope,
+              const Nullable<ArrayBuffer>& aP256dhKey,
+              const Nullable<ArrayBuffer>& aAuthSecret,
+              ErrorResult& aRv);
+
+  already_AddRefed<Promise>
+  Unsubscribe(ErrorResult& aRv);
+
+  void
+  ToJSON(PushSubscriptionJSON& aJSON);
+
+protected:
+  ~PushSubscription();
+
+private:
+  already_AddRefed<Promise>
+  UnsubscribeFromWorker(ErrorResult& aRv);
+
+  nsString mEndpoint;
+  nsString mScope;
+  nsTArray<uint8_t> mRawP256dhKey;
+  nsTArray<uint8_t> mAuthSecret;
+  nsCOMPtr<nsIGlobalObject> mGlobal;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_PushSubscription_h
--- a/dom/push/moz.build
+++ b/dom/push/moz.build
@@ -34,21 +34,23 @@ MOCHITEST_MANIFESTS += [
 
 XPCSHELL_TESTS_MANIFESTS += [
     'test/xpcshell/xpcshell.ini',
 ]
 
 EXPORTS.mozilla.dom += [
     'PushManager.h',
     'PushNotifier.h',
+    'PushSubscription.h',
 ]
 
 UNIFIED_SOURCES += [
     'PushManager.cpp',
     'PushNotifier.cpp',
+    'PushSubscription.cpp',
 ]
 
 TEST_DIRS += ['test/xpcshell']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']
--- a/dom/webidl/PushSubscription.webidl
+++ b/dom/webidl/PushSubscription.webidl
@@ -34,13 +34,9 @@ interface PushSubscription
 {
     readonly attribute USVString endpoint;
     ArrayBuffer? getKey(PushEncryptionKeyName name);
     [Throws, UseCounter]
     Promise<boolean> unsubscribe();
 
     // Implements the custom serializer specified in Push API, section 9.
     PushSubscriptionJSON toJSON();
-
-    // Used to set the principal from the JS implemented PushManager.
-    [Exposed=Window,ChromeOnly]
-    void setPrincipal(Principal principal);
 };