tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PaymentRequest.cpp (41211B)


      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/PaymentRequest.h"
      8 
      9 #include "BasicCardPayment.h"
     10 #include "PaymentResponse.h"
     11 #include "mozilla/StaticPrefs_dom.h"
     12 #include "mozilla/dom/Document.h"
     13 #include "mozilla/dom/Element.h"
     14 #include "mozilla/dom/FeaturePolicyUtils.h"
     15 #include "mozilla/dom/MerchantValidationEvent.h"
     16 #include "mozilla/dom/PaymentMethodChangeEvent.h"
     17 #include "mozilla/dom/PaymentRequestChild.h"
     18 #include "mozilla/dom/PaymentRequestManager.h"
     19 #include "mozilla/dom/RootedDictionary.h"
     20 #include "mozilla/dom/UserActivation.h"
     21 #include "mozilla/dom/WindowContext.h"
     22 #include "mozilla/intl/Locale.h"
     23 #include "mozilla/intl/LocaleService.h"
     24 #include "nsContentUtils.h"
     25 #include "nsGlobalWindowInner.h"
     26 #include "nsIDUtils.h"
     27 #include "nsIRegion.h"
     28 #include "nsIScriptError.h"
     29 #include "nsIURLParser.h"
     30 #include "nsImportModule.h"
     31 #include "nsNetCID.h"
     32 #include "nsServiceManagerUtils.h"
     33 
     34 using mozilla::intl::LocaleService;
     35 
     36 namespace mozilla::dom {
     37 
     38 NS_IMPL_CYCLE_COLLECTION_CLASS(PaymentRequest)
     39 
     40 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PaymentRequest,
     41                                               DOMEventTargetHelper)
     42  // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
     43  // DOMEventTargetHelper does it for us.
     44 NS_IMPL_CYCLE_COLLECTION_TRACE_END
     45 
     46 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PaymentRequest,
     47                                                  DOMEventTargetHelper)
     48  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResultPromise)
     49  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAcceptPromise)
     50  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAbortPromise)
     51  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponse)
     52  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mShippingAddress)
     53  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFullShippingAddress)
     54  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
     55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     56 
     57 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PaymentRequest,
     58                                                DOMEventTargetHelper)
     59  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResultPromise)
     60  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAcceptPromise)
     61  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAbortPromise)
     62  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponse)
     63  NS_IMPL_CYCLE_COLLECTION_UNLINK(mShippingAddress)
     64  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFullShippingAddress)
     65  tmp->UnregisterActivityObserver();
     66  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
     67 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     68 
     69 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PaymentRequest)
     70  NS_INTERFACE_MAP_ENTRY(nsIDocumentActivity)
     71 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
     72 
     73 NS_IMPL_ADDREF_INHERITED(PaymentRequest, DOMEventTargetHelper)
     74 NS_IMPL_RELEASE_INHERITED(PaymentRequest, DOMEventTargetHelper)
     75 
     76 bool PaymentRequest::PrefEnabled(JSContext* aCx, JSObject* aObj) {
     77 #if defined(NIGHTLY_BUILD)
     78  if (!XRE_IsContentProcess()) {
     79    return false;
     80  }
     81  if (!StaticPrefs::dom_payments_request_enabled()) {
     82    return false;
     83  }
     84  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
     85  MOZ_ASSERT(manager);
     86 
     87  nsCOMPtr<nsIRegion> regionJsm =
     88      do_ImportESModule("resource://gre/modules/Region.sys.mjs", "Region");
     89  nsAutoString region;
     90  nsresult rv = regionJsm->GetHome(region);
     91  if (NS_FAILED(rv)) {
     92    return false;
     93  }
     94 
     95  if (!manager->IsRegionSupported(region)) {
     96    return false;
     97  }
     98  nsAutoCString locale;
     99  LocaleService::GetInstance()->GetAppLocaleAsBCP47(locale);
    100  mozilla::intl::Locale loc;
    101  auto result = mozilla::intl::LocaleParser::TryParse(locale, loc);
    102  if (!(result.isOk() && loc.Canonicalize().isOk() &&
    103        loc.Language().EqualTo("en") && loc.Region().EqualTo("US"))) {
    104    return false;
    105  }
    106 
    107  return true;
    108 #else
    109  return false;
    110 #endif
    111 }
    112 
    113 void PaymentRequest::IsValidStandardizedPMI(const nsAString& aIdentifier,
    114                                            ErrorResult& aRv) {
    115  /*
    116   *   The syntax of a standardized payment method identifier is given by the
    117   *   following [ABNF]:
    118   *
    119   *       stdpmi = part *( "-" part )
    120   *       part = 1loweralpha *( DIGIT / loweralpha )
    121   *       loweralpha =  %x61-7A
    122   */
    123  const char16_t* start = aIdentifier.BeginReading();
    124  const char16_t* end = aIdentifier.EndReading();
    125  while (start != end) {
    126    // the first char must be in the range %x61-7A
    127    if ((*start < 'a' || *start > 'z')) {
    128      nsAutoCString error;
    129      error.AssignLiteral("'");
    130      error.Append(NS_ConvertUTF16toUTF8(aIdentifier));
    131      error.AppendLiteral("' is not valid. The character '");
    132      error.Append(NS_ConvertUTF16toUTF8(start, 1));
    133      error.AppendLiteral(
    134          "' at the beginning or after the '-' must be in the range [a-z].");
    135      aRv.ThrowRangeError(error);
    136      return;
    137    }
    138    ++start;
    139    // the rest can be in the range %x61-7A + DIGITs
    140    while (start != end && *start != '-' &&
    141           ((*start >= 'a' && *start <= 'z') ||
    142            (*start >= '0' && *start <= '9'))) {
    143      ++start;
    144    }
    145    // if the char is not in the range %x61-7A + DIGITs, it must be '-'
    146    if (start != end && *start != '-') {
    147      nsAutoCString error;
    148      error.AssignLiteral("'");
    149      error.Append(NS_ConvertUTF16toUTF8(aIdentifier));
    150      error.AppendLiteral("' is not valid. The character '");
    151      error.Append(NS_ConvertUTF16toUTF8(start, 1));
    152      error.AppendLiteral("' must be in the range [a-zA-z0-9-].");
    153      aRv.ThrowRangeError(error);
    154      return;
    155    }
    156    if (*start == '-') {
    157      ++start;
    158      // the last char can not be '-'
    159      if (start == end) {
    160        nsAutoCString error;
    161        error.AssignLiteral("'");
    162        error.Append(NS_ConvertUTF16toUTF8(aIdentifier));
    163        error.AppendLiteral("' is not valid. The last character '");
    164        error.Append(NS_ConvertUTF16toUTF8(start, 1));
    165        error.AppendLiteral("' must be in the range [a-z0-9].");
    166        aRv.ThrowRangeError(error);
    167        return;
    168      }
    169    }
    170  }
    171 }
    172 
    173 void PaymentRequest::IsValidPaymentMethodIdentifier(
    174    const nsAString& aIdentifier, ErrorResult& aRv) {
    175  if (aIdentifier.IsEmpty()) {
    176    aRv.ThrowTypeError("Payment method identifier is required.");
    177    return;
    178  }
    179  /*
    180   *  URL-based payment method identifier
    181   *
    182   *  1. If url's scheme is not "https", return false.
    183   *  2. If url's username or password is not the empty string, return false.
    184   *  3. Otherwise, return true.
    185   */
    186  nsCOMPtr<nsIURLParser> urlParser = do_GetService(NS_STDURLPARSER_CONTRACTID);
    187  MOZ_ASSERT(urlParser);
    188  uint32_t schemePos = 0;
    189  int32_t schemeLen = 0;
    190  uint32_t authorityPos = 0;
    191  int32_t authorityLen = 0;
    192  NS_ConvertUTF16toUTF8 url(aIdentifier);
    193  nsresult rv =
    194      urlParser->ParseURL(url.get(), url.Length(), &schemePos, &schemeLen,
    195                          &authorityPos, &authorityLen, nullptr, nullptr);
    196  if (NS_FAILED(rv)) {
    197    nsAutoCString error;
    198    error.AppendLiteral("Error parsing payment method identifier '");
    199    error.Append(NS_ConvertUTF16toUTF8(aIdentifier));
    200    error.AppendLiteral("'as a URL.");
    201    aRv.ThrowRangeError(error);
    202    return;
    203  }
    204 
    205  if (schemeLen == -1) {
    206    // The PMI is not a URL-based PMI, check if it is a standardized PMI
    207    IsValidStandardizedPMI(aIdentifier, aRv);
    208    return;
    209  }
    210  if (!Substring(aIdentifier, schemePos, schemeLen).EqualsASCII("https")) {
    211    nsAutoCString error;
    212    error.AssignLiteral("'");
    213    error.Append(NS_ConvertUTF16toUTF8(aIdentifier));
    214    error.AppendLiteral("' is not valid. The scheme must be 'https'.");
    215    aRv.ThrowRangeError(error);
    216    return;
    217  }
    218  if (Substring(aIdentifier, authorityPos, authorityLen).IsEmpty()) {
    219    nsAutoCString error;
    220    error.AssignLiteral("'");
    221    error.Append(NS_ConvertUTF16toUTF8(aIdentifier));
    222    error.AppendLiteral("' is not valid. hostname can not be empty.");
    223    aRv.ThrowRangeError(error);
    224    return;
    225  }
    226 
    227  uint32_t usernamePos = 0;
    228  int32_t usernameLen = 0;
    229  uint32_t passwordPos = 0;
    230  int32_t passwordLen = 0;
    231  uint32_t hostnamePos = 0;
    232  int32_t hostnameLen = 0;
    233  int32_t port = 0;
    234 
    235  NS_ConvertUTF16toUTF8 authority(
    236      Substring(aIdentifier, authorityPos, authorityLen));
    237  rv = urlParser->ParseAuthority(
    238      authority.get(), authority.Length(), &usernamePos, &usernameLen,
    239      &passwordPos, &passwordLen, &hostnamePos, &hostnameLen, &port);
    240  if (NS_FAILED(rv)) {
    241    // Handle the special cases that URLParser treats it as an invalid URL, but
    242    // are used in web-platform-test
    243    // For exmaple:
    244    //     https://:@example.com             // should be considered as valid
    245    //     https://:password@example.com.    // should be considered as invalid
    246    int32_t atPos = authority.FindChar('@');
    247    if (atPos >= 0) {
    248      // only accept the case https://:@xxx
    249      if (atPos == 1 && authority.CharAt(0) == ':') {
    250        usernamePos = 0;
    251        usernameLen = 0;
    252        passwordPos = 0;
    253        passwordLen = 0;
    254      } else {
    255        // for the fail cases, don't care about what the actual length is.
    256        usernamePos = 0;
    257        usernameLen = INT32_MAX;
    258        passwordPos = 0;
    259        passwordLen = INT32_MAX;
    260      }
    261    } else {
    262      usernamePos = 0;
    263      usernameLen = -1;
    264      passwordPos = 0;
    265      passwordLen = -1;
    266    }
    267    // Parse server information when both username and password are empty or do
    268    // not exist.
    269    if ((usernameLen <= 0) && (passwordLen <= 0)) {
    270      if (authority.Length() - atPos - 1 == 0) {
    271        nsAutoCString error;
    272        error.AssignLiteral("'");
    273        error.Append(NS_ConvertUTF16toUTF8(aIdentifier));
    274        error.AppendLiteral("' is not valid. hostname can not be empty.");
    275        aRv.ThrowRangeError(error);
    276        return;
    277      }
    278      // Re-using nsIURLParser::ParseServerInfo to extract the hostname and port
    279      // information. This can help us to handle complicated IPv6 cases.
    280      nsAutoCString serverInfo(
    281          Substring(authority, atPos + 1, authority.Length() - atPos - 1));
    282      rv = urlParser->ParseServerInfo(serverInfo.get(), serverInfo.Length(),
    283                                      &hostnamePos, &hostnameLen, &port);
    284      if (NS_FAILED(rv)) {
    285        // ParseServerInfo returns NS_ERROR_MALFORMED_URI in all fail cases, we
    286        // probably need a followup bug to figure out the fail reason.
    287        nsAutoCString error;
    288        error.AssignLiteral("Error extracting hostname from '");
    289        error.Append(serverInfo);
    290        error.AppendLiteral("'.");
    291        aRv.ThrowRangeError(error);
    292        return;
    293      }
    294    }
    295  }
    296  // PMI is valid when usernameLen/passwordLen equals to -1 or 0.
    297  if (usernameLen > 0 || passwordLen > 0) {
    298    nsAutoCString error;
    299    error.AssignLiteral("'");
    300    error.Append(NS_ConvertUTF16toUTF8(aIdentifier));
    301    error.AssignLiteral("' is not valid. Username and password must be empty.");
    302    aRv.ThrowRangeError(error);
    303    return;
    304  }
    305 
    306  // PMI is valid when hostnameLen is larger than 0
    307  if (hostnameLen <= 0) {
    308    nsAutoCString error;
    309    error.AssignLiteral("'");
    310    error.Append(NS_ConvertUTF16toUTF8(aIdentifier));
    311    error.AppendLiteral("' is not valid. hostname can not be empty.");
    312    aRv.ThrowRangeError(error);
    313    return;
    314  }
    315 }
    316 
    317 void PaymentRequest::IsValidMethodData(
    318    JSContext* aCx, const Sequence<PaymentMethodData>& aMethodData,
    319    ErrorResult& aRv) {
    320  if (!aMethodData.Length()) {
    321    aRv.ThrowTypeError("At least one payment method is required.");
    322    return;
    323  }
    324 
    325  nsTArray<nsString> methods;
    326  for (const PaymentMethodData& methodData : aMethodData) {
    327    IsValidPaymentMethodIdentifier(methodData.mSupportedMethods, aRv);
    328    if (aRv.Failed()) {
    329      return;
    330    }
    331 
    332    RefPtr<BasicCardService> service = BasicCardService::GetService();
    333    MOZ_ASSERT(service);
    334    if (service->IsBasicCardPayment(methodData.mSupportedMethods)) {
    335      if (!methodData.mData.WasPassed()) {
    336        continue;
    337      }
    338      MOZ_ASSERT(aCx);
    339      nsAutoString error;
    340      if (!service->IsValidBasicCardRequest(aCx, methodData.mData.Value(),
    341                                            error)) {
    342        aRv.ThrowTypeError(NS_ConvertUTF16toUTF8(error));
    343        return;
    344      }
    345    }
    346    if (!methods.Contains(methodData.mSupportedMethods)) {
    347      methods.AppendElement(methodData.mSupportedMethods);
    348    } else {
    349      aRv.ThrowRangeError(nsPrintfCString(
    350          "Duplicate payment method '%s'",
    351          NS_ConvertUTF16toUTF8(methodData.mSupportedMethods).get()));
    352      return;
    353    }
    354  }
    355 }
    356 
    357 void PaymentRequest::IsValidNumber(const nsAString& aItem,
    358                                   const nsAString& aStr, ErrorResult& aRv) {
    359  nsresult error = NS_ERROR_FAILURE;
    360 
    361  if (!aStr.IsEmpty()) {
    362    nsAutoString aValue(aStr);
    363 
    364    // If the beginning character is '-', we will check the second one.
    365    int beginningIndex = (aValue.First() == '-') ? 1 : 0;
    366 
    367    // Ensure
    368    // - the beginning character is a digit in [0-9], and
    369    // - the last character is not '.'
    370    // to follow spec:
    371    //   https://w3c.github.io/browser-payment-api/#dfn-valid-decimal-monetary-value
    372    //
    373    // For example, ".1" is not valid for '.' is not in [0-9],
    374    // and " 0.1" either for beginning with ' '
    375    if (aValue.Last() != '.' && aValue.CharAt(beginningIndex) >= '0' &&
    376        aValue.CharAt(beginningIndex) <= '9') {
    377      aValue.ToFloat(&error);
    378    }
    379  }
    380 
    381  if (NS_FAILED(error)) {
    382    nsAutoCString errorMsg;
    383    errorMsg.AssignLiteral("The amount.value of \"");
    384    errorMsg.Append(NS_ConvertUTF16toUTF8(aItem));
    385    errorMsg.AppendLiteral("\"(");
    386    errorMsg.Append(NS_ConvertUTF16toUTF8(aStr));
    387    errorMsg.AppendLiteral(") must be a valid decimal monetary value.");
    388    aRv.ThrowTypeError(errorMsg);
    389    return;
    390  }
    391 }
    392 
    393 void PaymentRequest::IsNonNegativeNumber(const nsAString& aItem,
    394                                         const nsAString& aStr,
    395                                         ErrorResult& aRv) {
    396  nsresult error = NS_ERROR_FAILURE;
    397 
    398  if (!aStr.IsEmpty()) {
    399    nsAutoString aValue(aStr);
    400    // Ensure
    401    // - the beginning character is a digit in [0-9], and
    402    // - the last character is not '.'
    403    if (aValue.Last() != '.' && aValue.First() >= '0' &&
    404        aValue.First() <= '9') {
    405      aValue.ToFloat(&error);
    406    }
    407  }
    408 
    409  if (NS_FAILED(error)) {
    410    nsAutoCString errorMsg;
    411    errorMsg.AssignLiteral("The amount.value of \"");
    412    errorMsg.Append(NS_ConvertUTF16toUTF8(aItem));
    413    errorMsg.AppendLiteral("\"(");
    414    errorMsg.Append(NS_ConvertUTF16toUTF8(aStr));
    415    errorMsg.AppendLiteral(
    416        ") must be a valid and non-negative decimal monetary value.");
    417    aRv.ThrowTypeError(errorMsg);
    418    return;
    419  }
    420 }
    421 
    422 void PaymentRequest::IsValidCurrency(const nsAString& aItem,
    423                                     const nsAString& aCurrency,
    424                                     ErrorResult& aRv) {
    425  /*
    426   *  According to spec in
    427   * https://w3c.github.io/payment-request/#validity-checkers, perform currency
    428   * validation with following criteria
    429   *  1. The currency length must be 3.
    430   *  2. The currency contains any character that must be in the range "A" to
    431   * "Z" (U+0041 to U+005A) or the range "a" to "z" (U+0061 to U+007A)
    432   */
    433  if (aCurrency.Length() != 3) {
    434    nsAutoCString error;
    435    error.AssignLiteral("The length amount.currency of \"");
    436    error.Append(NS_ConvertUTF16toUTF8(aItem));
    437    error.AppendLiteral("\"(");
    438    error.Append(NS_ConvertUTF16toUTF8(aCurrency));
    439    error.AppendLiteral(") must be 3.");
    440    aRv.ThrowRangeError(error);
    441    return;
    442  }
    443  // Don't use nsUnicharUtils::ToUpperCase, it converts the invalid "ınr" PMI to
    444  // to the valid one "INR".
    445  for (uint32_t idx = 0; idx < aCurrency.Length(); ++idx) {
    446    if ((aCurrency.CharAt(idx) >= 'A' && aCurrency.CharAt(idx) <= 'Z') ||
    447        (aCurrency.CharAt(idx) >= 'a' && aCurrency.CharAt(idx) <= 'z')) {
    448      continue;
    449    }
    450    nsAutoCString error;
    451    error.AssignLiteral("The character amount.currency of \"");
    452    error.Append(NS_ConvertUTF16toUTF8(aItem));
    453    error.AppendLiteral("\"(");
    454    error.Append(NS_ConvertUTF16toUTF8(aCurrency));
    455    error.AppendLiteral(
    456        ") must be in the range 'A' to 'Z'(U+0041 to U+005A) or 'a' to "
    457        "'z'(U+0061 to U+007A).");
    458    aRv.ThrowRangeError(error);
    459    return;
    460  }
    461 }
    462 
    463 void PaymentRequest::IsValidCurrencyAmount(const nsAString& aItem,
    464                                           const PaymentCurrencyAmount& aAmount,
    465                                           const bool aIsTotalItem,
    466                                           ErrorResult& aRv) {
    467  IsValidCurrency(aItem, aAmount.mCurrency, aRv);
    468  if (aRv.Failed()) {
    469    return;
    470  }
    471  if (aIsTotalItem) {
    472    IsNonNegativeNumber(aItem, aAmount.mValue, aRv);
    473    if (aRv.Failed()) {
    474      return;
    475    }
    476  } else {
    477    IsValidNumber(aItem, aAmount.mValue, aRv);
    478    if (aRv.Failed()) {
    479      return;
    480    }
    481  }
    482 }
    483 
    484 void PaymentRequest::IsValidDetailsInit(const PaymentDetailsInit& aDetails,
    485                                        const bool aRequestShipping,
    486                                        ErrorResult& aRv) {
    487  // Check the amount.value and amount.currency of detail.total
    488  IsValidCurrencyAmount(u"details.total"_ns, aDetails.mTotal.mAmount,
    489                        true,  // isTotalItem
    490                        aRv);
    491  if (aRv.Failed()) {
    492    return;
    493  }
    494  return IsValidDetailsBase(aDetails, aRequestShipping, aRv);
    495 }
    496 
    497 void PaymentRequest::IsValidDetailsUpdate(const PaymentDetailsUpdate& aDetails,
    498                                          const bool aRequestShipping,
    499                                          ErrorResult& aRv) {
    500  // Check the amount.value and amount.currency of detail.total
    501  if (aDetails.mTotal.WasPassed()) {
    502    IsValidCurrencyAmount(u"details.total"_ns, aDetails.mTotal.Value().mAmount,
    503                          true,  // isTotalItem
    504                          aRv);
    505    if (aRv.Failed()) {
    506      return;
    507    }
    508  }
    509  IsValidDetailsBase(aDetails, aRequestShipping, aRv);
    510 }
    511 
    512 void PaymentRequest::IsValidDetailsBase(const PaymentDetailsBase& aDetails,
    513                                        const bool aRequestShipping,
    514                                        ErrorResult& aRv) {
    515  // Check the amount.value of each item in the display items
    516  if (aDetails.mDisplayItems.WasPassed()) {
    517    const Sequence<PaymentItem>& displayItems = aDetails.mDisplayItems.Value();
    518    for (const PaymentItem& displayItem : displayItems) {
    519      IsValidCurrencyAmount(displayItem.mLabel, displayItem.mAmount,
    520                            false,  // isTotalItem
    521                            aRv);
    522      if (aRv.Failed()) {
    523        return;
    524      }
    525    }
    526  }
    527 
    528  // Check the shipping option
    529  if (aDetails.mShippingOptions.WasPassed() && aRequestShipping) {
    530    const Sequence<PaymentShippingOption>& shippingOptions =
    531        aDetails.mShippingOptions.Value();
    532    nsTArray<nsString> seenIDs;
    533    for (const PaymentShippingOption& shippingOption : shippingOptions) {
    534      IsValidCurrencyAmount(u"details.shippingOptions"_ns,
    535                            shippingOption.mAmount,
    536                            false,  // isTotalItem
    537                            aRv);
    538      if (aRv.Failed()) {
    539        return;
    540      }
    541      if (seenIDs.Contains(shippingOption.mId)) {
    542        nsAutoCString error;
    543        error.AssignLiteral("Duplicate shippingOption id '");
    544        error.Append(NS_ConvertUTF16toUTF8(shippingOption.mId));
    545        error.AppendLiteral("'");
    546        aRv.ThrowTypeError(error);
    547        return;
    548      }
    549      seenIDs.AppendElement(shippingOption.mId);
    550    }
    551  }
    552 
    553  // Check payment details modifiers
    554  if (aDetails.mModifiers.WasPassed()) {
    555    const Sequence<PaymentDetailsModifier>& modifiers =
    556        aDetails.mModifiers.Value();
    557    for (const PaymentDetailsModifier& modifier : modifiers) {
    558      IsValidPaymentMethodIdentifier(modifier.mSupportedMethods, aRv);
    559      if (aRv.Failed()) {
    560        return;
    561      }
    562      if (modifier.mTotal.WasPassed()) {
    563        IsValidCurrencyAmount(u"details.modifiers.total"_ns,
    564                              modifier.mTotal.Value().mAmount,
    565                              true,  // isTotalItem
    566                              aRv);
    567        if (aRv.Failed()) {
    568          return;
    569        }
    570      }
    571      if (modifier.mAdditionalDisplayItems.WasPassed()) {
    572        const Sequence<PaymentItem>& displayItems =
    573            modifier.mAdditionalDisplayItems.Value();
    574        for (const PaymentItem& displayItem : displayItems) {
    575          IsValidCurrencyAmount(displayItem.mLabel, displayItem.mAmount,
    576                                false,  // isTotalItem
    577                                aRv);
    578          if (aRv.Failed()) {
    579            return;
    580          }
    581        }
    582      }
    583    }
    584  }
    585 }
    586 
    587 already_AddRefed<PaymentRequest> PaymentRequest::Constructor(
    588    const GlobalObject& aGlobal, const Sequence<PaymentMethodData>& aMethodData,
    589    const PaymentDetailsInit& aDetails, const PaymentOptions& aOptions,
    590    ErrorResult& aRv) {
    591  nsCOMPtr<nsPIDOMWindowInner> window =
    592      do_QueryInterface(aGlobal.GetAsSupports());
    593  if (!window) {
    594    aRv.ThrowAbortError("No global object for creating PaymentRequest");
    595    return nullptr;
    596  }
    597 
    598  nsCOMPtr<Document> doc = window->GetExtantDoc();
    599  if (!doc) {
    600    aRv.ThrowAbortError("No document for creating PaymentRequest");
    601    return nullptr;
    602  }
    603 
    604  // the feature can only be used in an active document
    605  if (!doc->IsCurrentActiveDocument()) {
    606    aRv.ThrowSecurityError(
    607        "Can't create a PaymentRequest for an inactive document");
    608    return nullptr;
    609  }
    610 
    611  if (!FeaturePolicyUtils::IsFeatureAllowed(doc, u"payment"_ns)) {
    612    aRv.ThrowSecurityError(
    613        "Document's Feature Policy does not allow to create a PaymentRequest");
    614    return nullptr;
    615  }
    616 
    617  // Get the top same process document
    618  nsCOMPtr<Document> topSameProcessDoc = doc;
    619  topSameProcessDoc = doc;
    620  while (topSameProcessDoc) {
    621    nsCOMPtr<Document> parent = topSameProcessDoc->GetInProcessParentDocument();
    622    if (!parent || !parent->IsContentDocument()) {
    623      break;
    624    }
    625    topSameProcessDoc = parent;
    626  }
    627  nsCOMPtr<nsIPrincipal> topLevelPrincipal = topSameProcessDoc->NodePrincipal();
    628 
    629  // Check payment methods and details
    630  IsValidMethodData(aGlobal.Context(), aMethodData, aRv);
    631  if (aRv.Failed()) {
    632    return nullptr;
    633  }
    634  IsValidDetailsInit(aDetails, aOptions.mRequestShipping, aRv);
    635  if (aRv.Failed()) {
    636    return nullptr;
    637  }
    638 
    639  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
    640  if (NS_WARN_IF(!manager)) {
    641    return nullptr;
    642  }
    643 
    644  // Create PaymentRequest and set its |mId|
    645  RefPtr<PaymentRequest> request;
    646  manager->CreatePayment(aGlobal.Context(), window, topLevelPrincipal,
    647                         aMethodData, aDetails, aOptions,
    648                         getter_AddRefs(request), aRv);
    649  if (aRv.Failed()) {
    650    return nullptr;
    651  }
    652  return request.forget();
    653 }
    654 
    655 already_AddRefed<PaymentRequest> PaymentRequest::CreatePaymentRequest(
    656    nsPIDOMWindowInner* aWindow, ErrorResult& aRv) {
    657  // Generate a unique id for identification
    658  nsID uuid;
    659  if (NS_WARN_IF(NS_FAILED(nsID::GenerateUUIDInPlace(uuid)))) {
    660    aRv.ThrowAbortError(
    661        "Failed to create an internal UUID for the PaymentRequest");
    662    return nullptr;
    663  }
    664 
    665  NSID_TrimBracketsUTF16 id(uuid);
    666 
    667  // Create payment request with generated id
    668  RefPtr<PaymentRequest> request = new PaymentRequest(aWindow, id);
    669  return request.forget();
    670 }
    671 
    672 PaymentRequest::PaymentRequest(nsPIDOMWindowInner* aWindow,
    673                               const nsAString& aInternalId)
    674    : DOMEventTargetHelper(aWindow),
    675      mInternalId(aInternalId),
    676      mShippingAddress(nullptr),
    677      mUpdating(false),
    678      mRequestShipping(false),
    679      mState(eCreated),
    680      mIPC(nullptr) {
    681  MOZ_ASSERT(aWindow);
    682  RegisterActivityObserver();
    683 }
    684 
    685 already_AddRefed<Promise> PaymentRequest::CanMakePayment(ErrorResult& aRv) {
    686  if (!InFullyActiveDocument()) {
    687    aRv.ThrowAbortError("The owner document is not fully active");
    688    return nullptr;
    689  }
    690 
    691  if (mState != eCreated) {
    692    aRv.ThrowInvalidStateError(
    693        "The PaymentRequest's state should be 'Created'");
    694    return nullptr;
    695  }
    696 
    697  if (mResultPromise) {
    698    // XXX This doesn't match the spec but does match Chromium.
    699    aRv.ThrowNotAllowedError(
    700        "PaymentRequest.CanMakePayment() has already been called");
    701    return nullptr;
    702  }
    703 
    704  nsIGlobalObject* global = GetOwnerGlobal();
    705  RefPtr<Promise> promise = Promise::Create(global, aRv);
    706  if (aRv.Failed()) {
    707    return nullptr;
    708  }
    709 
    710  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
    711  MOZ_ASSERT(manager);
    712  manager->CanMakePayment(this, aRv);
    713  if (aRv.Failed()) {
    714    return nullptr;
    715  }
    716  mResultPromise = promise;
    717  return promise.forget();
    718 }
    719 
    720 void PaymentRequest::RespondCanMakePayment(bool aResult) {
    721  MOZ_ASSERT(mResultPromise);
    722  mResultPromise->MaybeResolve(aResult);
    723  mResultPromise = nullptr;
    724 }
    725 
    726 already_AddRefed<Promise> PaymentRequest::Show(
    727    const Optional<OwningNonNull<Promise>>& aDetailsPromise, ErrorResult& aRv) {
    728  if (!InFullyActiveDocument()) {
    729    aRv.ThrowAbortError("The owner document is not fully active");
    730    return nullptr;
    731  }
    732 
    733  nsIGlobalObject* global = GetOwnerGlobal();
    734  nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
    735  Document* doc = win->GetExtantDoc();
    736 
    737  if (!UserActivation::IsHandlingUserInput()) {
    738    nsString msg = nsLiteralString(
    739        u"User activation is now required to call PaymentRequest.show()");
    740    nsContentUtils::ReportToConsoleNonLocalized(
    741        msg, nsIScriptError::warningFlag, "Security"_ns, doc);
    742    if (StaticPrefs::dom_payments_request_user_interaction_required()) {
    743      aRv.ThrowSecurityError(NS_ConvertUTF16toUTF8(msg));
    744      return nullptr;
    745    }
    746  }
    747 
    748  if (mState != eCreated) {
    749    aRv.ThrowInvalidStateError(
    750        "The PaymentRequest's state should be 'Created'");
    751    return nullptr;
    752  }
    753 
    754  RefPtr<Promise> promise = Promise::Create(global, aRv);
    755  if (aRv.Failed()) {
    756    mState = eClosed;
    757    return nullptr;
    758  }
    759 
    760  if (aDetailsPromise.WasPassed()) {
    761    aDetailsPromise.Value().AppendNativeHandler(this);
    762    mUpdating = true;
    763  }
    764 
    765  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
    766  MOZ_ASSERT(manager);
    767  manager->ShowPayment(this, aRv);
    768  if (aRv.Failed()) {
    769    mState = eClosed;
    770    return nullptr;
    771  }
    772 
    773  mAcceptPromise = promise;
    774  mState = eInteractive;
    775  return promise.forget();
    776 }
    777 
    778 void PaymentRequest::RejectShowPayment(ErrorResult&& aRejectReason) {
    779  MOZ_ASSERT(mAcceptPromise || mResponse);
    780  MOZ_ASSERT(mState == eInteractive);
    781 
    782  if (mResponse) {
    783    mResponse->RejectRetry(std::move(aRejectReason));
    784  } else {
    785    mAcceptPromise->MaybeReject(std::move(aRejectReason));
    786  }
    787  mState = eClosed;
    788  mAcceptPromise = nullptr;
    789 }
    790 
    791 void PaymentRequest::RespondShowPayment(const nsAString& aMethodName,
    792                                        const ResponseData& aDetails,
    793                                        const nsAString& aPayerName,
    794                                        const nsAString& aPayerEmail,
    795                                        const nsAString& aPayerPhone,
    796                                        ErrorResult&& aResult) {
    797  MOZ_ASSERT(mState == eInteractive);
    798 
    799  if (aResult.Failed()) {
    800    RejectShowPayment(std::move(aResult));
    801    return;
    802  }
    803 
    804  // https://github.com/w3c/payment-request/issues/692
    805  mShippingAddress.swap(mFullShippingAddress);
    806  mFullShippingAddress = nullptr;
    807 
    808  if (mResponse) {
    809    mResponse->RespondRetry(aMethodName, mShippingOption, mShippingAddress,
    810                            aDetails, aPayerName, aPayerEmail, aPayerPhone);
    811  } else if (mAcceptPromise) {
    812    RefPtr<PaymentResponse> paymentResponse = new PaymentResponse(
    813        GetOwnerWindow(), this, mId, aMethodName, mShippingOption,
    814        mShippingAddress, aDetails, aPayerName, aPayerEmail, aPayerPhone);
    815    mResponse = paymentResponse;
    816    mAcceptPromise->MaybeResolve(paymentResponse);
    817  } else {
    818    // mAccpetPromise could be nulled through document activity changed. And
    819    // there is nothing to do here.
    820    mState = eClosed;
    821    return;
    822  }
    823 
    824  mState = eClosed;
    825  mAcceptPromise = nullptr;
    826 }
    827 
    828 void PaymentRequest::RespondComplete() {
    829  MOZ_ASSERT(mResponse);
    830  mResponse->RespondComplete();
    831 }
    832 
    833 already_AddRefed<Promise> PaymentRequest::Abort(ErrorResult& aRv) {
    834  if (!InFullyActiveDocument()) {
    835    aRv.ThrowAbortError("The owner document is not fully active");
    836    return nullptr;
    837  }
    838 
    839  if (mState != eInteractive) {
    840    aRv.ThrowSecurityError(
    841        "The PaymentRequest's state should be 'Interactive'");
    842    return nullptr;
    843  }
    844 
    845  if (mAbortPromise) {
    846    aRv.ThrowInvalidStateError(
    847        "PaymentRequest.abort() has already been called");
    848    return nullptr;
    849  }
    850 
    851  nsIGlobalObject* global = GetOwnerGlobal();
    852  RefPtr<Promise> promise = Promise::Create(global, aRv);
    853  if (aRv.Failed()) {
    854    return nullptr;
    855  }
    856 
    857  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
    858  MOZ_ASSERT(manager);
    859  manager->AbortPayment(this, aRv);
    860  if (aRv.Failed()) {
    861    return nullptr;
    862  }
    863 
    864  mAbortPromise = promise;
    865  return promise.forget();
    866 }
    867 
    868 void PaymentRequest::RespondAbortPayment(bool aSuccess) {
    869  // Check whether we are aborting the update:
    870  //
    871  // - If |mUpdateError| is failed, we are aborting the update as
    872  //   |mUpdateError| was set in method |AbortUpdate|.
    873  //   => Reject |mAcceptPromise| and reset |mUpdateError| to complete
    874  //      the action, regardless of |aSuccess|.
    875  //
    876  // - Otherwise, we are handling |Abort| method call from merchant.
    877  //   => Resolve/Reject |mAbortPromise| based on |aSuccess|.
    878  if (mUpdateError.Failed()) {
    879    // Respond show with mUpdateError, set mUpdating to false.
    880    mUpdating = false;
    881    RespondShowPayment(u""_ns, ResponseData(), u""_ns, u""_ns, u""_ns,
    882                       std::move(mUpdateError));
    883    return;
    884  }
    885 
    886  if (mState != eInteractive) {
    887    return;
    888  }
    889 
    890  if (mAbortPromise) {
    891    if (aSuccess) {
    892      mAbortPromise->MaybeResolve(JS::UndefinedHandleValue);
    893      mAbortPromise = nullptr;
    894      ErrorResult abortResult;
    895      abortResult.ThrowAbortError("The PaymentRequest is aborted");
    896      RejectShowPayment(std::move(abortResult));
    897    } else {
    898      mAbortPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    899      mAbortPromise = nullptr;
    900    }
    901  }
    902 }
    903 
    904 void PaymentRequest::UpdatePayment(JSContext* aCx,
    905                                   const PaymentDetailsUpdate& aDetails,
    906                                   ErrorResult& aRv) {
    907  MOZ_ASSERT(aCx);
    908  if (mState != eInteractive) {
    909    aRv.ThrowInvalidStateError(
    910        "The PaymentRequest state should be 'Interactive'");
    911    return;
    912  }
    913  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
    914  MOZ_ASSERT(manager);
    915  manager->UpdatePayment(aCx, this, aDetails, mRequestShipping, aRv);
    916 }
    917 
    918 void PaymentRequest::AbortUpdate(ErrorResult& aReason) {
    919  // AbortUpdate has the responsiblity to call aReason.SuppressException() when
    920  // fail to update.
    921 
    922  MOZ_ASSERT(aReason.Failed());
    923 
    924  // Completely ignoring the call when the owner document is not fully active.
    925  if (!InFullyActiveDocument()) {
    926    aReason.SuppressException();
    927    return;
    928  }
    929 
    930  // Completely ignoring the call when the PaymentRequest state is not
    931  // eInteractive.
    932  if (mState != eInteractive) {
    933    aReason.SuppressException();
    934    return;
    935  }
    936  // Try to close down any remaining user interface. Should recevie
    937  // RespondAbortPayment from chrome process.
    938  // Completely ignoring the call when failed to send action to chrome process.
    939  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
    940  MOZ_ASSERT(manager);
    941  IgnoredErrorResult result;
    942  manager->AbortPayment(this, result);
    943  if (result.Failed()) {
    944    aReason.SuppressException();
    945    return;
    946  }
    947 
    948  // Remember update error |aReason| and do the following steps in
    949  // RespondShowPayment.
    950  // 1. Set target.state to closed
    951  // 2. Reject the promise target.acceptPromise with exception "aRv"
    952  // 3. Abort the algorithm with update error
    953  mUpdateError = std::move(aReason);
    954 }
    955 
    956 void PaymentRequest::RetryPayment(JSContext* aCx,
    957                                  const PaymentValidationErrors& aErrors,
    958                                  ErrorResult& aRv) {
    959  if (mState == eInteractive) {
    960    aRv.ThrowInvalidStateError(
    961        "Call Retry() when the PaymentReqeust state is 'Interactive'");
    962    return;
    963  }
    964  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
    965  MOZ_ASSERT(manager);
    966  manager->RetryPayment(aCx, this, aErrors, aRv);
    967  if (aRv.Failed()) {
    968    return;
    969  }
    970  mState = eInteractive;
    971 }
    972 
    973 void PaymentRequest::GetId(nsAString& aRetVal) const { aRetVal = mId; }
    974 
    975 void PaymentRequest::GetInternalId(nsAString& aRetVal) {
    976  aRetVal = mInternalId;
    977 }
    978 
    979 void PaymentRequest::SetId(const nsAString& aId) { mId = aId; }
    980 
    981 bool PaymentRequest::Equals(const nsAString& aInternalId) const {
    982  return mInternalId.Equals(aInternalId);
    983 }
    984 
    985 bool PaymentRequest::ReadyForUpdate() {
    986  return mState == eInteractive && !mUpdating;
    987 }
    988 
    989 void PaymentRequest::SetUpdating(bool aUpdating) { mUpdating = aUpdating; }
    990 
    991 already_AddRefed<PaymentResponse> PaymentRequest::GetResponse() const {
    992  RefPtr<PaymentResponse> response = mResponse;
    993  return response.forget();
    994 }
    995 
    996 nsresult PaymentRequest::DispatchUpdateEvent(const nsAString& aType) {
    997  MOZ_ASSERT(ReadyForUpdate());
    998 
    999  PaymentRequestUpdateEventInit init;
   1000  init.mBubbles = false;
   1001  init.mCancelable = false;
   1002 
   1003  RefPtr<PaymentRequestUpdateEvent> event =
   1004      PaymentRequestUpdateEvent::Constructor(this, aType, init);
   1005  event->SetTrusted(true);
   1006  event->SetRequest(this);
   1007 
   1008  ErrorResult rv;
   1009  DispatchEvent(*event, rv);
   1010  return rv.StealNSResult();
   1011 }
   1012 
   1013 nsresult PaymentRequest::DispatchMerchantValidationEvent(
   1014    const nsAString& aType) {
   1015  MOZ_ASSERT(ReadyForUpdate());
   1016 
   1017  MerchantValidationEventInit init;
   1018  init.mBubbles = false;
   1019  init.mCancelable = false;
   1020  init.mValidationURL.Truncate();
   1021 
   1022  ErrorResult rv;
   1023  RefPtr<MerchantValidationEvent> event =
   1024      MerchantValidationEvent::Constructor(this, aType, init, rv);
   1025  if (rv.Failed()) {
   1026    return rv.StealNSResult();
   1027  }
   1028  event->SetTrusted(true);
   1029  event->SetRequest(this);
   1030 
   1031  DispatchEvent(*event, rv);
   1032  return rv.StealNSResult();
   1033 }
   1034 
   1035 nsresult PaymentRequest::DispatchPaymentMethodChangeEvent(
   1036    const nsAString& aMethodName, const ChangeDetails& aMethodDetails) {
   1037  MOZ_ASSERT(ReadyForUpdate());
   1038 
   1039  PaymentRequestUpdateEventInit init;
   1040  init.mBubbles = false;
   1041  init.mCancelable = false;
   1042 
   1043  RefPtr<PaymentMethodChangeEvent> event =
   1044      PaymentMethodChangeEvent::Constructor(this, u"paymentmethodchange"_ns,
   1045                                            init, aMethodName, aMethodDetails);
   1046  event->SetTrusted(true);
   1047  event->SetRequest(this);
   1048 
   1049  ErrorResult rv;
   1050  DispatchEvent(*event, rv);
   1051  return rv.StealNSResult();
   1052 }
   1053 
   1054 already_AddRefed<PaymentAddress> PaymentRequest::GetShippingAddress() const {
   1055  RefPtr<PaymentAddress> address = mShippingAddress;
   1056  return address.forget();
   1057 }
   1058 
   1059 nsresult PaymentRequest::UpdateShippingAddress(
   1060    const nsAString& aCountry, const nsTArray<nsString>& aAddressLine,
   1061    const nsAString& aRegion, const nsAString& aRegionCode,
   1062    const nsAString& aCity, const nsAString& aDependentLocality,
   1063    const nsAString& aPostalCode, const nsAString& aSortingCode,
   1064    const nsAString& aOrganization, const nsAString& aRecipient,
   1065    const nsAString& aPhone) {
   1066  nsTArray<nsString> emptyArray;
   1067  mShippingAddress = new PaymentAddress(
   1068      GetOwnerWindow(), aCountry, emptyArray, aRegion, aRegionCode, aCity,
   1069      aDependentLocality, aPostalCode, aSortingCode, u""_ns, u""_ns, u""_ns);
   1070  mFullShippingAddress =
   1071      new PaymentAddress(GetOwnerWindow(), aCountry, aAddressLine, aRegion,
   1072                         aRegionCode, aCity, aDependentLocality, aPostalCode,
   1073                         aSortingCode, aOrganization, aRecipient, aPhone);
   1074  // Fire shippingaddresschange event
   1075  return DispatchUpdateEvent(u"shippingaddresschange"_ns);
   1076 }
   1077 
   1078 void PaymentRequest::SetShippingOption(const nsAString& aShippingOption) {
   1079  mShippingOption = aShippingOption;
   1080 }
   1081 
   1082 void PaymentRequest::GetShippingOption(nsAString& aRetVal) const {
   1083  aRetVal = mShippingOption;
   1084 }
   1085 
   1086 nsresult PaymentRequest::UpdateShippingOption(
   1087    const nsAString& aShippingOption) {
   1088  mShippingOption = aShippingOption;
   1089 
   1090  // Fire shippingaddresschange event
   1091  return DispatchUpdateEvent(u"shippingoptionchange"_ns);
   1092 }
   1093 
   1094 nsresult PaymentRequest::UpdatePaymentMethod(
   1095    const nsAString& aMethodName, const ChangeDetails& aMethodDetails) {
   1096  return DispatchPaymentMethodChangeEvent(aMethodName, aMethodDetails);
   1097 }
   1098 
   1099 void PaymentRequest::SetShippingType(
   1100    const Nullable<PaymentShippingType>& aShippingType) {
   1101  mShippingType = aShippingType;
   1102 }
   1103 
   1104 Nullable<PaymentShippingType> PaymentRequest::GetShippingType() const {
   1105  return mShippingType;
   1106 }
   1107 
   1108 void PaymentRequest::GetOptions(PaymentOptions& aRetVal) const {
   1109  aRetVal = mOptions;
   1110 }
   1111 
   1112 void PaymentRequest::SetOptions(const PaymentOptions& aOptions) {
   1113  mOptions = aOptions;
   1114 }
   1115 
   1116 void PaymentRequest::ResolvedCallback(JSContext* aCx,
   1117                                      JS::Handle<JS::Value> aValue,
   1118                                      ErrorResult& aRv) {
   1119  if (!InFullyActiveDocument()) {
   1120    return;
   1121  }
   1122 
   1123  MOZ_ASSERT(aCx);
   1124  mUpdating = false;
   1125  if (NS_WARN_IF(!aValue.isObject())) {
   1126    return;
   1127  }
   1128 
   1129  ErrorResult result;
   1130  // Converting value to a PaymentDetailsUpdate dictionary
   1131  RootedDictionary<PaymentDetailsUpdate> details(aCx);
   1132  if (!details.Init(aCx, aValue)) {
   1133    result.StealExceptionFromJSContext(aCx);
   1134    AbortUpdate(result);
   1135    return;
   1136  }
   1137 
   1138  IsValidDetailsUpdate(details, mRequestShipping, result);
   1139  if (result.Failed()) {
   1140    AbortUpdate(result);
   1141    return;
   1142  }
   1143 
   1144  // Update the PaymentRequest with the new details
   1145  UpdatePayment(aCx, details, result);
   1146  if (result.Failed()) {
   1147    AbortUpdate(result);
   1148    return;
   1149  }
   1150 }
   1151 
   1152 void PaymentRequest::RejectedCallback(JSContext* aCx,
   1153                                      JS::Handle<JS::Value> aValue,
   1154                                      ErrorResult& aRv) {
   1155  if (!InFullyActiveDocument()) {
   1156    return;
   1157  }
   1158 
   1159  mUpdating = false;
   1160  ErrorResult result;
   1161  result.ThrowAbortError(
   1162      "Details promise for PaymentRequest.show() is rejected by merchant");
   1163  AbortUpdate(result);
   1164 }
   1165 
   1166 bool PaymentRequest::InFullyActiveDocument() {
   1167  nsIGlobalObject* global = GetOwnerGlobal();
   1168  if (!global) {
   1169    return false;
   1170  }
   1171 
   1172  nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
   1173 
   1174  Document* doc = win->GetExtantDoc();
   1175  if (!doc || !doc->IsCurrentActiveDocument()) {
   1176    return false;
   1177  }
   1178 
   1179  WindowContext* winContext = win->GetWindowContext();
   1180  if (!winContext) {
   1181    return false;
   1182  }
   1183 
   1184  while (winContext) {
   1185    if (!winContext->IsCurrent()) {
   1186      return false;
   1187    }
   1188    winContext = winContext->GetParentWindowContext();
   1189  }
   1190 
   1191  return true;
   1192 }
   1193 
   1194 void PaymentRequest::RegisterActivityObserver() {
   1195  if (nsPIDOMWindowInner* window = GetOwnerWindow()) {
   1196    mDocument = window->GetExtantDoc();
   1197    if (mDocument) {
   1198      mDocument->RegisterActivityObserver(
   1199          NS_ISUPPORTS_CAST(nsIDocumentActivity*, this));
   1200    }
   1201  }
   1202 }
   1203 
   1204 void PaymentRequest::UnregisterActivityObserver() {
   1205  if (mDocument) {
   1206    mDocument->UnregisterActivityObserver(
   1207        NS_ISUPPORTS_CAST(nsIDocumentActivity*, this));
   1208  }
   1209 }
   1210 
   1211 void PaymentRequest::NotifyOwnerDocumentActivityChanged() {
   1212  nsPIDOMWindowInner* window = GetOwnerWindow();
   1213  NS_ENSURE_TRUE_VOID(window);
   1214  Document* doc = window->GetExtantDoc();
   1215  NS_ENSURE_TRUE_VOID(doc);
   1216 
   1217  if (!InFullyActiveDocument()) {
   1218    if (mState == eInteractive) {
   1219      if (mAcceptPromise) {
   1220        mAcceptPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   1221        mAcceptPromise = nullptr;
   1222      }
   1223      if (mResponse) {
   1224        ErrorResult rejectReason;
   1225        rejectReason.ThrowAbortError("The owner documnet is not fully active");
   1226        mResponse->RejectRetry(std::move(rejectReason));
   1227      }
   1228      if (mAbortPromise) {
   1229        mAbortPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   1230        mAbortPromise = nullptr;
   1231      }
   1232    }
   1233    if (mState == eCreated) {
   1234      if (mResultPromise) {
   1235        mResultPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   1236        mResultPromise = nullptr;
   1237      }
   1238    }
   1239    RefPtr<PaymentRequestManager> mgr = PaymentRequestManager::GetSingleton();
   1240    mgr->ClosePayment(this);
   1241  }
   1242 }
   1243 
   1244 PaymentRequest::~PaymentRequest() {
   1245  // Suppress any pending unreported exception on mUpdateError.  We don't use
   1246  // IgnoredErrorResult for mUpdateError because that doesn't play very nice
   1247  // with move assignment operators.
   1248  mUpdateError.SuppressException();
   1249 
   1250  if (mIPC) {
   1251    // If we're being destroyed, the PaymentRequestManager isn't holding any
   1252    // references to us and we can't be waiting for any replies.
   1253    mIPC->MaybeDelete(false);
   1254  }
   1255  UnregisterActivityObserver();
   1256 }
   1257 
   1258 JSObject* PaymentRequest::WrapObject(JSContext* aCx,
   1259                                     JS::Handle<JSObject*> aGivenProto) {
   1260  return PaymentRequest_Binding::Wrap(aCx, this, aGivenProto);
   1261 }
   1262 
   1263 }  // namespace mozilla::dom