Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/push/PushManager.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/dom/PushManager.h"
8
9
#include "mozilla/Base64.h"
10
#include "mozilla/Preferences.h"
11
#include "mozilla/Services.h"
12
#include "mozilla/Unused.h"
13
#include "mozilla/dom/PushManagerBinding.h"
14
#include "mozilla/dom/PushSubscription.h"
15
#include "mozilla/dom/PushSubscriptionOptionsBinding.h"
16
#include "mozilla/dom/PushUtil.h"
17
#include "mozilla/dom/WorkerRunnable.h"
18
#include "mozilla/dom/WorkerPrivate.h"
19
#include "mozilla/dom/WorkerScope.h"
20
21
#include "mozilla/dom/Promise.h"
22
#include "mozilla/dom/PromiseWorkerProxy.h"
23
24
#include "nsIGlobalObject.h"
25
#include "nsIPermissionManager.h"
26
#include "nsIPrincipal.h"
27
#include "nsIPushService.h"
28
29
#include "nsComponentManagerUtils.h"
30
#include "nsContentUtils.h"
31
32
namespace mozilla {
33
namespace dom {
34
35
namespace {
36
37
nsresult
38
GetPermissionState(nsIPrincipal* aPrincipal,
39
                   PushPermissionState& aState)
40
0
{
41
0
  nsCOMPtr<nsIPermissionManager> permManager =
42
0
    mozilla::services::GetPermissionManager();
43
0
44
0
  if (!permManager) {
45
0
    return NS_ERROR_FAILURE;
46
0
  }
47
0
  uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
48
0
  nsresult rv = permManager->TestExactPermissionFromPrincipal(
49
0
                  aPrincipal,
50
0
                  "desktop-notification",
51
0
                  &permission);
52
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
53
0
    return rv;
54
0
  }
55
0
56
0
  if (permission == nsIPermissionManager::ALLOW_ACTION ||
57
0
      Preferences::GetBool("dom.push.testing.ignorePermission", false)) {
58
0
    aState = PushPermissionState::Granted;
59
0
  } else if (permission == nsIPermissionManager::DENY_ACTION) {
60
0
    aState = PushPermissionState::Denied;
61
0
  } else {
62
0
    aState = PushPermissionState::Prompt;
63
0
  }
64
0
65
0
  return NS_OK;
66
0
}
67
68
// A helper class that frees an `nsIPushSubscription` key buffer when it
69
// goes out of scope.
70
class MOZ_RAII AutoFreeKeyBuffer final
71
{
72
  uint8_t** mKeyBuffer;
73
74
public:
75
  explicit AutoFreeKeyBuffer(uint8_t** aKeyBuffer)
76
    : mKeyBuffer(aKeyBuffer)
77
0
  {
78
0
    MOZ_ASSERT(mKeyBuffer);
79
0
  }
80
81
  ~AutoFreeKeyBuffer()
82
0
  {
83
0
    free(*mKeyBuffer);
84
0
  }
85
};
86
87
// Copies a subscription key buffer into an array.
88
nsresult
89
CopySubscriptionKeyToArray(nsIPushSubscription* aSubscription,
90
                           const nsAString& aKeyName,
91
                           nsTArray<uint8_t>& aKey)
92
0
{
93
0
  uint8_t* keyBuffer = nullptr;
94
0
  AutoFreeKeyBuffer autoFree(&keyBuffer);
95
0
96
0
  uint32_t keyLen;
97
0
  nsresult rv = aSubscription->GetKey(aKeyName, &keyLen, &keyBuffer);
98
0
  if (NS_FAILED(rv)) {
99
0
    return rv;
100
0
  }
101
0
  if (!aKey.SetCapacity(keyLen, fallible) ||
102
0
      !aKey.InsertElementsAt(0, keyBuffer, keyLen, fallible)) {
103
0
    return NS_ERROR_OUT_OF_MEMORY;
104
0
  }
105
0
  return NS_OK;
106
0
}
107
108
nsresult
109
GetSubscriptionParams(nsIPushSubscription* aSubscription,
110
                      nsAString& aEndpoint,
111
                      nsTArray<uint8_t>& aRawP256dhKey,
112
                      nsTArray<uint8_t>& aAuthSecret,
113
                      nsTArray<uint8_t>& aAppServerKey)
114
0
{
115
0
  if (!aSubscription) {
116
0
    return NS_OK;
117
0
  }
118
0
119
0
  nsresult rv = aSubscription->GetEndpoint(aEndpoint);
120
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
121
0
    return rv;
122
0
  }
123
0
124
0
  rv = CopySubscriptionKeyToArray(aSubscription, NS_LITERAL_STRING("p256dh"),
125
0
                                  aRawP256dhKey);
126
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
127
0
    return rv;
128
0
  }
129
0
  rv = CopySubscriptionKeyToArray(aSubscription, NS_LITERAL_STRING("auth"),
130
0
                                  aAuthSecret);
131
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
132
0
    return rv;
133
0
  }
