Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/u2f/U2F.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/U2F.h"
8
#include "mozilla/dom/WebCryptoCommon.h"
9
#include "mozilla/ipc/PBackgroundChild.h"
10
#include "mozilla/ipc/BackgroundChild.h"
11
#include "mozilla/dom/WebAuthnTransactionChild.h"
12
#include "mozilla/dom/WebAuthnUtil.h"
13
#include "nsContentUtils.h"
14
#include "nsIEffectiveTLDService.h"
15
#include "nsNetUtil.h"
16
#include "nsURLParsers.h"
17
18
using namespace mozilla::ipc;
19
20
// Forward decl because of nsHTMLDocument.h's complex dependency on /layout/style
21
class nsHTMLDocument {
22
public:
23
  bool IsRegistrableDomainSuffixOfOrEqualTo(const nsAString& aHostSuffixString,
24
                                            const nsACString& aOrigHost);
25
};
26
27
namespace mozilla {
28
namespace dom {
29
30
static mozilla::LazyLogModule gU2FLog("u2fmanager");
31
32
NS_NAMED_LITERAL_STRING(kFinishEnrollment, "navigator.id.finishEnrollment");
33
NS_NAMED_LITERAL_STRING(kGetAssertion, "navigator.id.getAssertion");
34
35
// Bug #1436078 - Permit Google Accounts. Remove in Bug #1436085 in Jan 2023.
36
NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId1,
37
  "https://www.gstatic.com/securitykey/origins.json");
38
NS_NAMED_LITERAL_STRING(kGoogleAccountsAppId2,
39
  "https://www.gstatic.com/securitykey/a/google.com/origins.json");
40
41
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(U2F)
42
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
43
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
44
0
  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
45
0
NS_INTERFACE_MAP_END
46
47
NS_IMPL_CYCLE_COLLECTING_ADDREF(U2F)
48
NS_IMPL_CYCLE_COLLECTING_RELEASE(U2F)
49
50
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(U2F, mParent)
51
52
/***********************************************************************
53
 * Utility Functions
54
 **********************************************************************/
55
56
static ErrorCode
57
ConvertNSResultToErrorCode(const nsresult& aError)
58
0
{
59
0
  if (aError == NS_ERROR_DOM_TIMEOUT_ERR) {
60
0
    return ErrorCode::TIMEOUT;
61
0
  }
62
0
  /* Emitted by U2F{Soft,HID}TokenManager when we really mean ineligible */
63
0
  if (aError == NS_ERROR_DOM_INVALID_STATE_ERR) {
64
0
    return ErrorCode::DEVICE_INELIGIBLE;
65
0
  }
66
0
  return ErrorCode::OTHER_ERROR;
67
0
}
68
69
static uint32_t
70
AdjustedTimeoutMillis(const Optional<Nullable<int32_t>>& opt_aSeconds)
71
0
{
72
0
  uint32_t adjustedTimeoutMillis = 30000u;
73
0
  if (opt_aSeconds.WasPassed() && !opt_aSeconds.Value().IsNull()) {
74
0
    adjustedTimeoutMillis = opt_aSeconds.Value().Value() * 1000u;
75
0
    adjustedTimeoutMillis = std::max(15000u, adjustedTimeoutMillis);
76
0
    adjustedTimeoutMillis = std::min(120000u, adjustedTimeoutMillis);
77
0
  }
78
0
  return adjustedTimeoutMillis;
79
0
}
80
81
static nsresult
82
AssembleClientData(const nsAString& aOrigin, const nsAString& aTyp,
83
                   const nsAString& aChallenge,
84
                   /* out */ nsString& aClientData)
85
0
{
86
0
  MOZ_ASSERT(NS_IsMainThread());
87
0
  U2FClientData clientDataObject;
88
0
  clientDataObject.mTyp.Construct(aTyp); // "Typ" from the U2F specification
89
0
  clientDataObject.mChallenge.Construct(aChallenge);
90
0
  clientDataObject.mOrigin.Construct(aOrigin);
91
0
92
0
  if (NS_WARN_IF(!clientDataObject.ToJSON(aClientData))) {
93
0
    return NS_ERROR_FAILURE;
94
0
  }
95
0
96
0
  return NS_OK;
97
0
}
98
99
static void
100
RegisteredKeysToScopedCredentialList(const nsAString& aAppId,
101
  const nsTArray<RegisteredKey>& aKeys,
102
  nsTArray<WebAuthnScopedCredential>& aList)
103
0
{
104
0
  for (const RegisteredKey& key : aKeys) {
105
0
    // Check for required attributes
106
0
    if (!key.mVersion.WasPassed() || !key.mKeyHandle.WasPassed() ||
107
0
        key.mVersion.Value() != kRequiredU2FVersion) {
108
0
      continue;
109
0
    }
110
0
111
0
    // If this key's mAppId doesn't match the invocation, we can't handle it.
112
0
    if (key.mAppId.WasPassed() && !key.mAppId.Value().Equals(aAppId)) {
113
0
      continue;
114
0
    }
115
0
116
0
    CryptoBuffer keyHandle;
117
0
    nsresult rv = keyHandle.FromJwkBase64(key.mKeyHandle.Value());
118
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
119
0
      continue;
120
0
    }
121
0
122
0
    WebAuthnScopedCredential c;
123
0
    c.id() = keyHandle;
124
0
    aList.AppendElement(c);
125
0
  }
126
0
}
127
128
/***********************************************************************
129
 * U2F JavaScript API Implementation
130
 **********************************************************************/
