/* -*- 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/ipc/InputStreamUtils.h" #include "nsArrayUtils.h" #include "nsCOMPtr.h" #include "nsIPaymentRequestService.h" #include "nsISupportsPrimitives.h" #include "nsServiceManagerUtils.h" #include "PaymentRequestData.h" #include "PaymentRequestParent.h" #include "PaymentRequestService.h" namespace mozilla::dom { PaymentRequestParent::PaymentRequestParent() : mActorAlive(true), mRequestId(u""_ns) {} mozilla::ipc::IPCResult PaymentRequestParent::RecvRequestPayment( const IPCPaymentActionRequest& aRequest) { if (!mActorAlive) { return IPC_FAIL_NO_REASON(this); } switch (aRequest.type()) { case IPCPaymentActionRequest::TIPCPaymentCreateActionRequest: { const IPCPaymentCreateActionRequest& request = aRequest; mRequestId = request.requestId(); break; } case IPCPaymentActionRequest::TIPCPaymentCanMakeActionRequest: { const IPCPaymentCanMakeActionRequest& request = aRequest; mRequestId = request.requestId(); break; } case IPCPaymentActionRequest::TIPCPaymentShowActionRequest: { const IPCPaymentShowActionRequest& request = aRequest; mRequestId = request.requestId(); break; } case IPCPaymentActionRequest::TIPCPaymentAbortActionRequest: { const IPCPaymentAbortActionRequest& request = aRequest; mRequestId = request.requestId(); break; } case IPCPaymentActionRequest::TIPCPaymentCompleteActionRequest: { const IPCPaymentCompleteActionRequest& request = aRequest; mRequestId = request.requestId(); break; } case IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest: { const IPCPaymentUpdateActionRequest& request = aRequest; mRequestId = request.requestId(); break; } case IPCPaymentActionRequest::TIPCPaymentCloseActionRequest: { const IPCPaymentCloseActionRequest& request = aRequest; mRequestId = request.requestId(); break; } case IPCPaymentActionRequest::TIPCPaymentRetryActionRequest: { const IPCPaymentRetryActionRequest& request = aRequest; mRequestId = request.requestId(); break; } default: { return IPC_FAIL(this, "Unknown PaymentRequest action type"); } } nsCOMPtr service = do_GetService(NS_PAYMENT_REQUEST_SERVICE_CONTRACT_ID); MOZ_ASSERT(service); PaymentRequestService* rowService = static_cast(service.get()); MOZ_ASSERT(rowService); nsresult rv = rowService->RequestPayment(mRequestId, aRequest, this); if (NS_WARN_IF(NS_FAILED(rv))) { return IPC_FAIL(this, "nsIPaymentRequestService::RequestPayment failed"); } return IPC_OK(); } nsresult PaymentRequestParent::RespondPayment( nsIPaymentActionResponse* aResponse) { if (!NS_IsMainThread()) { RefPtr self = this; nsCOMPtr response = aResponse; nsCOMPtr r = NS_NewRunnableFunction( "PaymentRequestParent::RespondPayment", [self, response]() { self->RespondPayment(response); }); return NS_DispatchToMainThread(r); } if (!mActorAlive) { return NS_ERROR_FAILURE; } uint32_t type; nsresult rv = aResponse->GetType(&type); NS_ENSURE_SUCCESS(rv, rv); nsAutoString requestId; rv = aResponse->GetRequestId(requestId); NS_ENSURE_SUCCESS(rv, rv); switch (type) { case nsIPaymentActionResponse::CANMAKE_ACTION: { nsCOMPtr response = do_QueryInterface(aResponse); MOZ_ASSERT(response); bool result; rv = response->GetResult(&result); NS_ENSURE_SUCCESS(rv, rv); IPCPaymentCanMakeActionResponse actionResponse(requestId, result); if (!SendRespondPayment(actionResponse)) { return NS_ERROR_FAILURE; } break; } case nsIPaymentActionResponse::SHOW_ACTION: { nsCOMPtr response = do_QueryInterface(aResponse); MOZ_ASSERT(response); uint32_t acceptStatus; NS_ENSURE_SUCCESS(response->GetAcceptStatus(&acceptStatus), NS_ERROR_FAILURE); nsAutoString methodName; NS_ENSURE_SUCCESS(response->GetMethodName(methodName), NS_ERROR_FAILURE); IPCPaymentResponseData ipcData; if (acceptStatus == nsIPaymentActionResponse::PAYMENT_ACCEPTED) { nsCOMPtr data; NS_ENSURE_SUCCESS(response->GetData(getter_AddRefs(data)), NS_ERROR_FAILURE); MOZ_ASSERT(data); NS_ENSURE_SUCCESS(SerializeResponseData(ipcData, data), NS_ERROR_FAILURE); } else { ipcData = IPCGeneralResponse(); } nsAutoString payerName; NS_ENSURE_SUCCESS(response->GetPayerName(payerName), NS_ERROR_FAILURE); nsAutoString payerEmail; NS_ENSURE_SUCCESS(response->GetPayerEmail(payerEmail), NS_ERROR_FAILURE); nsAutoString payerPhone; NS_ENSURE_SUCCESS(response->GetPayerPhone(payerPhone), NS_ERROR_FAILURE); IPCPaymentShowActionResponse actionResponse( requestId, acceptStatus, methodName, ipcData, payerName, payerEmail, payerPhone); if (!SendRespondPayment(actionResponse)) { return NS_ERROR_FAILURE; } break; } case nsIPaymentActionResponse::ABORT_ACTION: { nsCOMPtr response = do_QueryInterface(aResponse); MOZ_ASSERT(response); bool isSucceeded; rv = response->IsSucceeded(&isSucceeded); NS_ENSURE_SUCCESS(rv, rv); IPCPaymentAbortActionResponse actionResponse(requestId, isSucceeded); if (!SendRespondPayment(actionResponse)) { return NS_ERROR_FAILURE; } break; } case nsIPaymentActionResponse::COMPLETE_ACTION: { nsCOMPtr response = do_QueryInterface(aResponse); MOZ_ASSERT(response); bool isCompleted; rv = response->IsCompleted(&isCompleted); NS_ENSURE_SUCCESS(rv, rv); IPCPaymentCompleteActionResponse actionResponse(requestId, isCompleted); if (!SendRespondPayment(actionResponse)) { return NS_ERROR_FAILURE; } break; } default: { return NS_ERROR_FAILURE; } } return NS_OK; } nsresult PaymentRequestParent::ChangeShippingAddress( const nsAString& aRequestId, nsIPaymentAddress* aAddress) { if (!NS_IsMainThread()) { RefPtr self = this; nsCOMPtr address = aAddress; nsAutoString requestId(aRequestId); nsCOMPtr r = NS_NewRunnableFunction( "dom::PaymentRequestParent::ChangeShippingAddress", [self, requestId, address]() { self->ChangeShippingAddress(requestId, address); }); return NS_DispatchToMainThread(r); } if (!mActorAlive) { return NS_ERROR_FAILURE; } IPCPaymentAddress ipcAddress; nsresult rv = SerializeAddress(ipcAddress, aAddress); NS_ENSURE_SUCCESS(rv, rv); nsAutoString requestId(aRequestId); if (!SendChangeShippingAddress(requestId, ipcAddress)) { return NS_ERROR_FAILURE; } return NS_OK; } nsresult PaymentRequestParent::ChangeShippingOption(const nsAString& aRequestId, const nsAString& aOption) { if (!NS_IsMainThread()) { RefPtr self = this; nsAutoString requestId(aRequestId); nsAutoString option(aOption); nsCOMPtr r = NS_NewRunnableFunction( "dom::PaymentRequestParent::ChangeShippingOption", [self, requestId, option]() { self->ChangeShippingOption(requestId, option); }); return NS_DispatchToMainThread(r); } if (!mActorAlive) { return NS_ERROR_FAILURE; } nsAutoString requestId(aRequestId); nsAutoString option(aOption); if (!SendChangeShippingOption(requestId, option)) { return NS_ERROR_FAILURE; } return NS_OK; } nsresult PaymentRequestParent::ChangePayerDetail(const nsAString& aRequestId, const nsAString& aPayerName, const nsAString& aPayerEmail, const nsAString& aPayerPhone) { nsAutoString requestId(aRequestId); nsAutoString payerName(aPayerName); nsAutoString payerEmail(aPayerEmail); nsAutoString payerPhone(aPayerPhone); if (!NS_IsMainThread()) { RefPtr self = this; nsCOMPtr r = NS_NewRunnableFunction( "dom::PaymentRequestParent::ChangePayerDetail", [self, requestId, payerName, payerEmail, payerPhone]() { self->ChangePayerDetail(requestId, payerName, payerEmail, payerPhone); }); return NS_DispatchToMainThread(r); } if (!mActorAlive) { return NS_ERROR_FAILURE; } if (!SendChangePayerDetail(requestId, payerName, payerEmail, payerPhone)) { return NS_ERROR_FAILURE; } return NS_OK; } nsresult PaymentRequestParent::ChangePaymentMethod( const nsAString& aRequestId, const nsAString& aMethodName, nsIMethodChangeDetails* aMethodDetails) { nsAutoString requestId(aRequestId); nsAutoString methodName(aMethodName); nsCOMPtr methodDetails(aMethodDetails); if (!NS_IsMainThread()) { RefPtr self = this; nsCOMPtr r = NS_NewRunnableFunction( "dom::PaymentRequestParent::ChangePaymentMethod", [self, requestId, methodName, methodDetails]() { self->ChangePaymentMethod(requestId, methodName, methodDetails); }); return NS_DispatchToMainThread(r); } if (!mActorAlive) { return NS_ERROR_FAILURE; } // Convert nsIMethodChangeDetails to IPCMethodChangeDetails // aMethodChangeDetails can be null IPCMethodChangeDetails ipcChangeDetails; if (aMethodDetails) { uint32_t dataType; NS_ENSURE_SUCCESS(aMethodDetails->GetType(&dataType), NS_ERROR_FAILURE); switch (dataType) { case nsIMethodChangeDetails::GENERAL_DETAILS: { nsCOMPtr details = do_QueryInterface(methodDetails); MOZ_ASSERT(details); IPCGeneralChangeDetails ipcGeneralDetails; NS_ENSURE_SUCCESS(details->GetDetails(ipcGeneralDetails.details()), NS_ERROR_FAILURE); ipcChangeDetails = ipcGeneralDetails; break; } case nsIMethodChangeDetails::BASICCARD_DETAILS: { nsCOMPtr details = do_QueryInterface(methodDetails); MOZ_ASSERT(details); IPCBasicCardChangeDetails ipcBasicCardDetails; nsCOMPtr address; NS_ENSURE_SUCCESS(details->GetBillingAddress(getter_AddRefs(address)), NS_ERROR_FAILURE); IPCPaymentAddress ipcAddress; NS_ENSURE_SUCCESS(SerializeAddress(ipcAddress, address), NS_ERROR_FAILURE); ipcBasicCardDetails.billingAddress() = ipcAddress; ipcChangeDetails = ipcBasicCardDetails; break; } default: { return NS_ERROR_FAILURE; } } } if (!SendChangePaymentMethod(requestId, methodName, ipcChangeDetails)) { return NS_ERROR_FAILURE; } return NS_OK; } mozilla::ipc::IPCResult PaymentRequestParent::Recv__delete__() { mActorAlive = false; return IPC_OK(); } void PaymentRequestParent::ActorDestroy(ActorDestroyReason aWhy) { mActorAlive = false; nsCOMPtr service = do_GetService(NS_PAYMENT_REQUEST_SERVICE_CONTRACT_ID); MOZ_ASSERT(service); if (!mRequestId.Equals(u""_ns)) { nsCOMPtr request; nsresult rv = service->GetPaymentRequestById(mRequestId, getter_AddRefs(request)); if (NS_WARN_IF(NS_FAILED(rv))) { return; } if (!request) { return; } payments::PaymentRequest* rowRequest = static_cast(request.get()); MOZ_ASSERT(rowRequest); rowRequest->SetIPC(nullptr); } } nsresult PaymentRequestParent::SerializeAddress(IPCPaymentAddress& aIPCAddress, nsIPaymentAddress* aAddress) { // address can be nullptr if (!aAddress) { return NS_OK; } nsAutoString country; nsresult rv = aAddress->GetCountry(country); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr iaddressLine; rv = aAddress->GetAddressLine(getter_AddRefs(iaddressLine)); NS_ENSURE_SUCCESS(rv, rv); nsAutoString region; rv = aAddress->GetRegion(region); NS_ENSURE_SUCCESS(rv, rv); nsAutoString regionCode; rv = aAddress->GetRegionCode(regionCode); NS_ENSURE_SUCCESS(rv, rv); nsAutoString city; rv = aAddress->GetCity(city); NS_ENSURE_SUCCESS(rv, rv); nsAutoString dependentLocality; rv = aAddress->GetDependentLocality(dependentLocality); NS_ENSURE_SUCCESS(rv, rv); nsAutoString postalCode; rv = aAddress->GetPostalCode(postalCode); NS_ENSURE_SUCCESS(rv, rv); nsAutoString sortingCode; rv = aAddress->GetSortingCode(sortingCode); NS_ENSURE_SUCCESS(rv, rv); nsAutoString organization; rv = aAddress->GetOrganization(organization); NS_ENSURE_SUCCESS(rv, rv); nsAutoString recipient; rv = aAddress->GetRecipient(recipient); NS_ENSURE_SUCCESS(rv, rv); nsAutoString phone; rv = aAddress->GetPhone(phone); NS_ENSURE_SUCCESS(rv, rv); nsTArray addressLine; uint32_t length; rv = iaddressLine->GetLength(&length); NS_ENSURE_SUCCESS(rv, rv); for (uint32_t index = 0; index < length; ++index) { nsCOMPtr iaddress = do_QueryElementAt(iaddressLine, index); MOZ_ASSERT(iaddress); nsAutoString address; rv = iaddress->GetData(address); NS_ENSURE_SUCCESS(rv, rv); addressLine.AppendElement(address); } aIPCAddress = IPCPaymentAddress(country, addressLine, region, regionCode, city, dependentLocality, postalCode, sortingCode, organization, recipient, phone); return NS_OK; } nsresult PaymentRequestParent::SerializeResponseData( IPCPaymentResponseData& aIPCData, nsIPaymentResponseData* aData) { NS_ENSURE_ARG_POINTER(aData); uint32_t dataType; NS_ENSURE_SUCCESS(aData->GetType(&dataType), NS_ERROR_FAILURE); switch (dataType) { case nsIPaymentResponseData::GENERAL_RESPONSE: { nsCOMPtr response = do_QueryInterface(aData); MOZ_ASSERT(response); IPCGeneralResponse data; NS_ENSURE_SUCCESS(response->GetData(data.data()), NS_ERROR_FAILURE); aIPCData = data; break; } case nsIPaymentResponseData::BASICCARD_RESPONSE: { nsCOMPtr response = do_QueryInterface(aData); MOZ_ASSERT(response); IPCBasicCardResponse data; NS_ENSURE_SUCCESS(response->GetCardholderName(data.cardholderName()), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(response->GetCardNumber(data.cardNumber()), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(response->GetExpiryMonth(data.expiryMonth()), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(response->GetExpiryYear(data.expiryYear()), NS_ERROR_FAILURE); NS_ENSURE_SUCCESS(response->GetCardSecurityCode(data.cardSecurityCode()), NS_ERROR_FAILURE); nsCOMPtr address; NS_ENSURE_SUCCESS(response->GetBillingAddress(getter_AddRefs(address)), NS_ERROR_FAILURE); IPCPaymentAddress ipcAddress; NS_ENSURE_SUCCESS(SerializeAddress(ipcAddress, address), NS_ERROR_FAILURE); data.billingAddress() = ipcAddress; aIPCData = data; break; } default: { return NS_ERROR_FAILURE; } } return NS_OK; } } // namespace mozilla::dom