forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			527 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			527 lines
		
	
	
	
		
			16 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/PushManager.h"
 | |
| 
 | |
| #include "mozilla/Base64.h"
 | |
| #include "mozilla/Preferences.h"
 | |
| #include "mozilla/Components.h"
 | |
| #include "mozilla/Unused.h"
 | |
| #include "mozilla/dom/PushManagerBinding.h"
 | |
| #include "mozilla/dom/PushSubscription.h"
 | |
| #include "mozilla/dom/PushSubscriptionOptionsBinding.h"
 | |
| #include "mozilla/dom/PushUtil.h"
 | |
| #include "mozilla/dom/WorkerRunnable.h"
 | |
| #include "mozilla/dom/WorkerPrivate.h"
 | |
| #include "mozilla/dom/WorkerScope.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 "nsContentUtils.h"
 | |
| #include "nsServiceManagerUtils.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace dom {
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| nsresult GetPermissionState(nsIPrincipal* aPrincipal,
 | |
|                             PushPermissionState& aState) {
 | |
|   nsCOMPtr<nsIPermissionManager> permManager =
 | |
|       mozilla::components::PermissionManager::Service();
 | |
| 
 | |
|   if (!permManager) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
 | |
|   nsresult rv = permManager->TestExactPermissionFromPrincipal(
 | |
|       aPrincipal, "desktop-notification"_ns, &permission);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   if (permission == nsIPermissionManager::ALLOW_ACTION ||
 | |
|       Preferences::GetBool("dom.push.testing.ignorePermission", false)) {
 | |
|     aState = PushPermissionState::Granted;
 | |
|   } else if (permission == nsIPermissionManager::DENY_ACTION) {
 | |
|     aState = PushPermissionState::Denied;
 | |
|   } else {
 | |
|     aState = PushPermissionState::Prompt;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult GetSubscriptionParams(nsIPushSubscription* aSubscription,
 | |
|                                nsAString& aEndpoint,
 | |
|                                nsTArray<uint8_t>& aRawP256dhKey,
 | |
|                                nsTArray<uint8_t>& aAuthSecret,
 | |
|                                nsTArray<uint8_t>& aAppServerKey) {
 | |
|   if (!aSubscription) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   nsresult rv = aSubscription->GetEndpoint(aEndpoint);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   rv = aSubscription->GetKey(u"p256dh"_ns, aRawP256dhKey);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
|   rv = aSubscription->GetKey(u"auth"_ns, aAuthSecret);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
|   rv = aSubscription->GetKey(u"appServer"_ns, aAppServerKey);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| class GetSubscriptionResultRunnable final : public WorkerRunnable {
 | |
|  public:
 | |
|   GetSubscriptionResultRunnable(WorkerPrivate* aWorkerPrivate,
 | |
|                                 RefPtr<PromiseWorkerProxy>&& aProxy,
 | |
|                                 nsresult aStatus, const nsAString& aEndpoint,
 | |
|                                 const nsAString& aScope,
 | |
|                                 nsTArray<uint8_t>&& aRawP256dhKey,
 | |
|                                 nsTArray<uint8_t>&& aAuthSecret,
 | |
|                                 nsTArray<uint8_t>&& aAppServerKey)
 | |
|       : WorkerRunnable(aWorkerPrivate),
 | |
|         mProxy(std::move(aProxy)),
 | |
|         mStatus(aStatus),
 | |
|         mEndpoint(aEndpoint),
 | |
|         mScope(aScope),
 | |
|         mRawP256dhKey(std::move(aRawP256dhKey)),
 | |
|         mAuthSecret(std::move(aAuthSecret)),
 | |
|         mAppServerKey(std::move(aAppServerKey)) {}
 | |
| 
 | |
|   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, std::move(mRawP256dhKey),
 | |
|             std::move(mAuthSecret), std::move(mAppServerKey));
 | |
|         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() = default;
 | |
| 
 | |
|   RefPtr<PromiseWorkerProxy> mProxy;
 | |
|   nsresult mStatus;
 | |
|   nsString mEndpoint;
 | |
|   nsString mScope;
 | |
|   nsTArray<uint8_t> mRawP256dhKey;
 | |
|   nsTArray<uint8_t> mAuthSecret;
 | |
|   nsTArray<uint8_t> mAppServerKey;
 | |
| };
 | |
| 
 | |
| class GetSubscriptionCallback final : public nsIPushSubscriptionCallback {
 | |
|  public:
 | |
|   NS_DECL_ISUPPORTS
 | |
| 
 | |
|   explicit GetSubscriptionCallback(PromiseWorkerProxy* aProxy,
 | |
|                                    const nsAString& aScope)
 | |
|       : mProxy(aProxy), mScope(aScope) {}
 | |
| 
 | |
|   NS_IMETHOD
 | |
|   OnPushSubscription(nsresult aStatus,
 | |
|                      nsIPushSubscription* aSubscription) override {
 | |
|     AssertIsOnMainThread();
 | |
|     MOZ_ASSERT(mProxy, "OnPushSubscription() called twice?");
 | |
| 
 | |
|     MutexAutoLock lock(mProxy->Lock());
 | |
|     if (mProxy->CleanedUp()) {
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     nsAutoString endpoint;
 | |
|     nsTArray<uint8_t> rawP256dhKey, authSecret, appServerKey;
 | |
|     if (NS_SUCCEEDED(aStatus)) {
 | |
|       aStatus = GetSubscriptionParams(aSubscription, endpoint, rawP256dhKey,
 | |
|                                       authSecret, appServerKey);
 | |
|     }
 | |
| 
 | |
|     WorkerPrivate* worker = mProxy->GetWorkerPrivate();
 | |
|     RefPtr<GetSubscriptionResultRunnable> r = new GetSubscriptionResultRunnable(
 | |
|         worker, std::move(mProxy), aStatus, endpoint, mScope,
 | |
|         std::move(rawP256dhKey), std::move(authSecret),
 | |
|         std::move(appServerKey));
 | |
|     if (!r->Dispatch()) {
 | |
|       return NS_ERROR_UNEXPECTED;
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // Convenience method for use in this file.
 | |
|   void OnPushSubscriptionError(nsresult aStatus) {
 | |
|     Unused << NS_WARN_IF(NS_FAILED(OnPushSubscription(aStatus, nullptr)));
 | |
|   }
 | |
| 
 | |
|  protected:
 | |
|   ~GetSubscriptionCallback() = default;
 | |
| 
 | |
|  private:
 | |
|   RefPtr<PromiseWorkerProxy> mProxy;
 | |
|   nsString mScope;
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(GetSubscriptionCallback, nsIPushSubscriptionCallback)
 | |
| 
 | |
| class GetSubscriptionRunnable final : public Runnable {
 | |
|  public:
 | |
|   GetSubscriptionRunnable(PromiseWorkerProxy* aProxy, const nsAString& aScope,
 | |
|                           PushManager::SubscriptionAction aAction,
 | |
|                           nsTArray<uint8_t>&& aAppServerKey)
 | |
|       : Runnable("dom::GetSubscriptionRunnable"),
 | |
|         mProxy(aProxy),
 | |
|         mScope(aScope),
 | |
|         mAction(aAction),
 | |
|         mAppServerKey(std::move(aAppServerKey)) {}
 | |
| 
 | |
|   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 == 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 (NS_WARN_IF(!service)) {
 | |
|       callback->OnPushSubscriptionError(NS_ERROR_FAILURE);
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     if (mAction == PushManager::SubscribeAction) {
 | |
|       if (mAppServerKey.IsEmpty()) {
 | |
|         rv = service->Subscribe(mScope, principal, callback);
 | |
|       } else {
 | |
|         rv = service->SubscribeWithKey(mScope, principal, mAppServerKey,
 | |
|                                        callback);
 | |
|       }
 | |
|     } else {
 | |
|       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;
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ~GetSubscriptionRunnable() = default;
 | |
| 
 | |
|   RefPtr<PromiseWorkerProxy> mProxy;
 | |
|   nsString mScope;
 | |
|   PushManager::SubscriptionAction mAction;
 | |
|   nsTArray<uint8_t> mAppServerKey;
 | |
| };
 | |
| 
 | |
| class PermissionResultRunnable final : public WorkerRunnable {
 | |
|  public:
 | |
|   PermissionResultRunnable(PromiseWorkerProxy* aProxy, nsresult aStatus,
 | |
|                            PushPermissionState aState)
 | |
|       : WorkerRunnable(aProxy->GetWorkerPrivate()),
 | |
|         mProxy(aProxy),
 | |
|         mStatus(aStatus),
 | |
|         mState(aState) {
 | |
|     AssertIsOnMainThread();
 | |
|   }
 | |
| 
 | |
|   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
 | |
|     MOZ_ASSERT(aWorkerPrivate);
 | |
|     aWorkerPrivate->AssertIsOnWorkerThread();
 | |
| 
 | |
|     RefPtr<Promise> promise = mProxy->WorkerPromise();
 | |
|     if (NS_SUCCEEDED(mStatus)) {
 | |
|       promise->MaybeResolve(mState);
 | |
|     } else {
 | |
|       promise->MaybeRejectWithUndefined();
 | |
|     }
 | |
| 
 | |
|     mProxy->CleanUp();
 | |
| 
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ~PermissionResultRunnable() = default;
 | |
| 
 | |
|   RefPtr<PromiseWorkerProxy> mProxy;
 | |
|   nsresult mStatus;
 | |
|   PushPermissionState mState;
 | |
| };
 | |
| 
 | |
| class PermissionStateRunnable final : public Runnable {
 | |
|  public:
 | |
|   explicit PermissionStateRunnable(PromiseWorkerProxy* aProxy)
 | |
|       : Runnable("dom::PermissionStateRunnable"), mProxy(aProxy) {}
 | |
| 
 | |
|   NS_IMETHOD
 | |
|   Run() override {
 | |
|     AssertIsOnMainThread();
 | |
|     MutexAutoLock lock(mProxy->Lock());
 | |
|     if (mProxy->CleanedUp()) {
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     PushPermissionState state;
 | |
|     nsresult rv =
 | |
|         GetPermissionState(mProxy->GetWorkerPrivate()->GetPrincipal(), state);
 | |
| 
 | |
|     RefPtr<PermissionResultRunnable> r =
 | |
|         new PermissionResultRunnable(mProxy, rv, state);
 | |
|     MOZ_ALWAYS_TRUE(r->Dispatch());
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ~PermissionStateRunnable() = default;
 | |
| 
 | |
|   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() = default;
 | |
| 
 | |
| 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 PushManager_Binding::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> PushManager::Subscribe(
 | |
|     const PushSubscriptionOptionsInit& aOptions, ErrorResult& aRv) {
 | |
|   if (mImpl) {
 | |
|     MOZ_ASSERT(NS_IsMainThread());
 | |
|     return mImpl->Subscribe(aOptions, aRv);
 | |
|   }
 | |
| 
 | |
|   return PerformSubscriptionActionFromWorker(SubscribeAction, aOptions, 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(
 | |
|     const PushSubscriptionOptionsInit& aOptions, ErrorResult& aRv) {
 | |
|   if (mImpl) {
 | |
|     MOZ_ASSERT(NS_IsMainThread());
 | |
|     return mImpl->PermissionState(aOptions, 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->MaybeRejectWithUndefined();
 | |
|     return p.forget();
 | |
|   }
 | |
| 
 | |
|   RefPtr<PermissionStateRunnable> r = new PermissionStateRunnable(proxy);
 | |
|   NS_DispatchToMainThread(r);
 | |
| 
 | |
|   return p.forget();
 | |
| }
 | |
| 
 | |
| already_AddRefed<Promise> PushManager::PerformSubscriptionActionFromWorker(
 | |
|     SubscriptionAction aAction, ErrorResult& aRv) {
 | |
|   PushSubscriptionOptionsInit options;
 | |
|   return PerformSubscriptionActionFromWorker(aAction, options, aRv);
 | |
| }
 | |
| 
 | |
| already_AddRefed<Promise> PushManager::PerformSubscriptionActionFromWorker(
 | |
|     SubscriptionAction aAction, const PushSubscriptionOptionsInit& aOptions,
 | |
|     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();
 | |
|   }
 | |
| 
 | |
|   nsTArray<uint8_t> appServerKey;
 | |
|   if (!aOptions.mApplicationServerKey.IsNull()) {
 | |
|     nsresult rv = NormalizeAppServerKey(aOptions.mApplicationServerKey.Value(),
 | |
|                                         appServerKey);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       p->MaybeReject(rv);
 | |
|       return p.forget();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   RefPtr<GetSubscriptionRunnable> r = new GetSubscriptionRunnable(
 | |
|       proxy, mScope, aAction, std::move(appServerKey));
 | |
|   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
 | |
| 
 | |
|   return p.forget();
 | |
| }
 | |
| 
 | |
| nsresult PushManager::NormalizeAppServerKey(
 | |
|     const OwningArrayBufferViewOrArrayBufferOrString& aSource,
 | |
|     nsTArray<uint8_t>& aAppServerKey) {
 | |
|   if (aSource.IsString()) {
 | |
|     NS_ConvertUTF16toUTF8 base64Key(aSource.GetAsString());
 | |
|     FallibleTArray<uint8_t> decodedKey;
 | |
|     nsresult rv = Base64URLDecode(
 | |
|         base64Key, Base64URLDecodePaddingPolicy::Reject, decodedKey);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
 | |
|     }
 | |
|     aAppServerKey = decodedKey;
 | |
|   } else if (aSource.IsArrayBuffer()) {
 | |
|     if (!PushUtil::CopyArrayBufferToArray(aSource.GetAsArrayBuffer(),
 | |
|                                           aAppServerKey)) {
 | |
|       return NS_ERROR_DOM_PUSH_INVALID_KEY_ERR;
 | |
|     }
 | |
|   } else if (aSource.IsArrayBufferView()) {
 | |
|     if (!PushUtil::CopyArrayBufferViewToArray(aSource.GetAsArrayBufferView(),
 | |
|                                               aAppServerKey)) {
 | |
|       return NS_ERROR_DOM_PUSH_INVALID_KEY_ERR;
 | |
|     }
 | |
|   } else {
 | |
|     MOZ_CRASH("Uninitialized union: expected string, buffer, or view");
 | |
|   }
 | |
|   if (aAppServerKey.IsEmpty()) {
 | |
|     return NS_ERROR_DOM_PUSH_INVALID_KEY_ERR;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| }  // namespace dom
 | |
| }  // namespace mozilla
 | 
