diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/payments/PaymentRequestManager.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/payments/PaymentRequestManager.cpp')
-rw-r--r-- | dom/payments/PaymentRequestManager.cpp | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/dom/payments/PaymentRequestManager.cpp b/dom/payments/PaymentRequestManager.cpp new file mode 100644 index 0000000000..f615fedf77 --- /dev/null +++ b/dom/payments/PaymentRequestManager.cpp @@ -0,0 +1,742 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/dom/PaymentRequestChild.h" +#include "mozilla/dom/BrowserChild.h" +#include "mozilla/Preferences.h" +#include "nsContentUtils.h" +#include "nsString.h" +#include "nsIPrincipal.h" +#include "nsIPaymentActionResponse.h" +#include "PaymentRequestManager.h" +#include "PaymentRequestUtils.h" +#include "PaymentResponse.h" + +namespace mozilla::dom { +namespace { + +/* + * Following Convert* functions are used for convert PaymentRequest structs + * to transferable structs for IPC. + */ +void ConvertMethodData(JSContext* aCx, const PaymentMethodData& aMethodData, + IPCPaymentMethodData& aIPCMethodData, ErrorResult& aRv) { + MOZ_ASSERT(aCx); + // Convert JSObject to a serialized string + nsAutoString serializedData; + if (aMethodData.mData.WasPassed()) { + JS::Rooted<JSObject*> object(aCx, aMethodData.mData.Value()); + if (NS_WARN_IF( + NS_FAILED(SerializeFromJSObject(aCx, object, serializedData)))) { + aRv.ThrowTypeError( + "The PaymentMethodData.data must be a serializable object"); + return; + } + } + aIPCMethodData = + IPCPaymentMethodData(aMethodData.mSupportedMethods, serializedData); +} + +void ConvertCurrencyAmount(const PaymentCurrencyAmount& aAmount, + IPCPaymentCurrencyAmount& aIPCCurrencyAmount) { + aIPCCurrencyAmount = + IPCPaymentCurrencyAmount(aAmount.mCurrency, aAmount.mValue); +} + +void ConvertItem(const PaymentItem& aItem, IPCPaymentItem& aIPCItem) { + IPCPaymentCurrencyAmount amount; + ConvertCurrencyAmount(aItem.mAmount, amount); + aIPCItem = IPCPaymentItem(aItem.mLabel, amount, aItem.mPending); +} + +void ConvertModifier(JSContext* aCx, const PaymentDetailsModifier& aModifier, + IPCPaymentDetailsModifier& aIPCModifier, + ErrorResult& aRv) { + MOZ_ASSERT(aCx); + // Convert JSObject to a serialized string + nsAutoString serializedData; + if (aModifier.mData.WasPassed()) { + JS::Rooted<JSObject*> object(aCx, aModifier.mData.Value()); + if (NS_WARN_IF( + NS_FAILED(SerializeFromJSObject(aCx, object, serializedData)))) { + aRv.ThrowTypeError("The Modifier.data must be a serializable object"); + return; + } + } + + IPCPaymentItem total; + if (aModifier.mTotal.WasPassed()) { + ConvertItem(aModifier.mTotal.Value(), total); + } + + nsTArray<IPCPaymentItem> additionalDisplayItems; + if (aModifier.mAdditionalDisplayItems.WasPassed()) { + for (const PaymentItem& item : aModifier.mAdditionalDisplayItems.Value()) { + IPCPaymentItem displayItem; + ConvertItem(item, displayItem); + additionalDisplayItems.AppendElement(displayItem); + } + } + aIPCModifier = IPCPaymentDetailsModifier( + aModifier.mSupportedMethods, total, additionalDisplayItems, + serializedData, aModifier.mAdditionalDisplayItems.WasPassed()); +} + +void ConvertShippingOption(const PaymentShippingOption& aOption, + IPCPaymentShippingOption& aIPCOption) { + IPCPaymentCurrencyAmount amount; + ConvertCurrencyAmount(aOption.mAmount, amount); + aIPCOption = IPCPaymentShippingOption(aOption.mId, aOption.mLabel, amount, + aOption.mSelected); +} + +void ConvertDetailsBase(JSContext* aCx, const PaymentDetailsBase& aDetails, + nsTArray<IPCPaymentItem>& aDisplayItems, + nsTArray<IPCPaymentShippingOption>& aShippingOptions, + nsTArray<IPCPaymentDetailsModifier>& aModifiers, + bool aRequestShipping, ErrorResult& aRv) { + MOZ_ASSERT(aCx); + if (aDetails.mDisplayItems.WasPassed()) { + for (const PaymentItem& item : aDetails.mDisplayItems.Value()) { + IPCPaymentItem displayItem; + ConvertItem(item, displayItem); + aDisplayItems.AppendElement(displayItem); + } + } + if (aRequestShipping && aDetails.mShippingOptions.WasPassed()) { + for (const PaymentShippingOption& option : + aDetails.mShippingOptions.Value()) { + IPCPaymentShippingOption shippingOption; + ConvertShippingOption(option, shippingOption); + aShippingOptions.AppendElement(shippingOption); + } + } + if (aDetails.mModifiers.WasPassed()) { + for (const PaymentDetailsModifier& modifier : aDetails.mModifiers.Value()) { + IPCPaymentDetailsModifier detailsModifier; + ConvertModifier(aCx, modifier, detailsModifier, aRv); + if (aRv.Failed()) { + return; + } + aModifiers.AppendElement(detailsModifier); + } + } +} + +void ConvertDetailsInit(JSContext* aCx, const PaymentDetailsInit& aDetails, + IPCPaymentDetails& aIPCDetails, bool aRequestShipping, + ErrorResult& aRv) { + MOZ_ASSERT(aCx); + // Convert PaymentDetailsBase members + nsTArray<IPCPaymentItem> displayItems; + nsTArray<IPCPaymentShippingOption> shippingOptions; + nsTArray<IPCPaymentDetailsModifier> modifiers; + ConvertDetailsBase(aCx, aDetails, displayItems, shippingOptions, modifiers, + aRequestShipping, aRv); + if (aRv.Failed()) { + return; + } + + // Convert |id| + nsAutoString id; + if (aDetails.mId.WasPassed()) { + id = aDetails.mId.Value(); + } + + // Convert required |total| + IPCPaymentItem total; + ConvertItem(aDetails.mTotal, total); + + aIPCDetails = + IPCPaymentDetails(id, total, displayItems, shippingOptions, modifiers, + u""_ns, // error message + u""_ns, // shippingAddressErrors + u""_ns, // payerErrors + u""_ns); // paymentMethodErrors +} + +void ConvertDetailsUpdate(JSContext* aCx, const PaymentDetailsUpdate& aDetails, + IPCPaymentDetails& aIPCDetails, bool aRequestShipping, + ErrorResult& aRv) { + MOZ_ASSERT(aCx); + // Convert PaymentDetailsBase members + nsTArray<IPCPaymentItem> displayItems; + nsTArray<IPCPaymentShippingOption> shippingOptions; + nsTArray<IPCPaymentDetailsModifier> modifiers; + ConvertDetailsBase(aCx, aDetails, displayItems, shippingOptions, modifiers, + aRequestShipping, aRv); + if (aRv.Failed()) { + return; + } + + // Convert required |total| + IPCPaymentItem total; + if (aDetails.mTotal.WasPassed()) { + ConvertItem(aDetails.mTotal.Value(), total); + } + + // Convert |error| + nsAutoString error; + if (aDetails.mError.WasPassed()) { + error = aDetails.mError.Value(); + } + + nsAutoString shippingAddressErrors; + if (aDetails.mShippingAddressErrors.WasPassed()) { + if (!aDetails.mShippingAddressErrors.Value().ToJSON( + shippingAddressErrors)) { + aRv.ThrowTypeError("The ShippingAddressErrors can not be serailized"); + return; + } + } + + nsAutoString payerErrors; + if (aDetails.mPayerErrors.WasPassed()) { + if (!aDetails.mPayerErrors.Value().ToJSON(payerErrors)) { + aRv.ThrowTypeError("The PayerErrors can not be serialized"); + return; + } + } + + nsAutoString paymentMethodErrors; + if (aDetails.mPaymentMethodErrors.WasPassed()) { + JS::Rooted<JSObject*> object(aCx, aDetails.mPaymentMethodErrors.Value()); + if (NS_WARN_IF(NS_FAILED( + SerializeFromJSObject(aCx, object, paymentMethodErrors)))) { + aRv.ThrowTypeError("The PaymentMethodErrors can not be serialized"); + return; + } + } + + aIPCDetails = IPCPaymentDetails(u""_ns, // id + total, displayItems, shippingOptions, + modifiers, error, shippingAddressErrors, + payerErrors, paymentMethodErrors); +} + +void ConvertOptions(const PaymentOptions& aOptions, + IPCPaymentOptions& aIPCOption) { + NS_ConvertASCIItoUTF16 shippingType( + PaymentShippingTypeValues::GetString(aOptions.mShippingType)); + aIPCOption = + IPCPaymentOptions(aOptions.mRequestPayerName, aOptions.mRequestPayerEmail, + aOptions.mRequestPayerPhone, aOptions.mRequestShipping, + aOptions.mRequestBillingAddress, shippingType); +} + +void ConvertResponseData(const IPCPaymentResponseData& aIPCData, + ResponseData& aData) { + switch (aIPCData.type()) { + case IPCPaymentResponseData::TIPCGeneralResponse: { + const IPCGeneralResponse& data = aIPCData; + GeneralData gData; + gData.data = data.data(); + aData = gData; + break; + } + case IPCPaymentResponseData::TIPCBasicCardResponse: { + const IPCBasicCardResponse& data = aIPCData; + BasicCardData bData; + bData.cardholderName = data.cardholderName(); + bData.cardNumber = data.cardNumber(); + bData.expiryMonth = data.expiryMonth(); + bData.expiryYear = data.expiryYear(); + bData.cardSecurityCode = data.cardSecurityCode(); + bData.billingAddress.country = data.billingAddress().country(); + bData.billingAddress.addressLine = + data.billingAddress().addressLine().Clone(); + bData.billingAddress.region = data.billingAddress().region(); + bData.billingAddress.regionCode = data.billingAddress().regionCode(); + bData.billingAddress.city = data.billingAddress().city(); + bData.billingAddress.dependentLocality = + data.billingAddress().dependentLocality(); + bData.billingAddress.postalCode = data.billingAddress().postalCode(); + bData.billingAddress.sortingCode = data.billingAddress().sortingCode(); + bData.billingAddress.organization = data.billingAddress().organization(); + bData.billingAddress.recipient = data.billingAddress().recipient(); + bData.billingAddress.phone = data.billingAddress().phone(); + aData = bData; + break; + } + default: { + break; + } + } +} + +void ConvertMethodChangeDetails(const IPCMethodChangeDetails& aIPCDetails, + ChangeDetails& aDetails) { + switch (aIPCDetails.type()) { + case IPCMethodChangeDetails::TIPCGeneralChangeDetails: { + const IPCGeneralChangeDetails& details = aIPCDetails; + GeneralDetails gDetails; + gDetails.details = details.details(); + aDetails = gDetails; + break; + } + case IPCMethodChangeDetails::TIPCBasicCardChangeDetails: { + const IPCBasicCardChangeDetails& details = aIPCDetails; + BasicCardDetails bDetails; + bDetails.billingAddress.country = details.billingAddress().country(); + bDetails.billingAddress.addressLine = + details.billingAddress().addressLine(); + bDetails.billingAddress.region = details.billingAddress().region(); + bDetails.billingAddress.regionCode = + details.billingAddress().regionCode(); + bDetails.billingAddress.city = details.billingAddress().city(); + bDetails.billingAddress.dependentLocality = + details.billingAddress().dependentLocality(); + bDetails.billingAddress.postalCode = + details.billingAddress().postalCode(); + bDetails.billingAddress.sortingCode = + details.billingAddress().sortingCode(); + bDetails.billingAddress.organization = + details.billingAddress().organization(); + bDetails.billingAddress.recipient = details.billingAddress().recipient(); + bDetails.billingAddress.phone = details.billingAddress().phone(); + aDetails = bDetails; + break; + } + default: { + break; + } + } +} +} // end of namespace + +/* PaymentRequestManager */ + +StaticRefPtr<PaymentRequestManager> gPaymentManager; +const char kSupportedRegionsPref[] = "dom.payments.request.supportedRegions"; + +void SupportedRegionsPrefChangedCallback(const char* aPrefName, void* aRetval) { + auto retval = static_cast<nsTArray<nsString>*>(aRetval); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!strcmp(aPrefName, kSupportedRegionsPref)); + + nsAutoString supportedRegions; + Preferences::GetString(aPrefName, supportedRegions); + retval->Clear(); + for (const nsAString& each : supportedRegions.Split(',')) { + retval->AppendElement(each); + } +} + +PaymentRequestManager::PaymentRequestManager() { + Preferences::RegisterCallbackAndCall(SupportedRegionsPrefChangedCallback, + kSupportedRegionsPref, + &this->mSupportedRegions); +} + +PaymentRequestManager::~PaymentRequestManager() { + MOZ_ASSERT(mActivePayments.Count() == 0); + Preferences::UnregisterCallback(SupportedRegionsPrefChangedCallback, + kSupportedRegionsPref, + &this->mSupportedRegions); + mSupportedRegions.Clear(); +} + +bool PaymentRequestManager::IsRegionSupported(const nsAString& region) const { + return mSupportedRegions.Contains(region); +} + +PaymentRequestChild* PaymentRequestManager::GetPaymentChild( + PaymentRequest* aRequest) { + MOZ_ASSERT(aRequest); + + if (PaymentRequestChild* child = aRequest->GetIPC()) { + return child; + } + + nsPIDOMWindowInner* win = aRequest->GetOwner(); + NS_ENSURE_TRUE(win, nullptr); + BrowserChild* browserChild = BrowserChild::GetFrom(win->GetDocShell()); + NS_ENSURE_TRUE(browserChild, nullptr); + nsAutoString requestId; + aRequest->GetInternalId(requestId); + + PaymentRequestChild* paymentChild = new PaymentRequestChild(aRequest); + browserChild->SendPPaymentRequestConstructor(paymentChild); + + return paymentChild; +} + +nsresult PaymentRequestManager::SendRequestPayment( + PaymentRequest* aRequest, const IPCPaymentActionRequest& aAction, + bool aResponseExpected) { + PaymentRequestChild* requestChild = GetPaymentChild(aRequest); + // bug 1580496, ignoring the case that requestChild is nullptr. It could be + // nullptr while the corresponding nsPIDOMWindowInner is nullptr. + if (NS_WARN_IF(!requestChild)) { + return NS_ERROR_FAILURE; + } + nsresult rv = requestChild->RequestPayment(aAction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (aResponseExpected) { + ++mActivePayments.LookupOrInsert(aRequest, 0); + } + return NS_OK; +} + +void PaymentRequestManager::NotifyRequestDone(PaymentRequest* aRequest) { + auto entry = mActivePayments.Lookup(aRequest); + MOZ_ASSERT(entry); + MOZ_ASSERT(entry.Data() > 0); + + uint32_t count = --entry.Data(); + if (count == 0) { + entry.Remove(); + } +} + +void PaymentRequestManager::RequestIPCOver(PaymentRequest* aRequest) { + // This must only be called from ActorDestroy or if we're sure we won't + // receive any more IPC for aRequest. + mActivePayments.Remove(aRequest); +} + +already_AddRefed<PaymentRequestManager> PaymentRequestManager::GetSingleton() { + if (!gPaymentManager) { + gPaymentManager = new PaymentRequestManager(); + ClearOnShutdown(&gPaymentManager); + } + RefPtr<PaymentRequestManager> manager = gPaymentManager; + return manager.forget(); +} + +void GetSelectedShippingOption(const PaymentDetailsBase& aDetails, + nsAString& aOption) { + SetDOMStringToNull(aOption); + if (!aDetails.mShippingOptions.WasPassed()) { + return; + } + + const Sequence<PaymentShippingOption>& shippingOptions = + aDetails.mShippingOptions.Value(); + for (const PaymentShippingOption& shippingOption : shippingOptions) { + // set aOption to last selected option's ID + if (shippingOption.mSelected) { + aOption = shippingOption.mId; + } + } +} + +void PaymentRequestManager::CreatePayment( + JSContext* aCx, nsPIDOMWindowInner* aWindow, + nsIPrincipal* aTopLevelPrincipal, + const Sequence<PaymentMethodData>& aMethodData, + const PaymentDetailsInit& aDetails, const PaymentOptions& aOptions, + PaymentRequest** aRequest, ErrorResult& aRv) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aCx); + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aTopLevelPrincipal); + *aRequest = nullptr; + + RefPtr<PaymentRequest> request = + PaymentRequest::CreatePaymentRequest(aWindow, aRv); + if (aRv.Failed()) { + return; + } + request->SetOptions(aOptions); + /* + * Set request's |mId| to details.id if details.id exists. + * Otherwise, set |mId| to internal id. + */ + nsAutoString requestId; + if (aDetails.mId.WasPassed() && !aDetails.mId.Value().IsEmpty()) { + requestId = aDetails.mId.Value(); + } else { + request->GetInternalId(requestId); + } + request->SetId(requestId); + + /* + * Set request's |mShippingType| and |mShippingOption| if shipping is + * required. Set request's mShippingOption to last selected option's ID if + * details.shippingOptions exists, otherwise set it as null. + */ + nsAutoString shippingOption; + SetDOMStringToNull(shippingOption); + if (aOptions.mRequestShipping) { + request->ShippingWasRequested(); + request->SetShippingType( + Nullable<PaymentShippingType>(aOptions.mShippingType)); + GetSelectedShippingOption(aDetails, shippingOption); + } + request->SetShippingOption(shippingOption); + + nsAutoString internalId; + request->GetInternalId(internalId); + + nsTArray<IPCPaymentMethodData> methodData; + for (const PaymentMethodData& data : aMethodData) { + IPCPaymentMethodData ipcMethodData; + ConvertMethodData(aCx, data, ipcMethodData, aRv); + if (aRv.Failed()) { + return; + } + methodData.AppendElement(ipcMethodData); + } + + IPCPaymentDetails details; + ConvertDetailsInit(aCx, aDetails, details, aOptions.mRequestShipping, aRv); + if (aRv.Failed()) { + return; + } + + IPCPaymentOptions options; + ConvertOptions(aOptions, options); + + uint64_t topOuterWindowId = + aWindow->GetWindowContext()->TopWindowContext()->OuterWindowId(); + IPCPaymentCreateActionRequest action(topOuterWindowId, internalId, + aTopLevelPrincipal, methodData, details, + options, shippingOption); + + if (NS_WARN_IF(NS_FAILED(SendRequestPayment(request, action, false)))) { + aRv.ThrowUnknownError("Internal error sending payment request"); + return; + } + request.forget(aRequest); +} + +void PaymentRequestManager::CanMakePayment(PaymentRequest* aRequest, + ErrorResult& aRv) { + nsAutoString requestId; + aRequest->GetInternalId(requestId); + IPCPaymentCanMakeActionRequest action(requestId); + if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) { + aRv.ThrowUnknownError("Internal error sending payment request"); + } +} + +void PaymentRequestManager::ShowPayment(PaymentRequest* aRequest, + ErrorResult& aRv) { + nsAutoString requestId; + aRequest->GetInternalId(requestId); + IPCPaymentShowActionRequest action(requestId, aRequest->IsUpdating()); + if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) { + aRv.ThrowUnknownError("Internal error sending payment request"); + } +} + +void PaymentRequestManager::AbortPayment(PaymentRequest* aRequest, + ErrorResult& aRv) { + nsAutoString requestId; + aRequest->GetInternalId(requestId); + IPCPaymentAbortActionRequest action(requestId); + if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) { + aRv.ThrowUnknownError("Internal error sending payment request"); + } +} + +void PaymentRequestManager::CompletePayment(PaymentRequest* aRequest, + const PaymentComplete& aComplete, + ErrorResult& aRv, bool aTimedOut) { + nsString completeStatusString(u"unknown"_ns); + if (aTimedOut) { + completeStatusString.AssignLiteral("timeout"); + } else { + completeStatusString.AssignASCII( + PaymentCompleteValues::GetString(aComplete)); + } + + nsAutoString requestId; + aRequest->GetInternalId(requestId); + IPCPaymentCompleteActionRequest action(requestId, completeStatusString); + if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action, false)))) { + aRv.ThrowUnknownError("Internal error sending payment request"); + } +} + +void PaymentRequestManager::UpdatePayment(JSContext* aCx, + PaymentRequest* aRequest, + const PaymentDetailsUpdate& aDetails, + bool aRequestShipping, + ErrorResult& aRv) { + MOZ_ASSERT(aCx); + IPCPaymentDetails details; + ConvertDetailsUpdate(aCx, aDetails, details, aRequestShipping, aRv); + if (aRv.Failed()) { + return; + } + + nsAutoString shippingOption; + SetDOMStringToNull(shippingOption); + if (aRequestShipping) { + GetSelectedShippingOption(aDetails, shippingOption); + aRequest->SetShippingOption(shippingOption); + } + + nsAutoString requestId; + aRequest->GetInternalId(requestId); + IPCPaymentUpdateActionRequest action(requestId, details, shippingOption); + if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action, false)))) { + aRv.ThrowUnknownError("Internal error sending payment request"); + } +} + +nsresult PaymentRequestManager::ClosePayment(PaymentRequest* aRequest) { + // for the case, the payment request is waiting for response from user. + if (auto entry = mActivePayments.Lookup(aRequest)) { + NotifyRequestDone(aRequest); + } + nsAutoString requestId; + aRequest->GetInternalId(requestId); + IPCPaymentCloseActionRequest action(requestId); + return SendRequestPayment(aRequest, action, false); +} + +void PaymentRequestManager::RetryPayment(JSContext* aCx, + PaymentRequest* aRequest, + const PaymentValidationErrors& aErrors, + ErrorResult& aRv) { + MOZ_ASSERT(aCx); + MOZ_ASSERT(aRequest); + + nsAutoString requestId; + aRequest->GetInternalId(requestId); + + nsAutoString error; + if (aErrors.mError.WasPassed()) { + error = aErrors.mError.Value(); + } + + nsAutoString shippingAddressErrors; + if (aErrors.mShippingAddress.WasPassed()) { + if (!aErrors.mShippingAddress.Value().ToJSON(shippingAddressErrors)) { + aRv.ThrowTypeError("The ShippingAddressErrors can not be serialized"); + return; + } + } + + nsAutoString payerErrors; + if (aErrors.mPayer.WasPassed()) { + if (!aErrors.mPayer.Value().ToJSON(payerErrors)) { + aRv.ThrowTypeError("The PayerErrors can not be serialized"); + return; + } + } + + nsAutoString paymentMethodErrors; + if (aErrors.mPaymentMethod.WasPassed()) { + JS::Rooted<JSObject*> object(aCx, aErrors.mPaymentMethod.Value()); + if (NS_WARN_IF(NS_FAILED( + SerializeFromJSObject(aCx, object, paymentMethodErrors)))) { + aRv.ThrowTypeError("The PaymentMethodErrors can not be serialized"); + return; + } + } + IPCPaymentRetryActionRequest action(requestId, error, payerErrors, + paymentMethodErrors, + shippingAddressErrors); + if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) { + aRv.ThrowUnknownError("Internal error sending payment request"); + } +} + +nsresult PaymentRequestManager::RespondPayment( + PaymentRequest* aRequest, const IPCPaymentActionResponse& aResponse) { + switch (aResponse.type()) { + case IPCPaymentActionResponse::TIPCPaymentCanMakeActionResponse: { + const IPCPaymentCanMakeActionResponse& response = aResponse; + aRequest->RespondCanMakePayment(response.result()); + NotifyRequestDone(aRequest); + break; + } + case IPCPaymentActionResponse::TIPCPaymentShowActionResponse: { + const IPCPaymentShowActionResponse& response = aResponse; + ErrorResult rejectedReason; + ResponseData responseData; + ConvertResponseData(response.data(), responseData); + switch (response.status()) { + case nsIPaymentActionResponse::PAYMENT_ACCEPTED: { + break; + } + case nsIPaymentActionResponse::PAYMENT_REJECTED: { + rejectedReason.ThrowAbortError("The user rejected the payment"); + break; + } + case nsIPaymentActionResponse::PAYMENT_NOTSUPPORTED: { + rejectedReason.ThrowNotSupportedError("No supported payment method"); + break; + } + default: { + rejectedReason.ThrowUnknownError("Unknown response for the payment"); + break; + } + } + // If PaymentActionResponse is not PAYMENT_ACCEPTED, no need to keep the + // PaymentRequestChild instance. Otherwise, keep PaymentRequestChild for + // merchants call PaymentResponse.complete() + if (rejectedReason.Failed()) { + NotifyRequestDone(aRequest); + } + aRequest->RespondShowPayment(response.methodName(), responseData, + response.payerName(), response.payerEmail(), + response.payerPhone(), + std::move(rejectedReason)); + break; + } + case IPCPaymentActionResponse::TIPCPaymentAbortActionResponse: { + const IPCPaymentAbortActionResponse& response = aResponse; + aRequest->RespondAbortPayment(response.isSucceeded()); + NotifyRequestDone(aRequest); + break; + } + case IPCPaymentActionResponse::TIPCPaymentCompleteActionResponse: { + aRequest->RespondComplete(); + NotifyRequestDone(aRequest); + break; + } + default: { + return NS_ERROR_FAILURE; + } + } + return NS_OK; +} + +nsresult PaymentRequestManager::ChangeShippingAddress( + PaymentRequest* aRequest, const IPCPaymentAddress& aAddress) { + return aRequest->UpdateShippingAddress( + aAddress.country(), aAddress.addressLine(), aAddress.region(), + aAddress.regionCode(), aAddress.city(), aAddress.dependentLocality(), + aAddress.postalCode(), aAddress.sortingCode(), aAddress.organization(), + aAddress.recipient(), aAddress.phone()); +} + +nsresult PaymentRequestManager::ChangeShippingOption(PaymentRequest* aRequest, + const nsAString& aOption) { + return aRequest->UpdateShippingOption(aOption); +} + +nsresult PaymentRequestManager::ChangePayerDetail( + PaymentRequest* aRequest, const nsAString& aPayerName, + const nsAString& aPayerEmail, const nsAString& aPayerPhone) { + MOZ_ASSERT(aRequest); + RefPtr<PaymentResponse> response = aRequest->GetResponse(); + // ignoring the case call changePayerDetail during show(). + if (!response) { + return NS_OK; + } + return response->UpdatePayerDetail(aPayerName, aPayerEmail, aPayerPhone); +} + +nsresult PaymentRequestManager::ChangePaymentMethod( + PaymentRequest* aRequest, const nsAString& aMethodName, + const IPCMethodChangeDetails& aMethodDetails) { + NS_ENSURE_ARG_POINTER(aRequest); + ChangeDetails methodDetails; + ConvertMethodChangeDetails(aMethodDetails, methodDetails); + return aRequest->UpdatePaymentMethod(aMethodName, methodDetails); +} + +} // namespace mozilla::dom |