forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			753 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			753 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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 "nsContentUtils.h"
 | |
| #include "nsString.h"
 | |
| #include "nsIPrincipal.h"
 | |
| #include "PaymentRequestManager.h"
 | |
| #include "PaymentRequestUtils.h"
 | |
| #include "PaymentResponse.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace 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::RootedObject 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::RootedObject 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,
 | |
|                         EmptyString(),   // error message
 | |
|                         EmptyString(),   // shippingAddressErrors
 | |
|                         EmptyString(),   // payerErrors
 | |
|                         EmptyString());  // 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::RootedObject 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(EmptyString(),  // 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) {
 | |
|     auto count = mActivePayments.LookupForAdd(aRequest);
 | |
|     if (count) {
 | |
|       count.Data()++;
 | |
|     } else {
 | |
|       count.OrInsert([]() { return 1; });
 | |
|     }
 | |
|   }
 | |
|   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);
 | |
| 
 | |
|   nsCOMPtr<nsPIDOMWindowOuter> outerWindow = aWindow->GetOuterWindow();
 | |
|   MOZ_ASSERT(outerWindow);
 | |
|   if (nsCOMPtr<nsPIDOMWindowOuter> topOuterWindow =
 | |
|           outerWindow->GetInProcessTop()) {
 | |
|     outerWindow = topOuterWindow;
 | |
|   }
 | |
|   uint64_t topOuterWindowId = outerWindow->WindowID();
 | |
| 
 | |
|   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::RootedObject 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);
 | |
| }
 | |
| 
 | |
| }  // end of namespace dom
 | |
| }  // end of namespace mozilla
 | 