131
132
U2F::~U2F()
133
0
{
134
0
  MOZ_ASSERT(NS_IsMainThread());
135
0
136
0
  if (mTransaction.isSome()) {
137
0
    RejectTransaction(NS_ERROR_ABORT);
138
0
  }
139
0
140
0
  if (mChild) {
141
0
    RefPtr<WebAuthnTransactionChild> c;
142
0
    mChild.swap(c);
143
0
    c->Disconnect();
144
0
  }
145
0
}
146
147
void
148
U2F::Init(ErrorResult& aRv)
149
0
{
150
0
  MOZ_ASSERT(mParent);
151
0
152
0
  nsCOMPtr<nsIDocument> doc = mParent->GetDoc();
153
0
  MOZ_ASSERT(doc);
154
0
  if (!doc) {
155
0
    aRv.Throw(NS_ERROR_FAILURE);
156
0
    return;
157
0
  }
158
0
159
0
  nsIPrincipal* principal = doc->NodePrincipal();
160
0
  aRv = nsContentUtils::GetUTFOrigin(principal, mOrigin);
161
0
  if (NS_WARN_IF(aRv.Failed())) {
162
0
    return;
163
0
  }
164
0
165
0
  if (NS_WARN_IF(mOrigin.IsEmpty())) {
166
0
    aRv.Throw(NS_ERROR_FAILURE);
167
0
    return;
168
0
  }
169
0
}
170
171
/* virtual */ JSObject*
172
U2F::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
173
0
{
174
0
  return U2F_Binding::Wrap(aCx, this, aGivenProto);
175
0
}
176
177
template<typename T, typename C>
178
void
179
U2F::ExecuteCallback(T& aResp, nsMainThreadPtrHandle<C>& aCb)
180
0
{
181
0
  MOZ_ASSERT(NS_IsMainThread());
182
0
  MOZ_ASSERT(aCb);
183
0
184
0
  // Assert that mTransaction was cleared before before we were called to allow
185
0
  // reentrancy from microtask checkpoints.
186
0
  MOZ_ASSERT(mTransaction.isNothing());
187
0
188
0
  ErrorResult error;
189
0
  aCb->Call(aResp, error);
190
0
  NS_WARNING_ASSERTION(!error.Failed(), "dom::U2F::Promise callback failed");
191
0
  error.SuppressException(); // Useful exceptions already emitted
192
0
}
Unexecuted instantiation: void mozilla::dom::U2F::ExecuteCallback<mozilla::dom::RegisterResponse, mozilla::dom::U2FRegisterCallback>(mozilla::dom::RegisterResponse&, nsMainThreadPtrHandle<mozilla::dom::U2FRegisterCallback>&)
Unexecuted instantiation: void mozilla::dom::U2F::ExecuteCallback<mozilla::dom::SignResponse, mozilla::dom::U2FSignCallback>(mozilla::dom::SignResponse&, nsMainThreadPtrHandle<mozilla::dom::U2FSignCallback>&)
193
194
void
195
U2F::Register(const nsAString& aAppId,
196
              const Sequence<RegisterRequest>& aRegisterRequests,
197
              const Sequence<RegisteredKey>& aRegisteredKeys,
198
              U2FRegisterCallback& aCallback,
199
              const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
200
              ErrorResult& aRv)