134
0
  rv = CopySubscriptionKeyToArray(aSubscription, NS_LITERAL_STRING("appServer"),
135
0
                                  aAppServerKey);
136
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
137
0
    return rv;
138
0
  }
139
0
140
0
  return NS_OK;
141
0
}
142
143
class GetSubscriptionResultRunnable final : public WorkerRunnable
144
{
145
public:
146
  GetSubscriptionResultRunnable(WorkerPrivate* aWorkerPrivate,
147
                                already_AddRefed<PromiseWorkerProxy>&& aProxy,
148
                                nsresult aStatus,
149
                                const nsAString& aEndpoint,
150
                                const nsAString& aScope,
151
                                nsTArray<uint8_t>&& aRawP256dhKey,
152
                                nsTArray<uint8_t>&& aAuthSecret,
153
                                nsTArray<uint8_t>&& aAppServerKey)
154
    : WorkerRunnable(aWorkerPrivate)
155
    , mProxy(std::move(aProxy))
156
    , mStatus(aStatus)
157
    , mEndpoint(aEndpoint)
158
    , mScope(aScope)
159
    , mRawP256dhKey(std::move(aRawP256dhKey))
160
    , mAuthSecret(std::move(aAuthSecret))
161
    , mAppServerKey(std::move(aAppServerKey))
162
0
  { }
163
164
  bool
165
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
166
0
  {
167
0
    RefPtr<Promise> promise = mProxy->WorkerPromise();
168
0
    if (NS_SUCCEEDED(mStatus)) {
169
0
      if (mEndpoint.IsEmpty()) {
170
0
        promise->MaybeResolve(JS::NullHandleValue);
171
0
      } else {
172
0
        RefPtr<PushSubscription> sub =
173
0
            new PushSubscription(nullptr, mEndpoint, mScope,
174
0
                                 std::move(mRawP256dhKey), std::move(mAuthSecret),
175
0
                                 std::move(mAppServerKey));
176
0
        promise->MaybeResolve(sub);
177
0
      }
178
0
    } else if (NS_ERROR_GET_MODULE(mStatus) == NS_ERROR_MODULE_DOM_PUSH ) {
179
0
      promise->MaybeReject(mStatus);
180
0
    } else {
181
0
      promise->MaybeReject(NS_ERROR_DOM_PUSH_ABORT_ERR);
182
0
    }
183
0
184
0
    mProxy->CleanUp();
185
0
186
0
    return true;
187
0
  }
188
private:
189
  ~GetSubscriptionResultRunnable()
190
0
  {}
191
192
  RefPtr<PromiseWorkerProxy> mProxy;
193
  nsresult mStatus;
194
  nsString mEndpoint;
195
  nsString mScope;
196
  nsTArray<uint8_t> mRawP256dhKey;
197
  nsTArray<uint8_t> mAuthSecret;
198
  nsTArray<uint8_t> mAppServerKey;
199
};
200
201
class GetSubscriptionCallback final : public nsIPushSubscriptionCallback
202
{
203
public:
204
  NS_DECL_ISUPPORTS
205
206
  explicit GetSubscriptionCallback(PromiseWorkerProxy* aProxy,
207
                                   const nsAString& aScope)
208
    : mProxy(aProxy)
209
    , mScope(aScope)
210
0
  {}
211
212
  NS_IMETHOD
213
  OnPushSubscription(nsresult aStatus,
214
                     nsIPushSubscription* aSubscription) override
215
0
  {
216
0
    AssertIsOnMainThread();
217
0
    MOZ_ASSERT(mProxy, "OnPushSubscription() called twice?");
218
0
219
0
    MutexAutoLock lock(mProxy->Lock());
220
0
    if (mProxy->CleanedUp()) {
221
0
      return NS_OK;
222
0
    }
223
0
224
0
    nsAutoString endpoint;
225
0
    nsTArray<uint8_t> rawP256dhKey, authSecret, appServerKey;
226
0
    if (NS_SUCCEEDED(aStatus)) {
227
0
      aStatus = GetSubscriptionParams(aSubscription, endpoint, rawP256dhKey,
228
0
                                      authSecret, appServerKey);
229
0
    }
230
0
231
0
    WorkerPrivate* worker = mProxy->GetWorkerPrivate();
232
0
    RefPtr<GetSubscriptionResultRunnable> r =
233
0
      new GetSubscriptionResultRunnable(worker,
234
0
                                        mProxy.forget(),
235
0
                                        aStatus,
236
0
                                        endpoint,
237
0
                                        mScope,
238
0
                                        std::move(rawP256dhKey),
239
0
                                        std::move(authSecret),
240
0
                                        std::move(appServerKey));
241
0
    MOZ_ALWAYS_TRUE(r->Dispatch());
242
0
243
0
    return NS_OK;
244
0
  }
245
246
  // Convenience method for use in this file.
247
  void
248
  OnPushSubscriptionError(nsresult aStatus)
249
0
  {
250
0
    Unused << NS_WARN_IF(NS_FAILED(
251
0
        OnPushSubscription(aStatus, nullptr)));
252
0
  }
253
254
protected:
255
  ~GetSubscriptionCallback()
256
0
  {}
257
258
private:
259
  RefPtr<PromiseWorkerProxy> mProxy;
260
  nsString mScope;
261
};
262
263
NS_IMPL_ISUPPORTS(GetSubscriptionCallback, nsIPushSubscriptionCallback)
264
265
class GetSubscriptionRunnable final : public Runnable
266
{
267
public:
268
  GetSubscriptionRunnable(PromiseWorkerProxy* aProxy,
269
                          const nsAString& aScope,
270
                          PushManager::SubscriptionAction aAction,
271
                          nsTArray<uint8_t>&& aAppServerKey)
272
    : Runnable("dom::GetSubscriptionRunnable")
273
    , mProxy(aProxy)
274
    , mScope(aScope)
275
    , mAction(aAction)
276
    , mAppServerKey(std::move(aAppServerKey))
277
0
  {}
278
279
  NS_IMETHOD
280
  Run() override
281
0
  {
282
0
    AssertIsOnMainThread();
283
0
284
0
    nsCOMPtr<nsIPrincipal> principal;
285
0
286
0
    {
287
0
      // Bug 1228723: If permission is revoked or an error occurs, the
288
0
      // subscription callback will be called synchronously. This causes
289
0
      // `GetSubscriptionCallback::OnPushSubscription` to deadlock when
290
0
      // it tries to acquire the lock.
291
0
      MutexAutoLock lock(mProxy->Lock());
292
0
      if (mProxy->CleanedUp()) {
293
0
        return NS_OK;
294
0
      }
295
0
      principal = mProxy->GetWorkerPrivate()->GetPrincipal();
296
0
    }
297
0
298
0
    MOZ_ASSERT(principal);
299
0
300
0
    RefPtr<GetSubscriptionCallback> callback = new GetSubscriptionCallback(mProxy, mScope);
301
0
302
0
    PushPermissionState state;
303
0
    nsresult rv = GetPermissionState(principal, state);
304
0
    if (NS_FAILED(rv)) {
305
0
      callback->OnPushSubscriptionError(NS_ERROR_FAILURE);
306
0
      return NS_OK;
307
0
    }
308
0
309
0
    if (state != PushPermissionState::Granted) {
310
0
      if (mAction == PushManager::GetSubscriptionAction) {
311
0
        callback->OnPushSubscriptionError(NS_OK);
312
0
        return NS_OK;
313
0
      }
314
0
      callback->OnPushSubscriptionError(NS_ERROR_DOM_PUSH_DENIED_ERR);
315
0
      return NS_OK;
316
0
    }
317
0
318
0
    nsCOMPtr<nsIPushService> service =
319
0
      do_GetService("@mozilla.org/push/Service;1");
320
0
    if (NS_WARN_IF(!service)) {
321
0
      callback->OnPushSubscriptionError(NS_ERROR_FAILURE);
322
0
      return NS_OK;
323
0
    }
324
0
325
0
    if (mAction == PushManager::SubscribeAction) {
326
0
      if (mAppServerKey.IsEmpty()) {
327
0
        rv = service->Subscribe(mScope, principal, callback);
328
0
      } else {
329
0
        rv = service->SubscribeWithKey(mScope, principal,
330
0
                                       mAppServerKey.Length(),
331
0
                                       mAppServerKey.Elements(), callback);
332
0
      }
333
0
    } else {
334
0
      MOZ_ASSERT(mAction == PushManager::GetSubscriptionAction);
335
0
      rv = service->GetSubscription(mScope, principal, callback);
336
0
    }
337
0
338
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
339
0
      callback->OnPushSubscriptionError(NS_ERROR_FAILURE);
340
0
      return NS_OK;
341
0
    }
342
0
343
0
    return NS_OK;
344
0
  }
