Index: third_party/WebKit/Source/modules/payments/PaymentRequest.cpp |
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp |
index cd99d37cccb5ca2102cb723d95c7c7530dda5e0e..be948757a6651bef88a1858acc88fbbd932a4c02 100644 |
--- a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp |
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp |
@@ -159,6 +159,10 @@ struct TypeConverter<WTFArray<PaymentMethodDataPtr>, WTF::Vector<blink::PaymentR |
namespace blink { |
namespace { |
+// If the website does not call complete() 60 seconds after show() has been resolved, then behave as if |
+// the website called complete("fail"). |
+static const int completeTimeoutSeconds = 60; |
+ |
// Validates ShippingOption or PaymentItem, which happen to have identical fields, |
// except for "id", which is present only in ShippingOption. |
template <typename T> |
@@ -418,10 +422,15 @@ ScriptPromise PaymentRequest::complete(ScriptState* scriptState, PaymentComplete |
if (m_completeResolver) |
return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(InvalidStateError, "Already called complete() once")); |
+ if (!m_completeTimer.isActive()) |
+ return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(InvalidStateError, "Timed out after 60 seconds, complete() called too late")); |
+ |
// User has cancelled the transaction while the website was processing it. |
if (!m_paymentProvider) |
return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(InvalidStateError, "Request cancelled")); |
+ m_completeTimer.stop(); |
+ |
// The payment provider should respond in PaymentRequest::OnComplete(). |
m_paymentProvider->Complete(mojom::blink::PaymentComplete(result)); |
@@ -477,11 +486,17 @@ DEFINE_TRACE(PaymentRequest) |
ContextLifecycleObserver::trace(visitor); |
} |
+void PaymentRequest::onCompleteTimeoutForTesting() |
+{ |
+ onCompleteTimeout(0); |
+} |
+ |
PaymentRequest::PaymentRequest(ScriptState* scriptState, const HeapVector<PaymentMethodData>& methodData, const PaymentDetails& details, const PaymentOptions& options, ExceptionState& exceptionState) |
: ContextLifecycleObserver(scriptState->getExecutionContext()) |
, ActiveScriptWrappable(this) |
, m_options(options) |
, m_clientBinding(this) |
+ , m_completeTimer(this, &PaymentRequest::onCompleteTimeout) |
{ |
validateAndConvertPaymentMethodData(methodData, &m_methodData, exceptionState); |
if (exceptionState.hadException()) |
@@ -554,6 +569,7 @@ void PaymentRequest::OnPaymentResponse(mojom::blink::PaymentResponsePtr response |
{ |
DCHECK(m_showResolver); |
DCHECK(!m_completeResolver); |
+ DCHECK(!m_completeTimer.isActive()); |
if (m_options.requestShipping()) { |
if (!response->shipping_address || response->shipping_option.isEmpty()) { |
@@ -588,10 +604,12 @@ void PaymentRequest::OnPaymentResponse(mojom::blink::PaymentResponsePtr response |
return; |
} |
+ m_completeTimer.startOneShot(completeTimeoutSeconds, BLINK_FROM_HERE); |
+ |
m_showResolver->resolve(new PaymentResponse(std::move(response), this)); |
// Do not close the mojo connection here. The merchant website should call |
- // PaymentResponse::complete(boolean), which will be forwarded over the mojo |
+ // PaymentResponse::complete(String), which will be forwarded over the mojo |
// connection to display a success or failure message to the user. |
m_showResolver.clear(); |
} |
@@ -666,6 +684,13 @@ void PaymentRequest::OnAbort(bool abortedSuccessfully) |
clearResolversAndCloseMojoConnection(); |
} |
+void PaymentRequest::onCompleteTimeout(TimerBase*) |
+{ |
+ m_completeTimer.stop(); |
+ m_paymentProvider->Complete(mojom::blink::PaymentComplete(Fail)); |
+ clearResolversAndCloseMojoConnection(); |
+} |
+ |
void PaymentRequest::clearResolversAndCloseMojoConnection() |
{ |
m_completeResolver.clear(); |