Clean up some includes to make the build a bit faster: DOMPromise
[WebKit-https.git] / Source / WebCore / Modules / paymentrequest / PaymentRequest.cpp
1 /*
2  * Copyright (C) 2017-2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "PaymentRequest.h"
28
29 #if ENABLE(PAYMENT_REQUEST)
30
31 #include "ApplePayPaymentHandler.h"
32 #include "Document.h"
33 #include "EventNames.h"
34 #include "JSDOMPromise.h"
35 #include "JSDOMPromiseDeferred.h"
36 #include "JSPaymentDetailsUpdate.h"
37 #include "JSPaymentResponse.h"
38 #include "Page.h"
39 #include "PaymentAddress.h"
40 #include "PaymentCoordinator.h"
41 #include "PaymentCurrencyAmount.h"
42 #include "PaymentDetailsInit.h"
43 #include "PaymentHandler.h"
44 #include "PaymentMethodChangeEvent.h"
45 #include "PaymentMethodData.h"
46 #include "PaymentOptions.h"
47 #include "PaymentRequestUpdateEvent.h"
48 #include "PaymentValidationErrors.h"
49 #include "ScriptController.h"
50 #include <JavaScriptCore/JSONObject.h>
51 #include <JavaScriptCore/ThrowScope.h>
52 #include <wtf/ASCIICType.h>
53 #include <wtf/IsoMallocInlines.h>
54 #include <wtf/RunLoop.h>
55 #include <wtf/Scope.h>
56 #include <wtf/UUID.h>
57
58 namespace WebCore {
59
60 WTF_MAKE_ISO_ALLOCATED_IMPL(PaymentRequest);
61
62 // Implements the IsWellFormedCurrencyCode abstract operation from ECMA 402
63 // https://tc39.github.io/ecma402/#sec-iswellformedcurrencycode
64 static bool isWellFormedCurrencyCode(const String& currency)
65 {
66     if (currency.length() == 3)
67         return currency.isAllSpecialCharacters<isASCIIAlpha>();
68     return false;
69 }
70
71 // Implements the "valid decimal monetary value" validity checker
72 // https://www.w3.org/TR/payment-request/#dfn-valid-decimal-monetary-value
73 static bool isValidDecimalMonetaryValue(StringView value)
74 {
75     enum class State {
76         Start,
77         Sign,
78         Digit,
79         Dot,
80         DotDigit,
81     };
82
83     auto state = State::Start;
84     for (auto character : value.codeUnits()) {
85         switch (state) {
86         case State::Start:
87             if (character == '-') {
88                 state = State::Sign;
89                 break;
90             }
91
92             if (isASCIIDigit(character)) {
93                 state = State::Digit;
94                 break;
95             }
96
97             return false;
98
99         case State::Sign:
100             if (isASCIIDigit(character)) {
101                 state = State::Digit;
102                 break;
103             }
104
105             return false;
106
107         case State::Digit:
108             if (character == '.') {
109                 state = State::Dot;
110                 break;
111             }
112
113             if (isASCIIDigit(character)) {
114                 state = State::Digit;
115                 break;
116             }
117
118             return false;
119
120         case State::Dot:
121             if (isASCIIDigit(character)) {
122                 state = State::DotDigit;
123                 break;
124             }
125
126             return false;
127
128         case State::DotDigit:
129             if (isASCIIDigit(character)) {
130                 state = State::DotDigit;
131                 break;
132             }
133
134             return false;
135         }
136     }
137
138     if (state == State::Digit || state == State::DotDigit)
139         return true;
140
141     return false;
142 }
143
144 // Implements the "check and canonicalize amount" validity checker
145 // https://www.w3.org/TR/payment-request/#dfn-check-and-canonicalize-amount
146 static ExceptionOr<void> checkAndCanonicalizeAmount(PaymentCurrencyAmount& amount)
147 {
148     if (!isWellFormedCurrencyCode(amount.currency))
149         return Exception { RangeError, makeString("\"", amount.currency, "\" is not a valid currency code.") };
150
151     if (!isValidDecimalMonetaryValue(amount.value))
152         return Exception { TypeError, makeString("\"", amount.value, "\" is not a valid decimal monetary value.") };
153
154     amount.currency = amount.currency.convertToASCIIUppercase();
155     return { };
156 }
157
158 // Implements the "check and canonicalize total" validity checker
159 // https://www.w3.org/TR/payment-request/#dfn-check-and-canonicalize-total
160 static ExceptionOr<void> checkAndCanonicalizeTotal(PaymentCurrencyAmount& total)
161 {
162     auto exception = checkAndCanonicalizeAmount(total);
163     if (exception.hasException())
164         return exception;
165
166     if (total.value[0] == '-')
167         return Exception { TypeError, "Total currency values cannot be negative."_s };
168
169     return { };
170 }
171
172 // Implements "validate a standardized payment method identifier"
173 // https://www.w3.org/TR/payment-method-id/#validity-0
174 static bool isValidStandardizedPaymentMethodIdentifier(StringView identifier)
175 {
176     enum class State {
177         Start,
178         Hyphen,
179         LowerAlpha,
180         Digit,
181     };
182
183     auto state = State::Start;
184     for (auto character : identifier.codeUnits()) {
185         switch (state) {
186         case State::Start:
187         case State::Hyphen:
188             if (isASCIILower(character)) {
189                 state = State::LowerAlpha;
190                 break;
191             }
192
193             return false;
194
195         case State::LowerAlpha:
196         case State::Digit:
197             if (isASCIILower(character)) {
198                 state = State::LowerAlpha;
199                 break;
200             }
201
202             if (isASCIIDigit(character)) {
203                 state = State::Digit;
204                 break;
205             }
206
207             if (character == '-') {
208                 state = State::Hyphen;
209                 break;
210             }
211
212             return false;
213         }
214     }
215
216     return state == State::LowerAlpha || state == State::Digit;
217 }
218
219 // Implements "validate a URL-based payment method identifier"
220 // https://www.w3.org/TR/payment-method-id/#validation
221 static bool isValidURLBasedPaymentMethodIdentifier(const URL& url)
222 {
223     if (!url.protocolIs("https"))
224         return false;
225
226     if (!url.user().isEmpty() || !url.pass().isEmpty())
227         return false;
228
229     return true;
230 }
231
232 // Implements "validate a payment method identifier"
233 // https://www.w3.org/TR/payment-method-id/#validity
234 Optional<PaymentRequest::MethodIdentifier> convertAndValidatePaymentMethodIdentifier(const String& identifier)
235 {
236     URL url { URL(), identifier };
237     if (!url.isValid()) {
238         if (isValidStandardizedPaymentMethodIdentifier(identifier))
239             return { identifier };
240         return WTF::nullopt;
241     }
242
243     if (isValidURLBasedPaymentMethodIdentifier(url))
244         return { WTFMove(url) };
245
246     return WTF::nullopt;
247 }
248
249 enum class ShouldValidatePaymentMethodIdentifier {
250     No,
251     Yes,
252 };
253
254 static ExceptionOr<std::tuple<String, Vector<String>>> checkAndCanonicalizeDetails(JSC::ExecState& execState, PaymentDetailsBase& details, bool requestShipping, ShouldValidatePaymentMethodIdentifier shouldValidatePaymentMethodIdentifier)
255 {
256     for (auto& item : details.displayItems) {
257         auto exception = checkAndCanonicalizeAmount(item.amount);
258         if (exception.hasException())
259             return exception.releaseException();
260     }
261
262     String selectedShippingOption;
263     if (requestShipping) {
264         HashSet<String> seenShippingOptionIDs;
265         for (auto& shippingOption : details.shippingOptions) {
266             auto exception = checkAndCanonicalizeAmount(shippingOption.amount);
267             if (exception.hasException())
268                 return exception.releaseException();
269
270             auto addResult = seenShippingOptionIDs.add(shippingOption.id);
271             if (!addResult.isNewEntry)
272                 return Exception { TypeError, "Shipping option IDs must be unique." };
273
274             if (shippingOption.selected)
275                 selectedShippingOption = shippingOption.id;
276         }
277     }
278
279     Vector<String> serializedModifierData;
280     serializedModifierData.reserveInitialCapacity(details.modifiers.size());
281     for (auto& modifier : details.modifiers) {
282         if (shouldValidatePaymentMethodIdentifier == ShouldValidatePaymentMethodIdentifier::Yes) {
283             auto paymentMethodIdentifier = convertAndValidatePaymentMethodIdentifier(modifier.supportedMethods);
284             if (!paymentMethodIdentifier)
285                 return Exception { RangeError, makeString('"', modifier.supportedMethods, "\" is an invalid payment method identifier.") };
286         }
287
288         if (modifier.total) {
289             auto exception = checkAndCanonicalizeTotal(modifier.total->amount);
290             if (exception.hasException())
291                 return exception.releaseException();
292         }
293
294         for (auto& item : modifier.additionalDisplayItems) {
295             auto exception = checkAndCanonicalizeAmount(item.amount);
296             if (exception.hasException())
297                 return exception.releaseException();
298         }
299
300         String serializedData;
301         if (modifier.data) {
302             auto scope = DECLARE_THROW_SCOPE(execState.vm());
303             serializedData = JSONStringify(&execState, modifier.data.get(), 0);
304             if (scope.exception())
305                 return Exception { ExistingExceptionError };
306             modifier.data.clear();
307         }
308         serializedModifierData.uncheckedAppend(WTFMove(serializedData));
309     }
310
311     return std::make_tuple(WTFMove(selectedShippingOption), WTFMove(serializedModifierData));
312 }
313
314 // Implements the PaymentRequest Constructor
315 // https://www.w3.org/TR/payment-request/#constructor
316 ExceptionOr<Ref<PaymentRequest>> PaymentRequest::create(Document& document, Vector<PaymentMethodData>&& methodData, PaymentDetailsInit&& details, PaymentOptions&& options)
317 {
318     auto canCreateSession = PaymentHandler::canCreateSession(document);
319     if (canCreateSession.hasException())
320         return canCreateSession.releaseException();
321
322     if (details.id.isNull())
323         details.id = createCanonicalUUIDString();
324
325     if (methodData.isEmpty())
326         return Exception { TypeError, "At least one payment method is required."_s };
327
328     Vector<Method> serializedMethodData;
329     serializedMethodData.reserveInitialCapacity(methodData.size());
330     for (auto& paymentMethod : methodData) {
331         auto identifier = convertAndValidatePaymentMethodIdentifier(paymentMethod.supportedMethods);
332         if (!identifier)
333             return Exception { RangeError, makeString('"', paymentMethod.supportedMethods, "\" is an invalid payment method identifier.") };
334
335         String serializedData;
336         if (paymentMethod.data) {
337             auto scope = DECLARE_THROW_SCOPE(document.execState()->vm());
338             serializedData = JSONStringify(document.execState(), paymentMethod.data.get(), 0);
339             if (scope.exception())
340                 return Exception { ExistingExceptionError };
341         }
342         serializedMethodData.uncheckedAppend({ WTFMove(*identifier), WTFMove(serializedData) });
343     }
344
345     auto totalResult = checkAndCanonicalizeTotal(details.total.amount);
346     if (totalResult.hasException())
347         return totalResult.releaseException();
348
349     auto detailsResult = checkAndCanonicalizeDetails(*document.execState(), details, options.requestShipping, ShouldValidatePaymentMethodIdentifier::No);
350     if (detailsResult.hasException())
351         return detailsResult.releaseException();
352
353     auto shippingOptionAndModifierData = detailsResult.releaseReturnValue();
354     return adoptRef(*new PaymentRequest(document, WTFMove(options), WTFMove(details), WTFMove(std::get<1>(shippingOptionAndModifierData)), WTFMove(serializedMethodData), WTFMove(std::get<0>(shippingOptionAndModifierData))));
355 }
356
357 bool PaymentRequest::enabledForContext(ScriptExecutionContext& context)
358 {
359     return PaymentHandler::enabledForContext(context);
360 }
361
362 PaymentRequest::PaymentRequest(Document& document, PaymentOptions&& options, PaymentDetailsInit&& details, Vector<String>&& serializedModifierData, Vector<Method>&& serializedMethodData, String&& selectedShippingOption)
363     : ActiveDOMObject { document }
364     , m_options { WTFMove(options) }
365     , m_details { WTFMove(details) }
366     , m_serializedModifierData { WTFMove(serializedModifierData) }
367     , m_serializedMethodData { WTFMove(serializedMethodData) }
368     , m_shippingOption { WTFMove(selectedShippingOption) }
369 {
370     suspendIfNeeded();
371 }
372
373 PaymentRequest::~PaymentRequest()
374 {
375     ASSERT(!hasPendingActivity());
376     ASSERT(!m_activePaymentHandler);
377 }
378
379 static ExceptionOr<JSC::JSValue> parse(ScriptExecutionContext& context, const String& string)
380 {
381     auto scope = DECLARE_THROW_SCOPE(context.vm());
382     JSC::JSValue data = JSONParse(context.execState(), string);
383     if (scope.exception())
384         return Exception { ExistingExceptionError };
385     return WTFMove(data);
386 }
387
388 // https://www.w3.org/TR/payment-request/#show()-method
389 void PaymentRequest::show(Document& document, RefPtr<DOMPromise>&& detailsPromise, ShowPromise&& promise)
390 {
391     if (!document.frame()) {
392         promise.reject(Exception { AbortError });
393         return;
394     }
395
396     if (!UserGestureIndicator::processingUserGesture()) {
397         promise.reject(Exception { SecurityError, "show() must be triggered by user activation." });
398         return;
399     }
400
401     if (m_state != State::Created) {
402         promise.reject(Exception { InvalidStateError });
403         return;
404     }
405
406     if (PaymentHandler::hasActiveSession(document)) {
407         promise.reject(Exception { AbortError });
408         m_state = State::Closed;
409         return;
410     }
411
412     m_state = State::Interactive;
413     ASSERT(!m_showPromise);
414     m_showPromise = WTF::makeUnique<ShowPromise>(WTFMove(promise));
415
416     RefPtr<PaymentHandler> selectedPaymentHandler;
417     for (auto& paymentMethod : m_serializedMethodData) {
418         auto data = parse(document, paymentMethod.serializedData);
419         if (data.hasException()) {
420             settleShowPromise(data.releaseException());
421             return;
422         }
423
424         auto handler = PaymentHandler::create(document, *this, paymentMethod.identifier);
425         if (!handler)
426             continue;
427
428         auto result = handler->convertData(data.releaseReturnValue());
429         if (result.hasException()) {
430             settleShowPromise(result.releaseException());
431             return;
432         }
433
434         if (!selectedPaymentHandler)
435             selectedPaymentHandler = WTFMove(handler);
436     }
437
438     if (!selectedPaymentHandler) {
439         settleShowPromise(Exception { NotSupportedError });
440         return;
441     }
442
443     auto exception = selectedPaymentHandler->show(document);
444     if (exception.hasException()) {
445         settleShowPromise(exception.releaseException());
446         return;
447     }
448
449     ASSERT(!m_activePaymentHandler);
450     m_activePaymentHandler = PaymentHandlerWithPendingActivity { selectedPaymentHandler.releaseNonNull(), makePendingActivity(*this) };
451
452     if (!detailsPromise)
453         return;
454
455     exception = updateWith(UpdateReason::ShowDetailsResolved, detailsPromise.releaseNonNull());
456     ASSERT(!exception.hasException());
457 }
458
459 void PaymentRequest::abortWithException(Exception&& exception)
460 {
461     ASSERT(m_state == State::Interactive);
462     closeActivePaymentHandler();
463
464     if (m_response)
465         m_response->abortWithException(WTFMove(exception));
466     else
467         settleShowPromise(WTFMove(exception));
468 }
469
470 void PaymentRequest::settleShowPromise(ExceptionOr<PaymentResponse&>&& result)
471 {
472     if (auto showPromise = std::exchange(m_showPromise, nullptr))
473         showPromise->settle(WTFMove(result));
474 }
475
476 void PaymentRequest::closeActivePaymentHandler()
477 {
478     if (auto activePaymentHandler = std::exchange(m_activePaymentHandler, WTF::nullopt))
479         activePaymentHandler->paymentHandler->hide();
480
481     m_isUpdating = false;
482     m_state = State::Closed;
483 }
484
485 void PaymentRequest::stop()
486 {
487     closeActivePaymentHandler();
488     settleShowPromise(Exception { AbortError });
489 }
490
491 // https://www.w3.org/TR/payment-request/#abort()-method
492 void PaymentRequest::abort(AbortPromise&& promise)
493 {
494     if (m_response && m_response->hasRetryPromise()) {
495         promise.reject(Exception { InvalidStateError });
496         return;
497     }
498
499     if (m_state != State::Interactive) {
500         promise.reject(Exception { InvalidStateError });
501         return;
502     }
503
504     abortWithException(Exception { AbortError });
505     promise.resolve();
506 }
507
508 // https://www.w3.org/TR/payment-request/#canmakepayment()-method
509 void PaymentRequest::canMakePayment(Document& document, CanMakePaymentPromise&& promise)
510 {
511     if (m_state != State::Created) {
512         promise.reject(Exception { InvalidStateError });
513         return;
514     }
515
516     for (auto& paymentMethod : m_serializedMethodData) {
517         auto handler = PaymentHandler::create(document, *this, paymentMethod.identifier);
518         if (!handler)
519             continue;
520
521         handler->canMakePayment(document, [promise = WTFMove(promise)](bool canMakePayment) mutable {
522             promise.resolve(canMakePayment);
523         });
524         return;
525     }
526
527     promise.resolve(false);
528 }
529
530 const String& PaymentRequest::id() const
531 {
532     return m_details.id;
533 }
534
535 Optional<PaymentShippingType> PaymentRequest::shippingType() const
536 {
537     if (m_options.requestShipping)
538         return m_options.shippingType;
539     return WTF::nullopt;
540 }
541
542 bool PaymentRequest::canSuspendForDocumentSuspension() const
543 {
544     return !hasPendingActivity();
545 }
546
547 void PaymentRequest::shippingAddressChanged(Ref<PaymentAddress>&& shippingAddress)
548 {
549     whenDetailsSettled([this, protectedThis = makeRefPtr(this), shippingAddress = makeRefPtr(shippingAddress.get())]() mutable {
550         m_shippingAddress = WTFMove(shippingAddress);
551         dispatchEvent(PaymentRequestUpdateEvent::create(eventNames().shippingaddresschangeEvent));
552     });
553 }
554
555 void PaymentRequest::shippingOptionChanged(const String& shippingOption)
556 {
557     whenDetailsSettled([this, protectedThis = makeRefPtr(this), shippingOption]() mutable {
558         m_shippingOption = shippingOption;
559         dispatchEvent(PaymentRequestUpdateEvent::create(eventNames().shippingoptionchangeEvent));
560     });
561 }
562
563 void PaymentRequest::paymentMethodChanged(const String& methodName, PaymentMethodChangeEvent::MethodDetailsFunction&& methodDetailsFunction)
564 {
565     whenDetailsSettled([this, protectedThis = makeRefPtr(this), methodName, methodDetailsFunction = WTFMove(methodDetailsFunction)]() mutable {
566         auto& eventName = eventNames().paymentmethodchangeEvent;
567         if (hasEventListeners(eventName))
568             dispatchEvent(PaymentMethodChangeEvent::create(eventName, methodName, WTFMove(methodDetailsFunction)));
569         else
570             activePaymentHandler()->detailsUpdated(UpdateReason::PaymentMethodChanged, { }, { }, { }, { });
571     });
572 }
573
574 ExceptionOr<void> PaymentRequest::updateWith(UpdateReason reason, Ref<DOMPromise>&& promise)
575 {
576     if (m_state != State::Interactive)
577         return Exception { InvalidStateError };
578
579     if (m_isUpdating)
580         return Exception { InvalidStateError };
581
582     m_isUpdating = true;
583
584     ASSERT(!m_detailsPromise);
585     m_detailsPromise = WTFMove(promise);
586     m_detailsPromise->whenSettled([this, protectedThis = makeRefPtr(this), reason]() {
587         settleDetailsPromise(reason);
588     });
589
590     return { };
591 }
592
593 ExceptionOr<void> PaymentRequest::completeMerchantValidation(Event& event, Ref<DOMPromise>&& merchantSessionPromise)
594 {
595     if (m_state != State::Interactive)
596         return Exception { InvalidStateError };
597
598     event.stopPropagation();
599     event.stopImmediatePropagation();
600
601     m_merchantSessionPromise = WTFMove(merchantSessionPromise);
602     m_merchantSessionPromise->whenSettled([this, protectedThis = makeRefPtr(this)]() {
603         if (m_state != State::Interactive)
604             return;
605
606         if (m_merchantSessionPromise->status() == DOMPromise::Status::Rejected) {
607             abortWithException(Exception { AbortError });
608             return;
609         }
610
611         auto exception = activePaymentHandler()->merchantValidationCompleted(m_merchantSessionPromise->result());
612         if (exception.hasException()) {
613             abortWithException(exception.releaseException());
614             return;
615         }
616     });
617
618     return { };
619 }
620
621 void PaymentRequest::settleDetailsPromise(UpdateReason reason)
622 {
623     auto scopeExit = makeScopeExit([&] {
624         m_isUpdating = false;
625         m_isCancelPending = false;
626         m_detailsPromise = nullptr;
627     });
628
629     if (m_state != State::Interactive)
630         return;
631
632     if (m_isCancelPending || m_detailsPromise->status() == DOMPromise::Status::Rejected) {
633         abortWithException(Exception { AbortError });
634         return;
635     }
636
637     auto& context = *m_detailsPromise->scriptExecutionContext();
638     auto throwScope = DECLARE_THROW_SCOPE(context.vm());
639     auto detailsUpdate = convertDictionary<PaymentDetailsUpdate>(*context.execState(), m_detailsPromise->result());
640     if (throwScope.exception()) {
641         abortWithException(Exception { ExistingExceptionError });
642         return;
643     }
644
645     auto totalResult = checkAndCanonicalizeTotal(detailsUpdate.total.amount);
646     if (totalResult.hasException()) {
647         abortWithException(totalResult.releaseException());
648         return;
649     }
650
651     auto detailsResult = checkAndCanonicalizeDetails(*context.execState(), detailsUpdate, m_options.requestShipping, ShouldValidatePaymentMethodIdentifier::Yes);
652     if (detailsResult.hasException()) {
653         abortWithException(detailsResult.releaseException());
654         return;
655     }
656
657     auto shippingOptionAndModifierData = detailsResult.releaseReturnValue();
658
659     m_details.total = WTFMove(detailsUpdate.total);
660     m_details.displayItems = WTFMove(detailsUpdate.displayItems);
661     if (m_options.requestShipping) {
662         m_details.shippingOptions = WTFMove(detailsUpdate.shippingOptions);
663         m_shippingOption = WTFMove(std::get<0>(shippingOptionAndModifierData));
664     }
665
666     m_details.modifiers = WTFMove(detailsUpdate.modifiers);
667     m_serializedModifierData = WTFMove(std::get<1>(shippingOptionAndModifierData));
668
669     auto result = activePaymentHandler()->detailsUpdated(reason, WTFMove(detailsUpdate.error), WTFMove(detailsUpdate.shippingAddressErrors), WTFMove(detailsUpdate.payerErrors), detailsUpdate.paymentMethodErrors.get());
670     if (result.hasException()) {
671         abortWithException(result.releaseException());
672         return;
673     }
674 }
675
676 void PaymentRequest::whenDetailsSettled(std::function<void()>&& callback)
677 {
678     auto whenSettledFunction = [this, callback = WTFMove(callback)] {
679         ASSERT(m_state == State::Interactive);
680         ASSERT(!m_isUpdating);
681         ASSERT(!m_isCancelPending);
682         ASSERT_UNUSED(this, this);
683         callback();
684     };
685
686     if (!m_detailsPromise) {
687         whenSettledFunction();
688         return;
689     }
690
691     m_detailsPromise->whenSettled([this, protectedThis = makeRefPtr(this), whenSettledFunction = WTFMove(whenSettledFunction)] {
692         if (m_state == State::Interactive)
693             whenSettledFunction();
694     });
695 }
696
697 void PaymentRequest::accept(const String& methodName, PaymentResponse::DetailsFunction&& detailsFunction, Ref<PaymentAddress>&& shippingAddress, const String& payerName, const String& payerEmail, const String& payerPhone)
698 {
699     ASSERT(!m_isUpdating);
700     ASSERT(m_state == State::Interactive);
701
702     bool isRetry = m_response;
703     if (!isRetry) {
704         m_response = PaymentResponse::create(scriptExecutionContext(), *this);
705         m_response->setRequestId(m_details.id);
706     }
707
708     m_response->setMethodName(methodName);
709     m_response->setDetailsFunction(WTFMove(detailsFunction));
710     m_response->setShippingAddress(m_options.requestShipping ? shippingAddress.ptr() : nullptr);
711     m_response->setShippingOption(m_options.requestShipping ? m_shippingOption : String { });
712     m_response->setPayerName(m_options.requestPayerName ? payerName : String { });
713     m_response->setPayerEmail(m_options.requestPayerEmail ? payerEmail : String { });
714     m_response->setPayerPhone(m_options.requestPayerPhone ? payerPhone : String { });
715
716     if (!isRetry)
717         settleShowPromise(*m_response);
718     else {
719         ASSERT(m_response->hasRetryPromise());
720         m_response->settleRetryPromise();
721     }
722
723     m_state = State::Closed;
724 }
725
726 ExceptionOr<void> PaymentRequest::complete(Optional<PaymentComplete>&& result)
727 {
728     ASSERT(m_state == State::Closed);
729     if (!m_activePaymentHandler)
730         return Exception { AbortError };
731
732     activePaymentHandler()->complete(WTFMove(result));
733     m_activePaymentHandler = WTF::nullopt;
734     return { };
735 }
736
737 ExceptionOr<void> PaymentRequest::retry(PaymentValidationErrors&& errors)
738 {
739     ASSERT(m_state == State::Closed);
740     if (!m_activePaymentHandler)
741         return Exception { AbortError };
742
743     m_state = State::Interactive;
744     return activePaymentHandler()->retry(WTFMove(errors));
745 }
746
747 void PaymentRequest::cancel()
748 {
749     m_activePaymentHandler = WTF::nullopt;
750
751     if (m_isUpdating) {
752         m_isCancelPending = true;
753         return;
754     }
755
756     abortWithException(Exception { AbortError });
757 }
758
759 } // namespace WebCore
760
761 #endif // ENABLE(PAYMENT_REQUEST)