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