201
0
{
202
0
  MOZ_ASSERT(NS_IsMainThread());
203
0
204
0
  if (mTransaction.isSome()) {
205
0
    CancelTransaction(NS_ERROR_ABORT);
206
0
  }
207
0
208
0
  nsMainThreadPtrHandle<U2FRegisterCallback> callback(
209
0
    new nsMainThreadPtrHolder<U2FRegisterCallback>("U2F::Register::callback",
210
0
                                                   &aCallback));
211
0
212
0
  // Ensure we have a callback.
213
0
  if (NS_WARN_IF(!callback)) {
214
0
    return;
215
0
  }
216
0
217
0
  // Evaluate the AppID
218
0
  nsString adjustedAppId(aAppId);
219
0
  if (!EvaluateAppID(mParent, mOrigin, U2FOperation::Register, adjustedAppId)) {
220
0
    RegisterResponse response;
221
0
    response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::BAD_REQUEST));
222
0
    ExecuteCallback(response, callback);
223
0
    return;
224
0
  }
225
0
226
0
  nsAutoString clientDataJSON;
227
0
228
0
  // Pick the first valid RegisterRequest; we can only work with one.
229
0
  CryptoBuffer challenge;
230
0
  for (const RegisterRequest& req : aRegisterRequests) {
231
0
    if (!req.mChallenge.WasPassed() || !req.mVersion.WasPassed() ||
232
0
        req.mVersion.Value() != kRequiredU2FVersion) {
233
0
      continue;
234
0
    }
235
0
    if (!challenge.Assign(NS_ConvertUTF16toUTF8(req.mChallenge.Value()))) {
236
0
      continue;
237
0
    }
238
0
239
0
    nsresult rv = AssembleClientData(mOrigin, kFinishEnrollment,
240
0
                                     req.mChallenge.Value(), clientDataJSON);
241
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
242
0
      continue;
243
0
    }
244
0
  }
245
0
246
0
  // Did we not get a valid RegisterRequest? Abort.
247
0
  if (clientDataJSON.IsEmpty()) {
248
0
    RegisterResponse response;
249
0
    response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::BAD_REQUEST));
250
0
    ExecuteCallback(response, callback);
251
0
    return;
252
0
  }
253
0
254
0
  // Build the exclusion list, if any
255
0
  nsTArray<WebAuthnScopedCredential> excludeList;
256
0
  RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
257
0
                                       excludeList);
258
0
259
0
  if (!MaybeCreateBackgroundActor()) {
260
0
    RegisterResponse response;
261
0
    response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
262
0
    ExecuteCallback(response, callback);
263
0
    return;
264
0
  }
265
0
266
0
  ListenForVisibilityEvents();
267
0
268
0
  NS_ConvertUTF16toUTF8 clientData(clientDataJSON);
269
0
  uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
270
0
271
0
  WebAuthnMakeCredentialInfo info(mOrigin,
272
0
                                  adjustedAppId,
273
0
                                  challenge,
274
0
                                  clientData,
275
0
                                  adjustedTimeoutMillis,
276
0
                                  excludeList,
277
0
                                  null_t() /* no extra info for U2F */);
