Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / dom / payments / PaymentRequestManager.cpp
blobf615fedf77df8e50831a55a5ae00b8e9c3ff43b0
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/. */
7 #include "mozilla/ClearOnShutdown.h"
8 #include "mozilla/dom/PaymentRequestChild.h"
9 #include "mozilla/dom/BrowserChild.h"
10 #include "mozilla/Preferences.h"
11 #include "nsContentUtils.h"
12 #include "nsString.h"
13 #include "nsIPrincipal.h"
14 #include "nsIPaymentActionResponse.h"
15 #include "PaymentRequestManager.h"
16 #include "PaymentRequestUtils.h"
17 #include "PaymentResponse.h"
19 namespace mozilla::dom {
20 namespace {
23 * Following Convert* functions are used for convert PaymentRequest structs
24 * to transferable structs for IPC.
26 void ConvertMethodData(JSContext* aCx, const PaymentMethodData& aMethodData,
27 IPCPaymentMethodData& aIPCMethodData, ErrorResult& aRv) {
28 MOZ_ASSERT(aCx);
29 // Convert JSObject to a serialized string
30 nsAutoString serializedData;
31 if (aMethodData.mData.WasPassed()) {
32 JS::Rooted<JSObject*> object(aCx, aMethodData.mData.Value());
33 if (NS_WARN_IF(
34 NS_FAILED(SerializeFromJSObject(aCx, object, serializedData)))) {
35 aRv.ThrowTypeError(
36 "The PaymentMethodData.data must be a serializable object");
37 return;
40 aIPCMethodData =
41 IPCPaymentMethodData(aMethodData.mSupportedMethods, serializedData);
44 void ConvertCurrencyAmount(const PaymentCurrencyAmount& aAmount,
45 IPCPaymentCurrencyAmount& aIPCCurrencyAmount) {
46 aIPCCurrencyAmount =
47 IPCPaymentCurrencyAmount(aAmount.mCurrency, aAmount.mValue);
50 void ConvertItem(const PaymentItem& aItem, IPCPaymentItem& aIPCItem) {
51 IPCPaymentCurrencyAmount amount;
52 ConvertCurrencyAmount(aItem.mAmount, amount);
53 aIPCItem = IPCPaymentItem(aItem.mLabel, amount, aItem.mPending);
56 void ConvertModifier(JSContext* aCx, const PaymentDetailsModifier& aModifier,
57 IPCPaymentDetailsModifier& aIPCModifier,
58 ErrorResult& aRv) {
59 MOZ_ASSERT(aCx);
60 // Convert JSObject to a serialized string
61 nsAutoString serializedData;
62 if (aModifier.mData.WasPassed()) {
63 JS::Rooted<JSObject*> object(aCx, aModifier.mData.Value());
64 if (NS_WARN_IF(
65 NS_FAILED(SerializeFromJSObject(aCx, object, serializedData)))) {
66 aRv.ThrowTypeError("The Modifier.data must be a serializable object");
67 return;
71 IPCPaymentItem total;
72 if (aModifier.mTotal.WasPassed()) {
73 ConvertItem(aModifier.mTotal.Value(), total);
76 nsTArray<IPCPaymentItem> additionalDisplayItems;
77 if (aModifier.mAdditionalDisplayItems.WasPassed()) {
78 for (const PaymentItem& item : aModifier.mAdditionalDisplayItems.Value()) {
79 IPCPaymentItem displayItem;
80 ConvertItem(item, displayItem);
81 additionalDisplayItems.AppendElement(displayItem);
84 aIPCModifier = IPCPaymentDetailsModifier(
85 aModifier.mSupportedMethods, total, additionalDisplayItems,
86 serializedData, aModifier.mAdditionalDisplayItems.WasPassed());
89 void ConvertShippingOption(const PaymentShippingOption& aOption,
90 IPCPaymentShippingOption& aIPCOption) {
91 IPCPaymentCurrencyAmount amount;
92 ConvertCurrencyAmount(aOption.mAmount, amount);
93 aIPCOption = IPCPaymentShippingOption(aOption.mId, aOption.mLabel, amount,
94 aOption.mSelected);
97 void ConvertDetailsBase(JSContext* aCx, const PaymentDetailsBase& aDetails,
98 nsTArray<IPCPaymentItem>& aDisplayItems,
99 nsTArray<IPCPaymentShippingOption>& aShippingOptions,
100 nsTArray<IPCPaymentDetailsModifier>& aModifiers,
101 bool aRequestShipping, ErrorResult& aRv) {
102 MOZ_ASSERT(aCx);
103 if (aDetails.mDisplayItems.WasPassed()) {
104 for (const PaymentItem& item : aDetails.mDisplayItems.Value()) {
105 IPCPaymentItem displayItem;
106 ConvertItem(item, displayItem);
107 aDisplayItems.AppendElement(displayItem);
110 if (aRequestShipping && aDetails.mShippingOptions.WasPassed()) {
111 for (const PaymentShippingOption& option :
112 aDetails.mShippingOptions.Value()) {
113 IPCPaymentShippingOption shippingOption;
114 ConvertShippingOption(option, shippingOption);
115 aShippingOptions.AppendElement(shippingOption);
118 if (aDetails.mModifiers.WasPassed()) {
119 for (const PaymentDetailsModifier& modifier : aDetails.mModifiers.Value()) {
120 IPCPaymentDetailsModifier detailsModifier;
121 ConvertModifier(aCx, modifier, detailsModifier, aRv);
122 if (aRv.Failed()) {
123 return;
125 aModifiers.AppendElement(detailsModifier);
130 void ConvertDetailsInit(JSContext* aCx, const PaymentDetailsInit& aDetails,
131 IPCPaymentDetails& aIPCDetails, bool aRequestShipping,
132 ErrorResult& aRv) {
133 MOZ_ASSERT(aCx);
134 // Convert PaymentDetailsBase members
135 nsTArray<IPCPaymentItem> displayItems;
136 nsTArray<IPCPaymentShippingOption> shippingOptions;
137 nsTArray<IPCPaymentDetailsModifier> modifiers;
138 ConvertDetailsBase(aCx, aDetails, displayItems, shippingOptions, modifiers,
139 aRequestShipping, aRv);
140 if (aRv.Failed()) {
141 return;
144 // Convert |id|
145 nsAutoString id;
146 if (aDetails.mId.WasPassed()) {
147 id = aDetails.mId.Value();
150 // Convert required |total|
151 IPCPaymentItem total;
152 ConvertItem(aDetails.mTotal, total);
154 aIPCDetails =
155 IPCPaymentDetails(id, total, displayItems, shippingOptions, modifiers,
156 u""_ns, // error message
157 u""_ns, // shippingAddressErrors
158 u""_ns, // payerErrors
159 u""_ns); // paymentMethodErrors
162 void ConvertDetailsUpdate(JSContext* aCx, const PaymentDetailsUpdate& aDetails,
163 IPCPaymentDetails& aIPCDetails, bool aRequestShipping,
164 ErrorResult& aRv) {
165 MOZ_ASSERT(aCx);
166 // Convert PaymentDetailsBase members
167 nsTArray<IPCPaymentItem> displayItems;
168 nsTArray<IPCPaymentShippingOption> shippingOptions;
169 nsTArray<IPCPaymentDetailsModifier> modifiers;
170 ConvertDetailsBase(aCx, aDetails, displayItems, shippingOptions, modifiers,
171 aRequestShipping, aRv);
172 if (aRv.Failed()) {
173 return;
176 // Convert required |total|
177 IPCPaymentItem total;
178 if (aDetails.mTotal.WasPassed()) {
179 ConvertItem(aDetails.mTotal.Value(), total);
182 // Convert |error|
183 nsAutoString error;
184 if (aDetails.mError.WasPassed()) {
185 error = aDetails.mError.Value();
188 nsAutoString shippingAddressErrors;
189 if (aDetails.mShippingAddressErrors.WasPassed()) {
190 if (!aDetails.mShippingAddressErrors.Value().ToJSON(
191 shippingAddressErrors)) {
192 aRv.ThrowTypeError("The ShippingAddressErrors can not be serailized");
193 return;
197 nsAutoString payerErrors;
198 if (aDetails.mPayerErrors.WasPassed()) {
199 if (!aDetails.mPayerErrors.Value().ToJSON(payerErrors)) {
200 aRv.ThrowTypeError("The PayerErrors can not be serialized");
201 return;
205 nsAutoString paymentMethodErrors;
206 if (aDetails.mPaymentMethodErrors.WasPassed()) {
207 JS::Rooted<JSObject*> object(aCx, aDetails.mPaymentMethodErrors.Value());
208 if (NS_WARN_IF(NS_FAILED(
209 SerializeFromJSObject(aCx, object, paymentMethodErrors)))) {
210 aRv.ThrowTypeError("The PaymentMethodErrors can not be serialized");
211 return;
215 aIPCDetails = IPCPaymentDetails(u""_ns, // id
216 total, displayItems, shippingOptions,
217 modifiers, error, shippingAddressErrors,
218 payerErrors, paymentMethodErrors);
221 void ConvertOptions(const PaymentOptions& aOptions,
222 IPCPaymentOptions& aIPCOption) {
223 NS_ConvertASCIItoUTF16 shippingType(
224 PaymentShippingTypeValues::GetString(aOptions.mShippingType));
225 aIPCOption =
226 IPCPaymentOptions(aOptions.mRequestPayerName, aOptions.mRequestPayerEmail,
227 aOptions.mRequestPayerPhone, aOptions.mRequestShipping,
228 aOptions.mRequestBillingAddress, shippingType);
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;
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;
265 default: {
266 break;
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;
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;
304 default: {
305 break;
309 } // end of namespace
311 /* PaymentRequestManager */
313 StaticRefPtr<PaymentRequestManager> gPaymentManager;
314 const char kSupportedRegionsPref[] = "dom.payments.request.supportedRegions";
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));
321 nsAutoString supportedRegions;
322 Preferences::GetString(aPrefName, supportedRegions);
323 retval->Clear();
324 for (const nsAString& each : supportedRegions.Split(',')) {
325 retval->AppendElement(each);
329 PaymentRequestManager::PaymentRequestManager() {
330 Preferences::RegisterCallbackAndCall(SupportedRegionsPrefChangedCallback,
331 kSupportedRegionsPref,
332 &this->mSupportedRegions);
335 PaymentRequestManager::~PaymentRequestManager() {
336 MOZ_ASSERT(mActivePayments.Count() == 0);
337 Preferences::UnregisterCallback(SupportedRegionsPrefChangedCallback,
338 kSupportedRegionsPref,
339 &this->mSupportedRegions);
340 mSupportedRegions.Clear();
343 bool PaymentRequestManager::IsRegionSupported(const nsAString& region) const {
344 return mSupportedRegions.Contains(region);
347 PaymentRequestChild* PaymentRequestManager::GetPaymentChild(
348 PaymentRequest* aRequest) {
349 MOZ_ASSERT(aRequest);
351 if (PaymentRequestChild* child = aRequest->GetIPC()) {
352 return child;
355 nsPIDOMWindowInner* win = aRequest->GetOwner();
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);
362 PaymentRequestChild* paymentChild = new PaymentRequestChild(aRequest);
363 browserChild->SendPPaymentRequestConstructor(paymentChild);
365 return paymentChild;
368 nsresult PaymentRequestManager::SendRequestPayment(
369 PaymentRequest* aRequest, const IPCPaymentActionRequest& aAction,
370 bool aResponseExpected) {
371 PaymentRequestChild* requestChild = GetPaymentChild(aRequest);
372 // bug 1580496, ignoring the case that requestChild is nullptr. It could be
373 // nullptr while the corresponding nsPIDOMWindowInner is nullptr.
374 if (NS_WARN_IF(!requestChild)) {
375 return NS_ERROR_FAILURE;
377 nsresult rv = requestChild->RequestPayment(aAction);
378 if (NS_WARN_IF(NS_FAILED(rv))) {
379 return rv;
382 if (aResponseExpected) {
383 ++mActivePayments.LookupOrInsert(aRequest, 0);
385 return NS_OK;
388 void PaymentRequestManager::NotifyRequestDone(PaymentRequest* aRequest) {
389 auto entry = mActivePayments.Lookup(aRequest);
390 MOZ_ASSERT(entry);
391 MOZ_ASSERT(entry.Data() > 0);
393 uint32_t count = --entry.Data();
394 if (count == 0) {
395 entry.Remove();
399 void PaymentRequestManager::RequestIPCOver(PaymentRequest* aRequest) {
400 // This must only be called from ActorDestroy or if we're sure we won't
401 // receive any more IPC for aRequest.
402 mActivePayments.Remove(aRequest);
405 already_AddRefed<PaymentRequestManager> PaymentRequestManager::GetSingleton() {
406 if (!gPaymentManager) {
407 gPaymentManager = new PaymentRequestManager();
408 ClearOnShutdown(&gPaymentManager);
410 RefPtr<PaymentRequestManager> manager = gPaymentManager;
411 return manager.forget();
414 void GetSelectedShippingOption(const PaymentDetailsBase& aDetails,
415 nsAString& aOption) {
416 SetDOMStringToNull(aOption);
417 if (!aDetails.mShippingOptions.WasPassed()) {
418 return;
421 const Sequence<PaymentShippingOption>& shippingOptions =
422 aDetails.mShippingOptions.Value();
423 for (const PaymentShippingOption& shippingOption : shippingOptions) {
424 // set aOption to last selected option's ID
425 if (shippingOption.mSelected) {
426 aOption = shippingOption.mId;
431 void PaymentRequestManager::CreatePayment(
432 JSContext* aCx, nsPIDOMWindowInner* aWindow,
433 nsIPrincipal* aTopLevelPrincipal,
434 const Sequence<PaymentMethodData>& aMethodData,
435 const PaymentDetailsInit& aDetails, const PaymentOptions& aOptions,
436 PaymentRequest** aRequest, ErrorResult& aRv) {
437 MOZ_ASSERT(NS_IsMainThread());
438 MOZ_ASSERT(aCx);
439 MOZ_ASSERT(aRequest);
440 MOZ_ASSERT(aTopLevelPrincipal);
441 *aRequest = nullptr;
443 RefPtr<PaymentRequest> request =
444 PaymentRequest::CreatePaymentRequest(aWindow, aRv);
445 if (aRv.Failed()) {
446 return;
448 request->SetOptions(aOptions);
450 * Set request's |mId| to details.id if details.id exists.
451 * Otherwise, set |mId| to internal id.
453 nsAutoString requestId;
454 if (aDetails.mId.WasPassed() && !aDetails.mId.Value().IsEmpty()) {
455 requestId = aDetails.mId.Value();
456 } else {
457 request->GetInternalId(requestId);
459 request->SetId(requestId);
462 * Set request's |mShippingType| and |mShippingOption| if shipping is
463 * required. Set request's mShippingOption to last selected option's ID if
464 * details.shippingOptions exists, otherwise set it as null.
466 nsAutoString shippingOption;
467 SetDOMStringToNull(shippingOption);
468 if (aOptions.mRequestShipping) {
469 request->ShippingWasRequested();
470 request->SetShippingType(
471 Nullable<PaymentShippingType>(aOptions.mShippingType));
472 GetSelectedShippingOption(aDetails, shippingOption);
474 request->SetShippingOption(shippingOption);
476 nsAutoString internalId;
477 request->GetInternalId(internalId);
479 nsTArray<IPCPaymentMethodData> methodData;
480 for (const PaymentMethodData& data : aMethodData) {
481 IPCPaymentMethodData ipcMethodData;
482 ConvertMethodData(aCx, data, ipcMethodData, aRv);
483 if (aRv.Failed()) {
484 return;
486 methodData.AppendElement(ipcMethodData);
489 IPCPaymentDetails details;
490 ConvertDetailsInit(aCx, aDetails, details, aOptions.mRequestShipping, aRv);
491 if (aRv.Failed()) {
492 return;
495 IPCPaymentOptions options;
496 ConvertOptions(aOptions, options);
498 uint64_t topOuterWindowId =
499 aWindow->GetWindowContext()->TopWindowContext()->OuterWindowId();
500 IPCPaymentCreateActionRequest action(topOuterWindowId, internalId,
501 aTopLevelPrincipal, methodData, details,
502 options, shippingOption);
504 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(request, action, false)))) {
505 aRv.ThrowUnknownError("Internal error sending payment request");
506 return;
508 request.forget(aRequest);
511 void PaymentRequestManager::CanMakePayment(PaymentRequest* aRequest,
512 ErrorResult& aRv) {
513 nsAutoString requestId;
514 aRequest->GetInternalId(requestId);
515 IPCPaymentCanMakeActionRequest action(requestId);
516 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) {
517 aRv.ThrowUnknownError("Internal error sending payment request");
521 void PaymentRequestManager::ShowPayment(PaymentRequest* aRequest,
522 ErrorResult& aRv) {
523 nsAutoString requestId;
524 aRequest->GetInternalId(requestId);
525 IPCPaymentShowActionRequest action(requestId, aRequest->IsUpdating());
526 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) {
527 aRv.ThrowUnknownError("Internal error sending payment request");
531 void PaymentRequestManager::AbortPayment(PaymentRequest* aRequest,
532 ErrorResult& aRv) {
533 nsAutoString requestId;
534 aRequest->GetInternalId(requestId);
535 IPCPaymentAbortActionRequest action(requestId);
536 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) {
537 aRv.ThrowUnknownError("Internal error sending payment request");
541 void PaymentRequestManager::CompletePayment(PaymentRequest* aRequest,
542 const PaymentComplete& aComplete,
543 ErrorResult& aRv, bool aTimedOut) {
544 nsString completeStatusString(u"unknown"_ns);
545 if (aTimedOut) {
546 completeStatusString.AssignLiteral("timeout");
547 } else {
548 completeStatusString.AssignASCII(
549 PaymentCompleteValues::GetString(aComplete));
552 nsAutoString requestId;
553 aRequest->GetInternalId(requestId);
554 IPCPaymentCompleteActionRequest action(requestId, completeStatusString);
555 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action, false)))) {
556 aRv.ThrowUnknownError("Internal error sending payment request");
560 void PaymentRequestManager::UpdatePayment(JSContext* aCx,
561 PaymentRequest* aRequest,
562 const PaymentDetailsUpdate& aDetails,
563 bool aRequestShipping,
564 ErrorResult& aRv) {
565 MOZ_ASSERT(aCx);
566 IPCPaymentDetails details;
567 ConvertDetailsUpdate(aCx, aDetails, details, aRequestShipping, aRv);
568 if (aRv.Failed()) {
569 return;
572 nsAutoString shippingOption;
573 SetDOMStringToNull(shippingOption);
574 if (aRequestShipping) {
575 GetSelectedShippingOption(aDetails, shippingOption);
576 aRequest->SetShippingOption(shippingOption);
579 nsAutoString requestId;
580 aRequest->GetInternalId(requestId);
581 IPCPaymentUpdateActionRequest action(requestId, details, shippingOption);
582 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action, false)))) {
583 aRv.ThrowUnknownError("Internal error sending payment request");
587 nsresult PaymentRequestManager::ClosePayment(PaymentRequest* aRequest) {
588 // for the case, the payment request is waiting for response from user.
589 if (auto entry = mActivePayments.Lookup(aRequest)) {
590 NotifyRequestDone(aRequest);
592 nsAutoString requestId;
593 aRequest->GetInternalId(requestId);
594 IPCPaymentCloseActionRequest action(requestId);
595 return SendRequestPayment(aRequest, action, false);
598 void PaymentRequestManager::RetryPayment(JSContext* aCx,
599 PaymentRequest* aRequest,
600 const PaymentValidationErrors& aErrors,
601 ErrorResult& aRv) {
602 MOZ_ASSERT(aCx);
603 MOZ_ASSERT(aRequest);
605 nsAutoString requestId;
606 aRequest->GetInternalId(requestId);
608 nsAutoString error;
609 if (aErrors.mError.WasPassed()) {
610 error = aErrors.mError.Value();
613 nsAutoString shippingAddressErrors;
614 if (aErrors.mShippingAddress.WasPassed()) {
615 if (!aErrors.mShippingAddress.Value().ToJSON(shippingAddressErrors)) {
616 aRv.ThrowTypeError("The ShippingAddressErrors can not be serialized");
617 return;
621 nsAutoString payerErrors;
622 if (aErrors.mPayer.WasPassed()) {
623 if (!aErrors.mPayer.Value().ToJSON(payerErrors)) {
624 aRv.ThrowTypeError("The PayerErrors can not be serialized");
625 return;
629 nsAutoString paymentMethodErrors;
630 if (aErrors.mPaymentMethod.WasPassed()) {
631 JS::Rooted<JSObject*> object(aCx, aErrors.mPaymentMethod.Value());
632 if (NS_WARN_IF(NS_FAILED(
633 SerializeFromJSObject(aCx, object, paymentMethodErrors)))) {
634 aRv.ThrowTypeError("The PaymentMethodErrors can not be serialized");
635 return;
638 IPCPaymentRetryActionRequest action(requestId, error, payerErrors,
639 paymentMethodErrors,
640 shippingAddressErrors);
641 if (NS_WARN_IF(NS_FAILED(SendRequestPayment(aRequest, action)))) {
642 aRv.ThrowUnknownError("Internal error sending payment request");
646 nsresult PaymentRequestManager::RespondPayment(
647 PaymentRequest* aRequest, const IPCPaymentActionResponse& aResponse) {
648 switch (aResponse.type()) {
649 case IPCPaymentActionResponse::TIPCPaymentCanMakeActionResponse: {
650 const IPCPaymentCanMakeActionResponse& response = aResponse;
651 aRequest->RespondCanMakePayment(response.result());
652 NotifyRequestDone(aRequest);
653 break;
655 case IPCPaymentActionResponse::TIPCPaymentShowActionResponse: {
656 const IPCPaymentShowActionResponse& response = aResponse;
657 ErrorResult rejectedReason;
658 ResponseData responseData;
659 ConvertResponseData(response.data(), responseData);
660 switch (response.status()) {
661 case nsIPaymentActionResponse::PAYMENT_ACCEPTED: {
662 break;
664 case nsIPaymentActionResponse::PAYMENT_REJECTED: {
665 rejectedReason.ThrowAbortError("The user rejected the payment");
666 break;
668 case nsIPaymentActionResponse::PAYMENT_NOTSUPPORTED: {
669 rejectedReason.ThrowNotSupportedError("No supported payment method");
670 break;
672 default: {
673 rejectedReason.ThrowUnknownError("Unknown response for the payment");
674 break;
677 // If PaymentActionResponse is not PAYMENT_ACCEPTED, no need to keep the
678 // PaymentRequestChild instance. Otherwise, keep PaymentRequestChild for
679 // merchants call PaymentResponse.complete()
680 if (rejectedReason.Failed()) {
681 NotifyRequestDone(aRequest);
683 aRequest->RespondShowPayment(response.methodName(), responseData,
684 response.payerName(), response.payerEmail(),
685 response.payerPhone(),
686 std::move(rejectedReason));
687 break;
689 case IPCPaymentActionResponse::TIPCPaymentAbortActionResponse: {
690 const IPCPaymentAbortActionResponse& response = aResponse;
691 aRequest->RespondAbortPayment(response.isSucceeded());
692 NotifyRequestDone(aRequest);
693 break;
695 case IPCPaymentActionResponse::TIPCPaymentCompleteActionResponse: {
696 aRequest->RespondComplete();
697 NotifyRequestDone(aRequest);
698 break;
700 default: {
701 return NS_ERROR_FAILURE;
704 return NS_OK;
707 nsresult PaymentRequestManager::ChangeShippingAddress(
708 PaymentRequest* aRequest, const IPCPaymentAddress& aAddress) {
709 return aRequest->UpdateShippingAddress(
710 aAddress.country(), aAddress.addressLine(), aAddress.region(),
711 aAddress.regionCode(), aAddress.city(), aAddress.dependentLocality(),
712 aAddress.postalCode(), aAddress.sortingCode(), aAddress.organization(),
713 aAddress.recipient(), aAddress.phone());
716 nsresult PaymentRequestManager::ChangeShippingOption(PaymentRequest* aRequest,
717 const nsAString& aOption) {
718 return aRequest->UpdateShippingOption(aOption);
721 nsresult PaymentRequestManager::ChangePayerDetail(
722 PaymentRequest* aRequest, const nsAString& aPayerName,
723 const nsAString& aPayerEmail, const nsAString& aPayerPhone) {
724 MOZ_ASSERT(aRequest);
725 RefPtr<PaymentResponse> response = aRequest->GetResponse();
726 // ignoring the case call changePayerDetail during show().
727 if (!response) {
728 return NS_OK;
730 return response->UpdatePayerDetail(aPayerName, aPayerEmail, aPayerPhone);
733 nsresult PaymentRequestManager::ChangePaymentMethod(
734 PaymentRequest* aRequest, const nsAString& aMethodName,
735 const IPCMethodChangeDetails& aMethodDetails) {
736 NS_ENSURE_ARG_POINTER(aRequest);
737 ChangeDetails methodDetails;
738 ConvertMethodChangeDetails(aMethodDetails, methodDetails);
739 return aRequest->UpdatePaymentMethod(aMethodName, methodDetails);
742 } // namespace mozilla::dom