forked from mirrors/gecko-dev
		
	 7c764ee1da
			
		
	
	
		7c764ee1da
		
	
	
	
	
		
			
			Depends on D66025 Differential Revision: https://phabricator.services.mozilla.com/D66027 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			358 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			358 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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/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/PushSubscriptionOptions.h"
 | |
| #include "mozilla/dom/PushUtil.h"
 | |
| #include "mozilla/dom/WorkerCommon.h"
 | |
| #include "mozilla/dom/WorkerPrivate.h"
 | |
| #include "mozilla/dom/WorkerScope.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace dom {
 | |
| 
 | |
| 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() = default;
 | |
| 
 | |
|   RefPtr<Promise> mPromise;
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(UnsubscribeResultCallback, nsIUnsubscribeResultCallback)
 | |
| 
 | |
| class UnsubscribeResultRunnable final : public WorkerRunnable {
 | |
|  public:
 | |
|   UnsubscribeResultRunnable(WorkerPrivate* aWorkerPrivate,
 | |
|                             RefPtr<PromiseWorkerProxy>&& aProxy,
 | |
|                             nsresult aStatus, bool aSuccess)
 | |
|       : WorkerRunnable(aWorkerPrivate),
 | |
|         mProxy(std::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() = default;
 | |
| 
 | |
|   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, std::move(mProxy), aStatus, aSuccess);
 | |
|     MOZ_ALWAYS_TRUE(r->Dispatch());
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ~WorkerUnsubscribeResultCallback() = default;
 | |
| 
 | |
|   RefPtr<PromiseWorkerProxy> mProxy;
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback)
 | |
| 
 | |
| class UnsubscribeRunnable final : public Runnable {
 | |
|  public:
 | |
|   UnsubscribeRunnable(PromiseWorkerProxy* aProxy, const nsAString& aScope)
 | |
|       : Runnable("dom::UnsubscribeRunnable"), 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() = default;
 | |
| 
 | |
|   RefPtr<PromiseWorkerProxy> mProxy;
 | |
|   nsString mScope;
 | |
| };
 | |
| 
 | |
| }  // anonymous namespace
 | |
| 
 | |
| PushSubscription::PushSubscription(nsIGlobalObject* aGlobal,
 | |
|                                    const nsAString& aEndpoint,
 | |
|                                    const nsAString& aScope,
 | |
|                                    nsTArray<uint8_t>&& aRawP256dhKey,
 | |
|                                    nsTArray<uint8_t>&& aAuthSecret,
 | |
|                                    nsTArray<uint8_t>&& aAppServerKey)
 | |
|     : mEndpoint(aEndpoint),
 | |
|       mScope(aScope),
 | |
|       mRawP256dhKey(std::move(aRawP256dhKey)),
 | |
|       mAuthSecret(std::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();
 | |
|     MOZ_ASSERT(worker);
 | |
|     worker->AssertIsOnWorkerThread();
 | |
| #endif
 | |
|   }
 | |
|   mOptions = new PushSubscriptionOptions(mGlobal, std::move(aAppServerKey));
 | |
| }
 | |
| 
 | |
| PushSubscription::~PushSubscription() = default;
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushSubscription, mGlobal, mOptions)
 | |
| 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 PushSubscription_Binding::Wrap(aCx, this, aGivenProto);
 | |
| }
 | |
| 
 | |
| // static
 | |
| already_AddRefed<PushSubscription> PushSubscription::Constructor(
 | |
|     GlobalObject& aGlobal, const PushSubscriptionInit& aInitDict,
 | |
|     ErrorResult& aRv) {
 | |
|   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
 | |
| 
 | |
|   nsTArray<uint8_t> rawKey;
 | |
|   if (aInitDict.mP256dhKey.WasPassed() &&
 | |
|       !aInitDict.mP256dhKey.Value().IsNull() &&
 | |
|       !PushUtil::CopyArrayBufferToArray(aInitDict.mP256dhKey.Value().Value(),
 | |
|                                         rawKey)) {
 | |
|     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   nsTArray<uint8_t> authSecret;
 | |
|   if (aInitDict.mAuthSecret.WasPassed() &&
 | |
|       !aInitDict.mAuthSecret.Value().IsNull() &&
 | |
|       !PushUtil::CopyArrayBufferToArray(aInitDict.mAuthSecret.Value().Value(),
 | |
|                                         authSecret)) {
 | |
|     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   nsTArray<uint8_t> appServerKey;
 | |
|   if (aInitDict.mAppServerKey.WasPassed() &&
 | |
|       !aInitDict.mAppServerKey.Value().IsNull()) {
 | |
|     const OwningArrayBufferViewOrArrayBuffer& bufferSource =
 | |
|         aInitDict.mAppServerKey.Value().Value();
 | |
|     if (!PushUtil::CopyBufferSourceToArray(bufferSource, appServerKey)) {
 | |
|       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
 | |
|       return nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   RefPtr<PushSubscription> sub = new PushSubscription(
 | |
|       global, aInitDict.mEndpoint, aInitDict.mScope, std::move(rawKey),
 | |
|       std::move(authSecret), std::move(appServerKey));
 | |
| 
 | |
|   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,
 | |
|                               ErrorResult& aRv) {
 | |
|   if (aType == PushEncryptionKeyName::P256dh) {
 | |
|     PushUtil::CopyArrayToArrayBuffer(aCx, mRawP256dhKey, aKey, aRv);
 | |
|   } else if (aType == PushEncryptionKeyName::Auth) {
 | |
|     PushUtil::CopyArrayToArrayBuffer(aCx, mAuthSecret, aKey, aRv);
 | |
|   } else {
 | |
|     aKey.set(nullptr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void PushSubscription::ToJSON(PushSubscriptionJSON& aJSON, ErrorResult& aRv) {
 | |
|   aJSON.mEndpoint.Construct();
 | |
|   aJSON.mEndpoint.Value() = mEndpoint;
 | |
| 
 | |
|   aJSON.mKeys.mP256dh.Construct();
 | |
|   nsresult rv = Base64URLEncode(
 | |
|       mRawP256dhKey.Length(), mRawP256dhKey.Elements(),
 | |
|       Base64URLEncodePaddingPolicy::Omit, aJSON.mKeys.mP256dh.Value());
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     aRv.Throw(rv);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   aJSON.mKeys.mAuth.Construct();
 | |
|   rv = Base64URLEncode(mAuthSecret.Length(), mAuthSecret.Elements(),
 | |
|                        Base64URLEncodePaddingPolicy::Omit,
 | |
|                        aJSON.mKeys.mAuth.Value());
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     aRv.Throw(rv);
 | |
|     return;
 | |
|   }
 | |
| }
 | |
| 
 | |
| already_AddRefed<PushSubscriptionOptions> PushSubscription::Options() {
 | |
|   RefPtr<PushSubscriptionOptions> options = mOptions;
 | |
|   return options.forget();
 | |
| }
 | |
| 
 | |
| 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
 |