278
0
279
0
  MOZ_ASSERT(mTransaction.isNothing());
280
0
  mTransaction = Some(U2FTransaction(AsVariant(callback)));
281
0
  mChild->SendRequestRegister(mTransaction.ref().mId, info);
282
0
}
283
284
void
285
U2F::FinishMakeCredential(const uint64_t& aTransactionId,
286
                          const WebAuthnMakeCredentialResult& aResult)
287
0
{
288
0
  MOZ_ASSERT(NS_IsMainThread());
289
0
290
0
  // Check for a valid transaction.
291
0
  if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
292
0
    return;
293
0
  }
294
0
295
0
  if (NS_WARN_IF(!mTransaction.ref().HasRegisterCallback())) {
296
0
    RejectTransaction(NS_ERROR_ABORT);
297
0
    return;
298
0
  }
299
0
300
0
  // A CTAP2 response.
301
0
  if (aResult.RegistrationData().Length() == 0) {
302
0
    RejectTransaction(NS_ERROR_ABORT);
303
0
    return;
304
0
  }
305
0
306
0
  CryptoBuffer clientDataBuf;
307
0
  if (NS_WARN_IF(!clientDataBuf.Assign(aResult.ClientDataJSON()))) {
308
0
    RejectTransaction(NS_ERROR_ABORT);
309
0
    return;
310
0
  }
311
0
312
0
  CryptoBuffer regBuf;
313
0
  if (NS_WARN_IF(!regBuf.Assign(aResult.RegistrationData()))) {
314
0
    RejectTransaction(NS_ERROR_ABORT);
315
0
    return;
316
0
  }
317
0
318
0
  nsString clientDataBase64;
319
0
  nsString registrationDataBase64;
320
0
  nsresult rvClientData = clientDataBuf.ToJwkBase64(clientDataBase64);
321
0
  nsresult rvRegistrationData = regBuf.ToJwkBase64(registrationDataBase64);
322
0
323
0
  if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
324
0
      NS_WARN_IF(NS_FAILED(rvRegistrationData))) {
325
0
    RejectTransaction(NS_ERROR_ABORT);
326
0
    return;
327
0
  }
328
0
329
0
  // Assemble a response object to return
330
0
  RegisterResponse response;
331
0
  response.mVersion.Construct(kRequiredU2FVersion);
332
0
  response.mClientData.Construct(clientDataBase64);
333
0
  response.mRegistrationData.Construct(registrationDataBase64);
334
0
  response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
335
0
336
0
  // Keep the callback pointer alive.
337
0
  nsMainThreadPtrHandle<U2FRegisterCallback> callback(
338
0
    mTransaction.ref().GetRegisterCallback());
339
0
340
0
  ClearTransaction();
341
0
  ExecuteCallback(response, callback);
342
0
}
343
344
void
345
U2F::Sign(const nsAString& aAppId,
346
          const nsAString& aChallenge,
347
          const Sequence<RegisteredKey>& aRegisteredKeys,
348
          U2FSignCallback& aCallback,
349
          const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
350
          ErrorResult& aRv)
351
0
{
352
0
  MOZ_ASSERT(NS_IsMainThread());
353
0
354
0
  if (mTransaction.isSome()) {
355
0
    CancelTransaction(NS_ERROR_ABORT);
356
0
  }
357
0
358
0
  nsMainThreadPtrHandle<U2FSignCallback> callback(
359
0
    new nsMainThreadPtrHolder<U2FSignCallback>("U2F::Sign::callback",
360
0
                                               &aCallback));
361
0
362
0
  // Ensure we have a callback.
363
0
  if (NS_WARN_IF(!callback)) {
364
0
    return;
365
0
  }
366
0
367
0
  // Evaluate the AppID
368
0
  nsString adjustedAppId(aAppId);
369
0
  if (!EvaluateAppID(mParent, mOrigin, U2FOperation::Sign, adjustedAppId)) {
370
0
    SignResponse response;
371
0
    response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::BAD_REQUEST));