345
346
private:
347
  ~GetSubscriptionRunnable()
348
0
  {}
349
350
  RefPtr<PromiseWorkerProxy> mProxy;
351
  nsString mScope;
352
  PushManager::SubscriptionAction mAction;
353
  nsTArray<uint8_t> mAppServerKey;
354
};
355
356
class PermissionResultRunnable final : public WorkerRunnable
357
{
358
public:
359
  PermissionResultRunnable(PromiseWorkerProxy *aProxy,
360
                           nsresult aStatus,
361
                           PushPermissionState aState)
362
    : WorkerRunnable(aProxy->GetWorkerPrivate())
363
    , mProxy(aProxy)
364
    , mStatus(aStatus)
365
    , mState(aState)
366
0
  {
367
0
    AssertIsOnMainThread();
368
0
  }
369
370
  bool
371
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
372
0
  {
373
0
    MOZ_ASSERT(aWorkerPrivate);
374
0
    aWorkerPrivate->AssertIsOnWorkerThread();
375
0
376
0
    RefPtr<Promise> promise = mProxy->WorkerPromise();
377
0
    if (NS_SUCCEEDED(mStatus)) {
378
0
      promise->MaybeResolve(mState);
379
0
    } else {
380
0
      promise->MaybeReject(aCx, JS::UndefinedHandleValue);
381
0
    }
382
0
383
0
    mProxy->CleanUp();
384
0
385
0
    return true;
386
0
  }
387
388
private:
389
  ~PermissionResultRunnable()
390
0
  {}
391
392
  RefPtr<PromiseWorkerProxy> mProxy;
393
  nsresult mStatus;
394
  PushPermissionState mState;
395
};
396
397
class PermissionStateRunnable final : public Runnable
398
{
399
public:
400
  explicit PermissionStateRunnable(PromiseWorkerProxy* aProxy)
401
    : Runnable("dom::PermissionStateRunnable")
402
    , mProxy(aProxy)
403
0
  {}
404
405
  NS_IMETHOD
406
  Run() override
407
0
  {
408
0
    AssertIsOnMainThread();
409
0
    MutexAutoLock lock(mProxy->Lock());
410
0
    if (mProxy->CleanedUp()) {
411
0
      return NS_OK;
412
0
    }
413
0
414
0
    PushPermissionState state;
415
0
    nsresult rv = GetPermissionState(
416
0
      mProxy->GetWorkerPrivate()->GetPrincipal(),
417
0
      state
418
0
    );
419
0
420
0
    RefPtr<PermissionResultRunnable> r =
421
0
      new PermissionResultRunnable(mProxy, rv, state);
422
0
    MOZ_ALWAYS_TRUE(r->Dispatch());
423
0
424
0
    return NS_OK;
425
0
  }
426
427
private:
428
  ~PermissionStateRunnable()
429
0
  {}
430
431
  RefPtr<PromiseWorkerProxy> mProxy;
432
};
433
434
} // anonymous namespace
435
436
PushManager::PushManager(nsIGlobalObject* aGlobal, PushManagerImpl* aImpl)
437
  : mGlobal(aGlobal)
