tor-browser

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

PaymentRequestManager.cpp (26838B)


      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 "PaymentRequestManager.h"
      8 
      9 #include "PaymentRequestUtils.h"
     10 #include "PaymentResponse.h"
     11 #include "mozilla/ClearOnShutdown.h"
     12 #include "mozilla/Preferences.h"
     13 #include "mozilla/dom/BrowserChild.h"
     14 #include "mozilla/dom/PaymentRequestChild.h"
     15 #include "nsContentUtils.h"
     16 #include "nsIPaymentActionResponse.h"
     17 #include "nsIPrincipal.h"
     18 #include "nsString.h"
     19 
     20 namespace mozilla::dom {
     21 namespace {
     22 
     23 /*
     24 *  Following Convert* functions are used for convert PaymentRequest structs
     25 *  to transferable structs for IPC.
     26 */
     27 void ConvertMethodData(JSContext* aCx, const PaymentMethodData& aMethodData,
     28                       IPCPaymentMethodData& aIPCMethodData, ErrorResult& aRv) {
     29  MOZ_ASSERT(aCx);
     30  // Convert JSObject to a serialized string
     31  nsAutoString serializedData;
     32  if (aMethodData.mData.WasPassed()) {
     33    JS::Rooted<JSObject*> object(aCx, aMethodData.mData.Value());
     34    if (NS_WARN_IF(
     35            NS_FAILED(SerializeFromJSObject(aCx, object, serializedData)))) {
     36      aRv.ThrowTypeError(
     37          "The PaymentMethodData.data must be a serializable object");
     38      return;
     39    }
     40  }
     41  aIPCMethodData =
     42      IPCPaymentMethodData(aMethodData.mSupportedMethods, serializedData);
     43 }
     44 
     45 void ConvertCurrencyAmount(const PaymentCurrencyAmount& aAmount,
     46                           IPCPaymentCurrencyAmount& aIPCCurrencyAmount) {
     47  aIPCCurrencyAmount =
     48      IPCPaymentCurrencyAmount(aAmount.mCurrency, aAmount.mValue);
     49 }
     50 
     51 void ConvertItem(const PaymentItem& aItem, IPCPaymentItem& aIPCItem) {
     52  IPCPaymentCurrencyAmount amount;
     53  ConvertCurrencyAmount(aItem.mAmount, amount);
     54  aIPCItem = IPCPaymentItem(aItem.mLabel, amount, aItem.mPending);
     55 }
     56 
     57 void ConvertModifier(JSContext* aCx, const PaymentDetailsModifier& aModifier,
     58                     IPCPaymentDetailsModifier& aIPCModifier,
     59                     ErrorResult& aRv) {
     60  MOZ_ASSERT(aCx);
     61  // Convert JSObject to a serialized string
     62  nsAutoString serializedData;
     63  if (aModifier.mData.WasPassed()) {
     64    JS::Rooted<JSObject*> object(aCx, aModifier.mData.Value());
     65    if (NS_WARN_IF(
     66            NS_FAILED(SerializeFromJSObject(aCx, object, serializedData)))) {
     67      aRv.ThrowTypeError("The Modifier.data must be a serializable object");
     68      return;
     69    }
     70  }
     71 
     72  IPCPaymentItem total;
     73  if (aModifier.mTotal.WasPassed()) {
     74    ConvertItem(aModifier.mTotal.Value(), total);
     75  }
     76 
     77  nsTArray<IPCPaymentItem> additionalDisplayItems;
     78  if (aModifier.mAdditionalDisplayItems.WasPassed()) {
     79    for (const PaymentItem& item : aModifier.mAdditionalDisplayItems.Value()) {
     80      IPCPaymentItem displayItem;
     81      ConvertItem(item, displayItem);
     82      additionalDisplayItems.AppendElement(displayItem);
     83    }
     84  }
     85  aIPCModifier = IPCPaymentDetailsModifier(
     86      aModifier.mSupportedMethods, total, additionalDisplayItems,
     87      serializedData, aModifier.mAdditionalDisplayItems.WasPassed());
     88 }
     89 
     90 void ConvertShippingOption(const PaymentShippingOption& aOption,
     91                           IPCPaymentShippingOption& aIPCOption) {
     92  IPCPaymentCurrencyAmount amount;
     93  ConvertCurrencyAmount(aOption.mAmount, amount);
     94  aIPCOption = IPCPaymentShippingOption(aOption.mId, aOption.mLabel, amount,
     95                                        aOption.mSelected);
     96 }
     97 
     98 void ConvertDetailsBase(JSContext* aCx, const PaymentDetailsBase& aDetails,
     99                        nsTArray<IPCPaymentItem>& aDisplayItems,
    100                        nsTArray<IPCPaymentShippingOption>& aShippingOptions,
    101                        nsTArray<IPCPaymentDetailsModifier>& aModifiers,
    102                        bool aRequestShipping, ErrorResult& aRv) {
    103  MOZ_ASSERT(aCx);
    104  if (aDetails.mDisplayItems.WasPassed()) {
    105    for (const PaymentItem& item : aDetails.mDisplayItems.Value()) {
    106      IPCPaymentItem displayItem;
    107      ConvertItem(item, displayItem);
    108      aDisplayItems.AppendElement(displayItem);
    109    }
    110  }
    111  if (aRequestShipping && aDetails.mShippingOptions.WasPassed()) {
    112    for (const PaymentShippingOption& option :
    113         aDetails.mShippingOptions.Value()) {
    114      IPCPaymentShippingOption shippingOption;
    115      ConvertShippingOption(option, shippingOption);
    116      aShippingOptions.AppendElement(shippingOption);
    117    }
    118  }
    119  if (aDetails.mModifiers.WasPassed()) {
    120    for (const PaymentDetailsModifier& modifier : aDetails.mModifiers.Value()) {
    121      IPCPaymentDetailsModifier detailsModifier;
    122      ConvertModifier(aCx, modifier, detailsModifier, aRv);
    123      if (aRv.Failed()) {
    124        return;
    125      }
    126      aModifiers.AppendElement(detailsModifier);
    127    }
    128  }
    129 }
    130 
    131 void ConvertDetailsInit(JSContext* aCx, const PaymentDetailsInit& aDetails,
    132                        IPCPaymentDetails& aIPCDetails, bool aRequestShipping,
    133                        ErrorResult& aRv) {
    134  MOZ_ASSERT(aCx);
    135  // Convert PaymentDetailsBase members
    136  nsTArray<IPCPaymentItem> displayItems;
    137  nsTArray<IPCPaymentShippingOption> shippingOptions;
    138  nsTArray<IPCPaymentDetailsModifier> modifiers;
    139  ConvertDetailsBase(aCx, aDetails, displayItems, shippingOptions, modifiers,
    140                     aRequestShipping, aRv);
    141  if (aRv.Failed()) {
    142    return;
    143  }
    144 
    145  // Convert |id|
    146  nsAutoString id;
    147  if (aDetails.mId.WasPassed()) {
    148    id = aDetails.mId.Value();
    149  }
    150 
    151  // Convert required |total|
    152  IPCPaymentItem total;
    153  ConvertItem(aDetails.mTotal, total);
    154 
    155  aIPCDetails =
    156      IPCPaymentDetails(id, total, displayItems, shippingOptions, modifiers,
    157                        u""_ns,   // error message
    158                        u""_ns,   // shippingAddressErrors
    159                        u""_ns,   // payerErrors
    160                        u""_ns);  // paymentMethodErrors
    161 }
    162 
    163 void ConvertDetailsUpdate(JSContext* aCx, const PaymentDetailsUpdate& aDetails,
    164                          IPCPaymentDetails& aIPCDetails, bool aRequestShipping,
    165                          ErrorResult& aRv) {
    166  MOZ_ASSERT(aCx);
    167  // Convert PaymentDetailsBase members
    168  nsTArray<IPCPaymentItem> displayItems;
    169  nsTArray<IPCPaymentShippingOption> shippingOptions;
    170  nsTArray<IPCPaymentDetailsModifier> modifiers;
    171  ConvertDetailsBase(aCx, aDetails, displayItems, shippingOptions, modifiers,
    172                     aRequestShipping, aRv);
    173  if (aRv.Failed()) {
    174    return;
    175  }
    176 
    177  // Convert required |total|
    178  IPCPaymentItem total;
    179  if (aDetails.mTotal.WasPassed()) {
    180    ConvertItem(aDetails.mTotal.Value(), total);
    181  }
    182 
    183  // Convert |error|
    184  nsAutoString error;
    185  if (aDetails.mError.WasPassed()) {
    186    error = aDetails.mError.Value();
    187  }
    188 
    189  nsAutoString shippingAddressErrors;
    190  if (aDetails.mShippingAddressErrors.WasPassed()) {
    191    if (!aDetails.mShippingAddressErrors.Value().ToJSON(
    192            shippingAddressErrors)) {
    193      aRv.ThrowTypeError("The ShippingAddressErrors can not be serailized");
    194      return;
    195    }
    196  }
    197 
    198  nsAutoString payerErrors;
    199  if (aDetails.mPayerErrors.WasPassed()) {
    200    if (!aDetails.mPayerErrors.Value().ToJSON(payerErrors)) {
    201      aRv.ThrowTypeError("The PayerErrors can not be serialized");
    202      return;
    203    }
    204  }
    205 
    206  nsAutoString paymentMethodErrors;
    207  if (aDetails.mPaymentMethodErrors.WasPassed()) {
    208    JS::Rooted<JSObject*> object(aCx, aDetails.mPaymentMethodErrors.Value());
    209    if (NS_WARN_IF(NS_FAILED(
    210            SerializeFromJSObject(aCx, object, paymentMethodErrors)))) {
    211      aRv.ThrowTypeError("The PaymentMethodErrors can not be serialized");
    212      return;
    213    }
    214  }
    215 
    216  aIPCDetails = IPCPaymentDetails(u""_ns,  // id
    217                                  total, displayItems, shippingOptions,
    218                                  modifiers, error, shippingAddressErrors,
    219                                  payerErrors, paymentMethodErrors);
    220 }
    221 
    222 void ConvertOptions(const PaymentOptions& aOptions,
    223                    IPCPaymentOptions& aIPCOption) {
    224  NS_ConvertASCIItoUTF16 shippingType(GetEnumString(aOptions.mShippingType));
    225  aIPCOption =
    226      IPCPaymentOptions(aOptions.mRequestPayerName, aOptions.mRequestPayerEmail,
    227                        aOptions.mRequestPayerPhone, aOptions.mRequestShipping,
    228                        aOptions.mRequestBillingAddress, shippingType);
    229 }
    230 
    231 void ConvertResponseData(const IPCPaymentResponseData& aIPCData,
    232                         ResponseData& aData) {
    233  switch (aIPCData.type()) {
    234    case IPCPaymentResponseData::TIPCGeneralResponse: {
    235      const IPCGeneralResponse& data = aIPCData;
    236      GeneralData gData;
    237      gData.data = data.data();
    238      aData = gData;
    239      break;
    240    }
    241    case IPCPaymentResponseData::TIPCBasicCardResponse: {
    242      const IPCBasicCardResponse& data = aIPCData;
    243      BasicCardData bData;
    244      bData.cardholderName = data.cardholderName();
    245      bData.cardNumber = data.cardNumber();
    246      bData.expiryMonth = data.expiryMonth();
    247      bData.expiryYear = data.expiryYear();
    248      bData.cardSecurityCode = data.cardSecurityCode();
    249      bData.billingAddress.country = data.billingAddress().country();
    250      bData.billingAddress.addressLine =
    251          data.billingAddress().addressLine().Clone();
    252      bData.billingAddress.region = data.billingAddress().region();
    253      bData.billingAddress.regionCode = data.billingAddress().regionCode();
    254      bData.billingAddress.city = data.billingAddress().city();
    255      bData.billingAddress.dependentLocality =
    256          data.billingAddress().dependentLocality();
    257      bData.billingAddress.postalCode = data.billingAddress().postalCode();
    258      bData.billingAddress.sortingCode = data.billingAddress().sortingCode();
    259      bData.billingAddress.organization = data.billingAddress().organization();
    260      bData.billingAddress.recipient = data.billingAddress().recipient();
    261      bData.billingAddress.phone = data.billingAddress().phone();
    262      aData = bData;
    263      break;
    264    }
    265    default: {
    266      break;
    267    }
    268  }
    269 }
    270 
    271 void ConvertMethodChangeDetails(const IPCMethodChangeDetails& aIPCDetails,
    272                                ChangeDetails& aDetails) {
    273  switch (aIPCDetails.type()) {
    274    case IPCMethodChangeDetails::TIPCGeneralChangeDetails: {
    275      const IPCGeneralChangeDetails& details = aIPCDetails;
    276      GeneralDetails gDetails;
    277      gDetails.details = details.details();
    278      aDetails = gDetails;
    279      break;
    280    }
    281    case IPCMethodChangeDetails::TIPCBasicCardChangeDetails: {
    282      const IPCBasicCardChangeDetails& details = aIPCDetails;
    283      BasicCardDetails bDetails;
    284      bDetails.billingAddress.country = details.billingAddress().country();
    285      bDetails.billingAddress.addressLine =
    286          details.billingAddress().addressLine();
    287      bDetails.billingAddress.region = details.billingAddress().region();
    288      bDetails.billingAddress.regionCode =
    289          details.billingAddress().regionCode();
    290      bDetails.billingAddress.city = details.billingAddress().city();
    291      bDetails.billingAddress.dependentLocality =
    292          details.billingAddress().dependentLocality();
    293      bDetails.billingAddress.postalCode =
    294          details.billingAddress().postalCode();
    295      bDetails.billingAddress.sortingCode =
    296          details.billingAddress().sortingCode();
    297      bDetails.billingAddress.organization =
    298          details.billingAddress().organization();
    299      bDetails.billingAddress.recipient = details.billingAddress().recipient();
    300      bDetails.billingAddress.phone = details.billingAddress().phone();
    301      aDetails = bDetails;
    302      break;
    303    }
    304    default: {
    305      break;
    306    }
    307  }
    308 }
    309 }  // end of namespace
    310 
    311 /* PaymentRequestManager */
    312 
    313 StaticRefPtr<PaymentRequestManager> gPaymentManager;
    314 const char kSupportedRegionsPref[] = "dom.payments.request.supportedRegions";
    315 
    316 void SupportedRegionsPrefChangedCallback(const char* aPrefName, void* aRetval) {
    317  auto retval = static_cast<nsTArray<nsString>*>(aRetval);
    318  MOZ_ASSERT(NS_IsMainThread());
    319  MOZ_ASSERT(!strcmp(aPrefName, kSupportedRegionsPref));
    320 
    321  nsAutoString supportedRegions;
    322  Preferences::GetString(aPrefName, supportedRegions);
    323  retval->Clear();
    324  for (const nsAString& each : supportedRegions.Split(',')) {
    325    retval->AppendElement(each);
    326  }
    327 }
    328 
    329 PaymentRequestManager::PaymentRequestManager() {
    330  Preferences::RegisterCallbackAndCall(SupportedRegionsPrefChangedCallback,
    331                                       kSupportedRegionsPref,
    332                                       &this->mSupportedRegions);
    333 }
    334 
    335 PaymentRequestManager::~PaymentRequestManager() {
    336  MOZ_ASSERT(mActivePayments.Count() == 0);
    337  Preferences::UnregisterCallback(SupportedRegionsPrefChangedCallback,
    338                                  kSupportedRegionsPref,
    339                                  &this->mSupportedRegions);
    340  mSupportedRegions.Clear();
    341 }
    342 
    343 bool PaymentRequestManager::IsRegionSupported(const nsAString& region) const {
    344  return mSupportedRegions.Contains(region);
    345 }
    346 
    347 PaymentRequestChild* PaymentRequestManager::GetPaymentChild(
    348    PaymentRequest* aRequest) {
    349  MOZ_ASSERT(aRequest);
    350 
    351  if (PaymentRequestChild* child = aRequest->GetIPC()) {
    352    return child;
    353  }
    354 
    355  nsPIDOMWindowInner* win = aRequest->GetOwnerWindow();
    356  NS_ENSURE_TRUE(win, nullptr);
    357  BrowserChild* browserChild = BrowserChild::GetFrom(win->GetDocShell());
    358  NS_ENSURE_TRUE(browserChild, nullptr);
    359  nsAutoString requestId;
    360  aRequest->GetInternalId(requestId);
    361 
    362  PaymentRequestChild* paymentChild = new PaymentRequestChild(aRequest);
    363  if (!browserChild->SendPPaymentRequestConstructor(paymentChild)) {
    364    // deleted by Constructor
    365    return nullptr;
    366  }
    367 
    368  return paymentChild;
    369 }
    370 
    371 nsresult PaymentRequestManager::SendRequestPayment(
    372    PaymentRequest* aRequest, const IPCPaymentActionRequest& aAction,
    373    bool aResponseExpected) {
    374  PaymentRequestChild* requestChild = GetPaymentChild(aRequest);
    375  // bug 1580496, ignoring the case that requestChild is nullptr. It could be
    376  // nullptr while the corresponding nsPIDOMWindowInner is nullptr.
    377  if (NS_WARN_IF(!requestChild)) {
    378    return NS_ERROR_FAILURE;
    379  }
    380  nsresult rv = requestChild->RequestPayment(aAction);
    381  if (NS_WARN_IF(NS_FAILED(rv))) {
    382    return rv;
    383  }
    384 
    385  if (aResponseExpected) {
    386    ++mActivePayments.LookupOrInsert(aRequest, 0);
    387  }
    388  return NS_OK;
    389 }
    390 
    391 void PaymentRequestManager::NotifyRequestDone(PaymentRequest* aRequest) {
    392  auto entry = mActivePayments.Lookup(aRequest);
    393  MOZ_ASSERT(entry);
    394  MOZ_ASSERT(entry.Data() > 0);
    395 
    396  uint32_t count = --entry.Data();
    397  if (count == 0) {
    398    entry.Remove();
    399  }
    400 }
    401 
    402 void PaymentRequestManager::RequestIPCOver(PaymentRequest* aRequest) {
    403  // This must only be called from ActorDestroy or if we're sure we won't
    404  // receive any more IPC for aRequest.
    405  mActivePayments.Remove(aRequest);
    406 }
    407 
    408 already_AddRefed<PaymentRequestManager> PaymentRequestManager::GetSingleton() {
    409  if (!gPaymentManager) {
    410    gPaymentManager = new PaymentRequestManager();
    411    ClearOnShutdown(&gPaymentManager);
    412  }
    413  RefPtr<PaymentRequestManager> manager = gPaymentManager;
    414  return manager.forget();
    415 }
    416 
    417 void GetSelectedShippingOption(const PaymentDetailsBase& aDetails,
    418                               nsAString& aOption) {
    419  SetDOMStringToNull(aOption);
    420  if (!aDetails.mShippingOptions.WasPassed()) {
    421    return;
    422  }
    423 
    424  const Sequence<PaymentShippingOption>& shippingOptions =
    425      aDetails.mShippingOptions.Value();
    426  for (const PaymentShippingOption& shippingOption : shippingOptions) {
    427    // set aOption to last selected option's ID
    428    if (shippingOption.mSelected) {
    429      aOption = shippingOption.mId;
    430    }
    431  }
    432 }
    433 
    434 void PaymentRequestManager::CreatePayment(
    435    JSContext* aCx, nsPIDOMWindowInner* aWindow,
    436    nsIPrincipal* aTopLevelPrincipal,
    437    const Sequence<PaymentMethodData>& aMethodData,
    438    const PaymentDetailsInit& aDetails, const PaymentOptions& aOptions,
    439    PaymentRequest** aRequest, ErrorResult& aRv) {
    440  MOZ_ASSERT(NS_IsMainThread());
    441  MOZ_ASSERT(aCx);
    442  MOZ_ASSERT(aRequest);
    443  MOZ_ASSERT(aTopLevelPrincipal);
    444  *aRequest = nullptr;
    445 
    446  RefPtr<PaymentRequest> request =
    447      PaymentRequest::CreatePaymentRequest(aWindow, aRv);
    448  if (aRv.Failed()) {
    449    return;
    450  }
    451  request->SetOptions(aOptions);
    452  /*
    453   *  Set request's |mId| to details.id if details.id exists.
    454   *  Otherwise, set |mId| to internal id.
    455   */
    456  nsAutoString requestId;
    457  if (aDetails.mId.WasPassed() && !aDetails.mId.Value().IsEmpty()) {
    458    requestId = aDetails.mId.Value();
    459  } else {
    460    request->GetInternalId(requestId);
    461  }
    462  request->SetId(requestId);
    463 
    464  /*
    465   * Set request's |mShippingType| and |mShippingOption| if shipping is
    466   * required. Set request's mShippingOption to last selected option's ID if
    467   * details.shippingOptions exists, otherwise set it as null.
    468   */
    469  nsAutoString shippingOption;
    470  SetDOMStringToNull(shippingOption);
    471  if (aOptions.mRequestShipping) {
    472    request->ShippingWasRequested();
    473    request->SetShippingType(
    474        Nullable<PaymentShippingType>(aOptions.mShippingType));
    475    GetSelectedShippingOption(aDetails, shippingOption);
    476  }
    477  request->SetShippingOption(shippingOption);
    478 
    479  nsAutoString internalId;
    480  request->GetInternalId(internalId);
    481 
    482  nsTArray<IPCPaymentMethodData> methodData;
    483  for (const PaymentMethodData& data : aMethodData) {
    484    IPCPaymentMethodData ipcMethodData;
    485    ConvertMethodData(aCx, data, ipcMethodData, aRv);
    486    if (aRv.Failed()) {
    487      return;
    488    }
    489    methodData.AppendElement(ipcMethodData);
    490  }
    491 
    492  IPCPaymentDetails details;
    493  ConvertDetailsInit(aCx, aDetails, details, aOptions.mRequestShipping, aRv);
    494  if (aRv.Failed()) {
    495    return;
    496  }
    497 
    498  IPCPaymentOptions options;
    499  ConvertOptions(aOptions, options);
    500 
    501  uint64_t topOuterWindowId =
    502      aWindow->GetWindowContext()->TopWindowContext()->OuterWindowId();
    503  IPCPaymentCreateActionRequest action(topOuterWindowId, internalId,
    504                                       aTopLevelPrincipal, methodData, details,
    505                                       options, shippingOption);
    506 
    507  if (NS_WARN_IF(NS_FAILED(SendRequestPayment(request, action, false)))) {
    508    aRv.ThrowUnknownError("Internal error sending payment request");
    509    return;
    510  }
    511  request.forget(aRequest);
    512 }
    513 
    514 void PaymentRequestManager::CanMakePayment(PaymentRequest* aRequest,
    515                                           ErrorResult& aRv) {
    516  nsAutoString requestId;
    517  aRequest->GetInternalId(requestId);
    518  IPCPaymentCanMakeActionRequest action(requestId);
    519  if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) {
    520    aRv.ThrowUnknownError("Internal error sending payment request");
    521  }
    522 }
    523 
    524 void PaymentRequestManager::ShowPayment(PaymentRequest* aRequest,
    525                                        ErrorResult& aRv) {
    526  nsAutoString requestId;
    527  aRequest->GetInternalId(requestId);
    528  IPCPaymentShowActionRequest action(requestId, aRequest->IsUpdating());
    529  if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) {
    530    aRv.ThrowUnknownError("Internal error sending payment request");
    531  }
    532 }
    533 
    534 void PaymentRequestManager::AbortPayment(PaymentRequest* aRequest,
    535                                         ErrorResult& aRv) {
    536  nsAutoString requestId;
    537  aRequest->GetInternalId(requestId);
    538  IPCPaymentAbortActionRequest action(requestId);
    539  if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) {
    540    aRv.ThrowUnknownError("Internal error sending payment request");
    541  }
    542 }
    543 
    544 void PaymentRequestManager::CompletePayment(PaymentRequest* aRequest,
    545                                            const PaymentComplete& aComplete,
    546                                            ErrorResult& aRv, bool aTimedOut) {
    547  nsString completeStatusString(u"unknown"_ns);
    548  if (aTimedOut) {
    549    completeStatusString.AssignLiteral("timeout");
    550  } else {
    551    completeStatusString.AssignASCII(GetEnumString(aComplete));
    552  }
    553 
    554  nsAutoString requestId;
    555  aRequest->GetInternalId(requestId);
    556  IPCPaymentCompleteActionRequest action(requestId, completeStatusString);
    557  if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action, false)))) {
    558    aRv.ThrowUnknownError("Internal error sending payment request");
    559  }
    560 }
    561 
    562 void PaymentRequestManager::UpdatePayment(JSContext* aCx,
    563                                          PaymentRequest* aRequest,
    564                                          const PaymentDetailsUpdate& aDetails,
    565                                          bool aRequestShipping,
    566                                          ErrorResult& aRv) {
    567  MOZ_ASSERT(aCx);
    568  IPCPaymentDetails details;
    569  ConvertDetailsUpdate(aCx, aDetails, details, aRequestShipping, aRv);
    570  if (aRv.Failed()) {
    571    return;
    572  }
    573 
    574  nsAutoString shippingOption;
    575  SetDOMStringToNull(shippingOption);
    576  if (aRequestShipping) {
    577    GetSelectedShippingOption(aDetails, shippingOption);
    578    aRequest->SetShippingOption(shippingOption);
    579  }
    580 
    581  nsAutoString requestId;
    582  aRequest->GetInternalId(requestId);
    583  IPCPaymentUpdateActionRequest action(requestId, details, shippingOption);
    584  if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action, false)))) {
    585    aRv.ThrowUnknownError("Internal error sending payment request");
    586  }
    587 }
    588 
    589 nsresult PaymentRequestManager::ClosePayment(PaymentRequest* aRequest) {
    590  // for the case, the payment request is waiting for response from user.
    591  if (auto entry = mActivePayments.Lookup(aRequest)) {
    592    NotifyRequestDone(aRequest);
    593  }
    594  nsAutoString requestId;
    595  aRequest->GetInternalId(requestId);
    596  IPCPaymentCloseActionRequest action(requestId);
    597  return SendRequestPayment(aRequest, action, false);
    598 }
    599 
    600 void PaymentRequestManager::RetryPayment(JSContext* aCx,
    601                                         PaymentRequest* aRequest,
    602                                         const PaymentValidationErrors& aErrors,
    603                                         ErrorResult& aRv) {
    604  MOZ_ASSERT(aCx);
    605  MOZ_ASSERT(aRequest);
    606 
    607  nsAutoString requestId;
    608  aRequest->GetInternalId(requestId);
    609 
    610  nsAutoString error;
    611  if (aErrors.mError.WasPassed()) {
    612    error = aErrors.mError.Value();
    613  }
    614 
    615  nsAutoString shippingAddressErrors;
    616  if (aErrors.mShippingAddress.WasPassed()) {
    617    if (!aErrors.mShippingAddress.Value().ToJSON(shippingAddressErrors)) {
    618      aRv.ThrowTypeError("The ShippingAddressErrors can not be serialized");
    619      return;
    620    }
    621  }
    622 
    623  nsAutoString payerErrors;
    624  if (aErrors.mPayer.WasPassed()) {
    625    if (!aErrors.mPayer.Value().ToJSON(payerErrors)) {
    626      aRv.ThrowTypeError("The PayerErrors can not be serialized");
    627      return;
    628    }
    629  }
    630 
    631  nsAutoString paymentMethodErrors;
    632  if (aErrors.mPaymentMethod.WasPassed()) {
    633    JS::Rooted<JSObject*> object(aCx, aErrors.mPaymentMethod.Value());
    634    if (NS_WARN_IF(NS_FAILED(
    635            SerializeFromJSObject(aCx, object, paymentMethodErrors)))) {
    636      aRv.ThrowTypeError("The PaymentMethodErrors can not be serialized");
    637      return;
    638    }
    639  }
    640  IPCPaymentRetryActionRequest action(requestId, error, payerErrors,
    641                                      paymentMethodErrors,
    642                                      shippingAddressErrors);
    643  if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) {
    644    aRv.ThrowUnknownError("Internal error sending payment request");
    645  }
    646 }
    647 
    648 nsresult PaymentRequestManager::RespondPayment(
    649    PaymentRequest* aRequest, const IPCPaymentActionResponse& aResponse) {
    650  switch (aResponse.type()) {
    651    case IPCPaymentActionResponse::TIPCPaymentCanMakeActionResponse: {
    652      const IPCPaymentCanMakeActionResponse& response = aResponse;
    653      aRequest->RespondCanMakePayment(response.result());
    654      NotifyRequestDone(aRequest);
    655      break;
    656    }
    657    case IPCPaymentActionResponse::TIPCPaymentShowActionResponse: {
    658      const IPCPaymentShowActionResponse& response = aResponse;
    659      ErrorResult rejectedReason;
    660      ResponseData responseData;
    661      ConvertResponseData(response.data(), responseData);
    662      switch (response.status()) {
    663        case nsIPaymentActionResponse::PAYMENT_ACCEPTED: {
    664          break;
    665        }
    666        case nsIPaymentActionResponse::PAYMENT_REJECTED: {
    667          rejectedReason.ThrowAbortError("The user rejected the payment");
    668          break;
    669        }
    670        case nsIPaymentActionResponse::PAYMENT_NOTSUPPORTED: {
    671          rejectedReason.ThrowNotSupportedError("No supported payment method");
    672          break;
    673        }
    674        default: {
    675          rejectedReason.ThrowUnknownError("Unknown response for the payment");
    676          break;
    677        }
    678      }
    679      // If PaymentActionResponse is not PAYMENT_ACCEPTED, no need to keep the
    680      // PaymentRequestChild instance. Otherwise, keep PaymentRequestChild for
    681      // merchants call PaymentResponse.complete()
    682      if (rejectedReason.Failed()) {
    683        NotifyRequestDone(aRequest);
    684      }
    685      aRequest->RespondShowPayment(response.methodName(), responseData,
    686                                   response.payerName(), response.payerEmail(),
    687                                   response.payerPhone(),
    688                                   std::move(rejectedReason));
    689      break;
    690    }
    691    case IPCPaymentActionResponse::TIPCPaymentAbortActionResponse: {
    692      const IPCPaymentAbortActionResponse& response = aResponse;
    693      aRequest->RespondAbortPayment(response.isSucceeded());
    694      NotifyRequestDone(aRequest);
    695      break;
    696    }
    697    case IPCPaymentActionResponse::TIPCPaymentCompleteActionResponse: {
    698      aRequest->RespondComplete();
    699      NotifyRequestDone(aRequest);
    700      break;
    701    }
    702    default: {
    703      return NS_ERROR_FAILURE;
    704    }
    705  }
    706  return NS_OK;
    707 }
    708 
    709 nsresult PaymentRequestManager::ChangeShippingAddress(
    710    PaymentRequest* aRequest, const IPCPaymentAddress& aAddress) {
    711  return aRequest->UpdateShippingAddress(
    712      aAddress.country(), aAddress.addressLine(), aAddress.region(),
    713      aAddress.regionCode(), aAddress.city(), aAddress.dependentLocality(),
    714      aAddress.postalCode(), aAddress.sortingCode(), aAddress.organization(),
    715      aAddress.recipient(), aAddress.phone());
    716 }
    717 
    718 nsresult PaymentRequestManager::ChangeShippingOption(PaymentRequest* aRequest,
    719                                                     const nsAString& aOption) {
    720  return aRequest->UpdateShippingOption(aOption);
    721 }
    722 
    723 nsresult PaymentRequestManager::ChangePayerDetail(
    724    PaymentRequest* aRequest, const nsAString& aPayerName,
    725    const nsAString& aPayerEmail, const nsAString& aPayerPhone) {
    726  MOZ_ASSERT(aRequest);
    727  RefPtr<PaymentResponse> response = aRequest->GetResponse();
    728  // ignoring the case call changePayerDetail during show().
    729  if (!response) {
    730    return NS_OK;
    731  }
    732  return response->UpdatePayerDetail(aPayerName, aPayerEmail, aPayerPhone);
    733 }
    734 
    735 nsresult PaymentRequestManager::ChangePaymentMethod(
    736    PaymentRequest* aRequest, const nsAString& aMethodName,
    737    const IPCMethodChangeDetails& aMethodDetails) {
    738  NS_ENSURE_ARG_POINTER(aRequest);
    739  ChangeDetails methodDetails;
    740  ConvertMethodChangeDetails(aMethodDetails, methodDetails);
    741  return aRequest->UpdatePaymentMethod(aMethodName, methodDetails);
    742 }
    743 
    744 }  // namespace mozilla::dom