372
0
    ExecuteCallback(response, callback);
373
0
    return;
374
0
  }
375
0
376
0
  // Produce the AppParam from the current AppID
377
0
  nsCString cAppId = NS_ConvertUTF16toUTF8(adjustedAppId);
378
0
379
0
  nsAutoString clientDataJSON;
380
0
  nsresult rv = AssembleClientData(mOrigin, kGetAssertion, aChallenge,
381
0
                                   clientDataJSON);
382
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
383
0
    SignResponse response;
384
0
    response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::BAD_REQUEST));
385
0
    ExecuteCallback(response, callback);
386
0
    return;
387
0
  }
388
0
389
0
  CryptoBuffer challenge;
390
0
  if (!challenge.Assign(NS_ConvertUTF16toUTF8(aChallenge))) {
391
0
    SignResponse response;
392
0
    response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
393
0
    ExecuteCallback(response, callback);
394
0
    return;
395
0
  }
396
0
397
0
  // Build the key list, if any
398
0
  nsTArray<WebAuthnScopedCredential> permittedList;
399
0
  RegisteredKeysToScopedCredentialList(adjustedAppId, aRegisteredKeys,
400
0
                                       permittedList);
401
0
402
0
  if (!MaybeCreateBackgroundActor()) {
403
0
    SignResponse response;
404
0
    response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OTHER_ERROR));
405
0
    ExecuteCallback(response, callback);
406
0
    return;
407
0
  }
408
0
409
0
  ListenForVisibilityEvents();
410
0
411
0
  // Always blank for U2F
412
0
  nsTArray<WebAuthnExtension> extensions;
413
0
414
0
  NS_ConvertUTF16toUTF8 clientData(clientDataJSON);
415
0
  uint32_t adjustedTimeoutMillis = AdjustedTimeoutMillis(opt_aTimeoutSeconds);
416
0
417
0
  WebAuthnGetAssertionInfo info(mOrigin,
418
0
                                adjustedAppId,
419
0
                                challenge,
420
0
                                clientData,
421
0
                                adjustedTimeoutMillis,
422
0
                                permittedList,
423
0
                                null_t() /* no extra info for U2F */);
424
0
425
0
  MOZ_ASSERT(mTransaction.isNothing());
426
0
  mTransaction = Some(U2FTransaction(AsVariant(callback)));
427
0
  mChild->SendRequestSign(mTransaction.ref().mId, info);
428
0
}
429
430
void
431
U2F::FinishGetAssertion(const uint64_t& aTransactionId,
432
                        const WebAuthnGetAssertionResult& aResult)
433
0
{
434
0
  MOZ_ASSERT(NS_IsMainThread());
435
0
436
0
  // Check for a valid transaction.
437
0
  if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
438
0
    return;
439
0
  }
440
0
441
0
  if (NS_WARN_IF(!mTransaction.ref().HasSignCallback())) {
442
0
    RejectTransaction(NS_ERROR_ABORT);
443
0
    return;
444
0
  }
445
0
446
0
  // A CTAP2 response.
447
0
  if (aResult.SignatureData().Length() == 0) {
448
0
    RejectTransaction(NS_ERROR_ABORT);
449
0
    return;
450
0
  }
451
0
452
0
  CryptoBuffer clientDataBuf;
453
0
  if (NS_WARN_IF(!clientDataBuf.Assign(aResult.ClientDataJSON()))) {
454
0
    RejectTransaction(NS_ERROR_ABORT);
455
0
    return;
456
0
  }
457
0
458
0
  CryptoBuffer credBuf;
459
0
  if (NS_WARN_IF(!credBuf.Assign(aResult.KeyHandle()))) {
460
0
    RejectTransaction(NS_ERROR_ABORT);
461
0
    return;
462
0
  }
463
0
464
0
  CryptoBuffer sigBuf;
465
0
  if (NS_WARN_IF(!sigBuf.Assign(aResult.SignatureData()))) {
466
0
    RejectTransaction(NS_ERROR_ABORT);
467
0
    return;
468
0
  }