438
  , mImpl(aImpl)
439
0
{
440
0
  AssertIsOnMainThread();
441
0
  MOZ_ASSERT(aImpl);
442
0
}
443
444
PushManager::PushManager(const nsAString& aScope)
445
  : mScope(aScope)
446
0
{
447
#ifdef DEBUG
448
  // There's only one global on a worker, so we don't need to pass a global
449
  // object to the constructor.
450
  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
451
  MOZ_ASSERT(worker);
452
  worker->AssertIsOnWorkerThread();
453
#endif
454
}
455
456
PushManager::~PushManager()
457
0
{}
458
459
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushManager, mGlobal, mImpl)
460
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushManager)
461
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushManager)
462
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushManager)
463
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
464
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
465
0
NS_INTERFACE_MAP_END
466
467
JSObject*
468
PushManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
469
0
{
470
0
  return PushManager_Binding::Wrap(aCx, this, aGivenProto);
471
0
}
472
473
// static
474
already_AddRefed<PushManager>
475
PushManager::Constructor(GlobalObject& aGlobal,
476
                         const nsAString& aScope,
477
                         ErrorResult& aRv)
478
0
{
479
0
  if (!NS_IsMainThread()) {
480
0
    RefPtr<PushManager> ret = new PushManager(aScope);
481
0
    return ret.forget();
482
0
  }
483
0
484
0
  RefPtr<PushManagerImpl> impl = PushManagerImpl::Constructor(aGlobal,
485
0
                                                              aGlobal.Context(),
486
0
                                                              aScope, aRv);
487
0
  if (aRv.Failed()) {
488
0
    return nullptr;
489
0
  }
490
0
491
0
  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
492
0
  RefPtr<PushManager> ret = new PushManager(global, impl);
493
0
494
0
  return ret.forget();
495
0
}
496
497
already_AddRefed<Promise>
498
PushManager::Subscribe(const PushSubscriptionOptionsInit& aOptions,
499
                       ErrorResult& aRv)
500
0
{
501
0
  if (mImpl) {
502
0
    MOZ_ASSERT(NS_IsMainThread());
503
0
    return mImpl->Subscribe(aOptions, aRv);
504
0
  }
505
0
506
0
  return PerformSubscriptionActionFromWorker(SubscribeAction, aOptions, aRv);
507
0
}
508
509
already_AddRefed<Promise>
510
PushManager::GetSubscription(ErrorResult& aRv)
511
0
{
512
0
  if (mImpl) {
513
0
    MOZ_ASSERT(NS_IsMainThread());
514
0
    return mImpl->GetSubscription(aRv);
515
0
  }
516
0
517
0
  return PerformSubscriptionActionFromWorker(GetSubscriptionAction, aRv);
518
0
}
519
520
already_AddRefed<Promise>
521
PushManager::PermissionState(const PushSubscriptionOptionsInit& aOptions,
522
                             ErrorResult& aRv)
523
0
{
524
0
  if (mImpl) {
525
0
    MOZ_ASSERT(NS_IsMainThread());
526
0
    return mImpl->PermissionState(aOptions, aRv);
527
0
  }
528
0
529
0
  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
530
0
  MOZ_ASSERT(worker);
531
0
  worker->AssertIsOnWorkerThread();
532
0
533
0
  nsCOMPtr<nsIGlobalObject> global = worker->GlobalScope();
534
0
  RefPtr<Promise> p = Promise::Create(global, aRv);
535
0
  if (NS_WARN_IF(aRv.Failed())) {
536
0
    return nullptr;
537
0
  }
538
0
539
0
  RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, p);
540
0
  if (!proxy) {
541
0
    p->MaybeReject(worker->GetJSContext(), JS::UndefinedHandleValue);
542
0
    return p.forget();
543
0
  }