469
0
470
0
  // Assemble a response object to return
471
0
  nsString clientDataBase64;
472
0
  nsString signatureDataBase64;
473
0
  nsString keyHandleBase64;
474
0
  nsresult rvClientData = clientDataBuf.ToJwkBase64(clientDataBase64);
475
0
  nsresult rvSignatureData = sigBuf.ToJwkBase64(signatureDataBase64);
476
0
  nsresult rvKeyHandle = credBuf.ToJwkBase64(keyHandleBase64);
477
0
  if (NS_WARN_IF(NS_FAILED(rvClientData)) ||
478
0
      NS_WARN_IF(NS_FAILED(rvSignatureData) ||
479
0
      NS_WARN_IF(NS_FAILED(rvKeyHandle)))) {
480
0
    RejectTransaction(NS_ERROR_ABORT);
481
0
    return;
482
0
  }
483
0
484
0
  SignResponse response;
485
0
  response.mKeyHandle.Construct(keyHandleBase64);
486
0
  response.mClientData.Construct(clientDataBase64);
487
0
  response.mSignatureData.Construct(signatureDataBase64);
488
0
  response.mErrorCode.Construct(static_cast<uint32_t>(ErrorCode::OK));
489
0
490
0
  // Keep the callback pointer alive.
491
0
  nsMainThreadPtrHandle<U2FSignCallback> callback(
492
0
    mTransaction.ref().GetSignCallback());
493
0
494
0
  ClearTransaction();
495
0
  ExecuteCallback(response, callback);
496
0
}
497
498
void
499
U2F::ClearTransaction()
500
0
{
501
0
  if (!NS_WARN_IF(mTransaction.isNothing())) {
502
0
    StopListeningForVisibilityEvents();
503
0
  }
504
0
505
0
  mTransaction.reset();
506
0
}
507
508
void
509
U2F::RejectTransaction(const nsresult& aError)
510
0
{
511
0
  if (NS_WARN_IF(mTransaction.isNothing())) {
512
0
    return;
513
0
  }
514
0
515
0
  StopListeningForVisibilityEvents();
516
0
517
0
  // Clear out mTransaction before calling ExecuteCallback() below to allow
518
0
  // reentrancy from microtask checkpoints.
519
0
  Maybe<U2FTransaction> maybeTransaction(std::move(mTransaction));
520
0
  MOZ_ASSERT(mTransaction.isNothing() && maybeTransaction.isSome());
521
0
522
0
  U2FTransaction& transaction = maybeTransaction.ref();
523
0
  ErrorCode code = ConvertNSResultToErrorCode(aError);
524
0
525
0
  if (transaction.HasRegisterCallback()) {
526
0
    RegisterResponse response;
527
0
    response.mErrorCode.Construct(static_cast<uint32_t>(code));
528
0
    ExecuteCallback(response, transaction.GetRegisterCallback());
529
0
  }
530
0
531
0
  if (transaction.HasSignCallback()) {
532
0
    SignResponse response;
533
0
    response.mErrorCode.Construct(static_cast<uint32_t>(code));
534
0
    ExecuteCallback(response, transaction.GetSignCallback());
535
0
  }
536
0
}
537
538
void
539
U2F::CancelTransaction(const nsresult& aError)
540
0
{
541
0
  if (!NS_WARN_IF(!mChild || mTransaction.isNothing())) {
542
0
    mChild->SendRequestCancel(mTransaction.ref().mId);
543
0
  }
544
0
545
0
  RejectTransaction(aError);
546
0
}
547
548
void
549
U2F::RequestAborted(const uint64_t& aTransactionId, const nsresult& aError)
550
0
{
551
0
  MOZ_ASSERT(NS_IsMainThread());
552
0
553
0
  if (mTransaction.isSome() && mTransaction.ref().mId == aTransactionId) {
554
0
    RejectTransaction(aError);
555
0
  }
556
0
}
557
558
} // namespace dom
559
} // namespace mozilla