544
0
545
0
  RefPtr<PermissionStateRunnable> r =
546
0
    new PermissionStateRunnable(proxy);
547
0
  NS_DispatchToMainThread(r);
548
0
549
0
  return p.forget();
550
0
}
551
552
already_AddRefed<Promise>
553
PushManager::PerformSubscriptionActionFromWorker(SubscriptionAction aAction,
554
                                                 ErrorResult& aRv)
555
0
{
556
0
  PushSubscriptionOptionsInit options;
557
0
  return PerformSubscriptionActionFromWorker(aAction, options, aRv);
558
0
}
559
560
already_AddRefed<Promise>
561
PushManager::PerformSubscriptionActionFromWorker(SubscriptionAction aAction,
562
                                                 const PushSubscriptionOptionsInit& aOptions,
563
                                                 ErrorResult& aRv)
564
0
{
565
0
  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
566
0
  MOZ_ASSERT(worker);
567
0
  worker->AssertIsOnWorkerThread();
568
0
569
0
  nsCOMPtr<nsIGlobalObject> global = worker->GlobalScope();
570
0
  RefPtr<Promise> p = Promise::Create(global, aRv);
571
0
  if (NS_WARN_IF(aRv.Failed())) {
572
0
    return nullptr;
573
0
  }
574
0
575
0
  RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, p);
576
0
  if (!proxy) {
577
0
    p->MaybeReject(NS_ERROR_DOM_PUSH_ABORT_ERR);
578
0
    return p.forget();
579
0
  }
580
0
581
0
  nsTArray<uint8_t> appServerKey;
582
0
  if (!aOptions.mApplicationServerKey.IsNull()) {
583
0
    nsresult rv = NormalizeAppServerKey(aOptions.mApplicationServerKey.Value(),
584
0
                                        appServerKey);
585
0
    if (NS_FAILED(rv)) {
586
0
      p->MaybeReject(rv);
587
0
      return p.forget();
588
0
    }
589
0
  }
590
0
591
0
  RefPtr<GetSubscriptionRunnable> r =
592
0
    new GetSubscriptionRunnable(proxy, mScope, aAction, std::move(appServerKey));
593
0
  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
594
0
595
0
  return p.forget();
596
0
}
597
598
nsresult
599
PushManager::NormalizeAppServerKey(const OwningArrayBufferViewOrArrayBufferOrString& aSource,
600
                                   nsTArray<uint8_t>& aAppServerKey)
601
0
{
602
0
  if (aSource.IsString()) {
603
0
    NS_ConvertUTF16toUTF8 base64Key(aSource.GetAsString());
604
0
    FallibleTArray<uint8_t> decodedKey;
605
0
    nsresult rv = Base64URLDecode(base64Key,
606
0
                                  Base64URLDecodePaddingPolicy::Reject,
607
0
                                  decodedKey);
608
0
    if (NS_FAILED(rv)) {
609
0
      return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
610
0
    }
611
0
    aAppServerKey = decodedKey;
612
0
  } else if (aSource.IsArrayBuffer()) {
613
0
    if (!PushUtil::CopyArrayBufferToArray(aSource.GetAsArrayBuffer(),
614
0
                                         aAppServerKey)) {
615
0
      return NS_ERROR_DOM_PUSH_INVALID_KEY_ERR;
616
0
    }
617
0
  } else if (aSource.IsArrayBufferView()) {
618
0
    if (!PushUtil::CopyArrayBufferViewToArray(aSource.GetAsArrayBufferView(),
619
0
                                              aAppServerKey)) {
620
0
      return NS_ERROR_DOM_PUSH_INVALID_KEY_ERR;
621
0
    }
622
0
  } else {
623
0
    MOZ_CRASH("Uninitialized union: expected string, buffer, or view");
624
0
  }
625
0
  if (aAppServerKey.IsEmpty()) {
626
0
    return NS_ERROR_DOM_PUSH_INVALID_KEY_ERR;
627
0
  }
628
0
  return NS_OK;
629
0
}
630
631
} // namespace dom
632
} // namespace mozilla