OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "modules/payments/PaymentRequest.h" | 5 #include "modules/payments/PaymentRequest.h" |
6 | 6 |
7 #include "bindings/core/v8/ExceptionState.h" | 7 #include "bindings/core/v8/ExceptionState.h" |
8 #include "bindings/core/v8/JSONValuesForV8.h" | 8 #include "bindings/core/v8/JSONValuesForV8.h" |
9 #include "bindings/core/v8/ScriptPromiseResolver.h" | 9 #include "bindings/core/v8/ScriptPromiseResolver.h" |
10 #include "bindings/core/v8/ScriptState.h" | 10 #include "bindings/core/v8/ScriptState.h" |
| 11 #include "bindings/modules/v8/V8PaymentDetails.h" |
11 #include "core/EventTypeNames.h" | 12 #include "core/EventTypeNames.h" |
12 #include "core/dom/DOMException.h" | 13 #include "core/dom/DOMException.h" |
13 #include "core/dom/ExceptionCode.h" | 14 #include "core/dom/ExceptionCode.h" |
14 #include "core/events/Event.h" | 15 #include "core/events/Event.h" |
15 #include "core/events/EventQueue.h" | 16 #include "core/events/EventQueue.h" |
16 #include "modules/EventTargetModulesNames.h" | 17 #include "modules/EventTargetModulesNames.h" |
17 #include "modules/payments/PaymentItem.h" | 18 #include "modules/payments/PaymentItem.h" |
| 19 #include "modules/payments/PaymentRequestUpdateEvent.h" |
18 #include "modules/payments/PaymentResponse.h" | 20 #include "modules/payments/PaymentResponse.h" |
19 #include "modules/payments/PaymentsValidators.h" | 21 #include "modules/payments/PaymentsValidators.h" |
20 #include "modules/payments/ShippingAddress.h" | 22 #include "modules/payments/ShippingAddress.h" |
21 #include "modules/payments/ShippingOption.h" | 23 #include "modules/payments/ShippingOption.h" |
22 #include "mojo/public/cpp/bindings/interface_request.h" | 24 #include "mojo/public/cpp/bindings/interface_request.h" |
23 #include "mojo/public/cpp/bindings/wtf_array.h" | 25 #include "mojo/public/cpp/bindings/wtf_array.h" |
24 #include "platform/mojo/MojoHelper.h" | 26 #include "platform/mojo/MojoHelper.h" |
25 #include "public/platform/ServiceRegistry.h" | 27 #include "public/platform/ServiceRegistry.h" |
26 #include <utility> | 28 #include <utility> |
27 | 29 |
(...skipping 110 matching lines...) Loading... |
138 return; | 140 return; |
139 } | 141 } |
140 | 142 |
141 if (!PaymentsValidators::isValidAmountFormat(item.amount().value(), &err
orMessage)) { | 143 if (!PaymentsValidators::isValidAmountFormat(item.amount().value(), &err
orMessage)) { |
142 exceptionState.throwTypeError(errorMessage); | 144 exceptionState.throwTypeError(errorMessage); |
143 return; | 145 return; |
144 } | 146 } |
145 } | 147 } |
146 } | 148 } |
147 | 149 |
| 150 void validatePaymentDetails(const PaymentDetails& details, ExceptionState& excep
tionState) |
| 151 { |
| 152 if (!details.hasItems()) { |
| 153 exceptionState.throwTypeError("Must specify items"); |
| 154 return; |
| 155 } |
| 156 |
| 157 if (details.items().isEmpty()) { |
| 158 exceptionState.throwTypeError("Must specify at least one item"); |
| 159 return; |
| 160 } |
| 161 |
| 162 validateShippingOptionsOrPaymentItems(details.items(), exceptionState); |
| 163 if (exceptionState.hadException()) |
| 164 return; |
| 165 |
| 166 if (details.hasShippingOptions()) |
| 167 validateShippingOptionsOrPaymentItems(details.shippingOptions(), excepti
onState); |
| 168 } |
| 169 |
148 } // namespace | 170 } // namespace |
149 | 171 |
150 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St
ring>& supportedMethods, const PaymentDetails& details, ExceptionState& exceptio
nState) | 172 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St
ring>& supportedMethods, const PaymentDetails& details, ExceptionState& exceptio
nState) |
151 { | 173 { |
152 return new PaymentRequest(scriptState, supportedMethods, details, PaymentOpt
ions(), ScriptValue(), exceptionState); | 174 return new PaymentRequest(scriptState, supportedMethods, details, PaymentOpt
ions(), ScriptValue(), exceptionState); |
153 } | 175 } |
154 | 176 |
155 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St
ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op
tions, ExceptionState& exceptionState) | 177 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St
ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op
tions, ExceptionState& exceptionState) |
156 { | 178 { |
157 return new PaymentRequest(scriptState, supportedMethods, details, options, S
criptValue(), exceptionState); | 179 return new PaymentRequest(scriptState, supportedMethods, details, options, S
criptValue(), exceptionState); |
(...skipping 44 matching lines...) Loading... |
202 ExecutionContext* PaymentRequest::getExecutionContext() const | 224 ExecutionContext* PaymentRequest::getExecutionContext() const |
203 { | 225 { |
204 return ContextLifecycleObserver::getExecutionContext(); | 226 return ContextLifecycleObserver::getExecutionContext(); |
205 } | 227 } |
206 | 228 |
207 ScriptPromise PaymentRequest::complete(ScriptState* scriptState, bool success) | 229 ScriptPromise PaymentRequest::complete(ScriptState* scriptState, bool success) |
208 { | 230 { |
209 if (m_completeResolver) | 231 if (m_completeResolver) |
210 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::
create(InvalidStateError, "Already called complete() once")); | 232 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::
create(InvalidStateError, "Already called complete() once")); |
211 | 233 |
212 m_completeResolver = ScriptPromiseResolver::create(scriptState); | 234 // The payment provider should respond in PaymentRequest::OnComplete(). |
213 m_paymentProvider->Complete(success); | 235 m_paymentProvider->Complete(success); |
214 | 236 |
| 237 m_completeResolver = ScriptPromiseResolver::create(scriptState); |
215 return m_completeResolver->promise(); | 238 return m_completeResolver->promise(); |
216 } | 239 } |
217 | 240 |
| 241 void PaymentRequest::onUpdatePaymentDetails(const ScriptValue& detailsScriptValu
e) |
| 242 { |
| 243 if (!m_showResolver || !m_paymentProvider) |
| 244 return; |
| 245 |
| 246 PaymentDetails details; |
| 247 TrackExceptionState exceptionState; |
| 248 V8PaymentDetails::toImpl(detailsScriptValue.isolate(), detailsScriptValue.v8
Value(), details, exceptionState); |
| 249 if (exceptionState.hadException()) { |
| 250 m_showResolver->reject(DOMException::create(SyntaxError, exceptionState.
message())); |
| 251 clearResolversAndCloseMojoConnection(); |
| 252 return; |
| 253 } |
| 254 |
| 255 validatePaymentDetails(details, exceptionState); |
| 256 if (exceptionState.hadException()) { |
| 257 m_showResolver->reject(DOMException::create(SyntaxError, exceptionState.
message())); |
| 258 clearResolversAndCloseMojoConnection(); |
| 259 return; |
| 260 } |
| 261 |
| 262 // Set the currently selected option if only one option was passed. |
| 263 if (details.hasShippingOptions() && details.shippingOptions().size() == 1) |
| 264 m_shippingOption = details.shippingOptions().begin()->id(); |
| 265 else |
| 266 m_shippingOption = String(); |
| 267 |
| 268 m_paymentProvider->UpdateWith(mojom::blink::PaymentDetails::From(details)); |
| 269 } |
| 270 |
| 271 void PaymentRequest::onUpdatePaymentDetailsFailure(const ScriptValue& error) |
| 272 { |
| 273 if (m_showResolver) |
| 274 m_showResolver->reject(error); |
| 275 if (m_completeResolver) |
| 276 m_completeResolver->reject(error); |
| 277 clearResolversAndCloseMojoConnection(); |
| 278 } |
| 279 |
218 DEFINE_TRACE(PaymentRequest) | 280 DEFINE_TRACE(PaymentRequest) |
219 { | 281 { |
220 visitor->trace(m_details); | 282 visitor->trace(m_details); |
221 visitor->trace(m_options); | 283 visitor->trace(m_options); |
222 visitor->trace(m_shippingAddress); | 284 visitor->trace(m_shippingAddress); |
223 visitor->trace(m_showResolver); | 285 visitor->trace(m_showResolver); |
224 visitor->trace(m_completeResolver); | 286 visitor->trace(m_completeResolver); |
225 EventTargetWithInlineData::trace(visitor); | 287 EventTargetWithInlineData::trace(visitor); |
226 ContextLifecycleObserver::trace(visitor); | 288 ContextLifecycleObserver::trace(visitor); |
227 } | 289 } |
228 | 290 |
229 PaymentRequest::PaymentRequest(ScriptState* scriptState, const Vector<String>& s
upportedMethods, const PaymentDetails& details, const PaymentOptions& options, c
onst ScriptValue& data, ExceptionState& exceptionState) | 291 PaymentRequest::PaymentRequest(ScriptState* scriptState, const Vector<String>& s
upportedMethods, const PaymentDetails& details, const PaymentOptions& options, c
onst ScriptValue& data, ExceptionState& exceptionState) |
230 : ContextLifecycleObserver(scriptState->getExecutionContext()) | 292 : ContextLifecycleObserver(scriptState->getExecutionContext()) |
231 , m_supportedMethods(supportedMethods) | |
232 , m_details(details) | |
233 , m_options(options) | 293 , m_options(options) |
234 , m_clientBinding(this) | 294 , m_clientBinding(this) |
235 { | 295 { |
236 // TODO(rouslan): Also check for a top-level browsing context. | 296 // TODO(rouslan): Also check for a top-level browsing context. |
237 // https://github.com/w3c/browser-payment-api/issues/2 | 297 // https://github.com/w3c/browser-payment-api/issues/2 |
238 if (!scriptState->getExecutionContext()->isSecureContext()) { | 298 if (!scriptState->getExecutionContext()->isSecureContext()) { |
239 exceptionState.throwSecurityError("Must be in a secure context"); | 299 exceptionState.throwSecurityError("Must be in a secure context"); |
240 return; | 300 return; |
241 } | 301 } |
242 | 302 |
243 if (supportedMethods.isEmpty()) { | 303 if (supportedMethods.isEmpty()) { |
244 exceptionState.throwTypeError("Must specify at least one payment method
identifier"); | 304 exceptionState.throwTypeError("Must specify at least one payment method
identifier"); |
245 return; | 305 return; |
246 } | 306 } |
| 307 m_supportedMethods = supportedMethods; |
247 | 308 |
248 if (!details.hasItems()) { | 309 validatePaymentDetails(details, exceptionState); |
249 exceptionState.throwTypeError("Must specify items"); | |
250 return; | |
251 } | |
252 | |
253 if (details.items().isEmpty()) { | |
254 exceptionState.throwTypeError("Must specify at least one item"); | |
255 return; | |
256 } | |
257 | |
258 validateShippingOptionsOrPaymentItems(details.items(), exceptionState); | |
259 if (exceptionState.hadException()) | 310 if (exceptionState.hadException()) |
260 return; | 311 return; |
261 | 312 m_details = details; |
262 if (details.hasShippingOptions()) { | |
263 validateShippingOptionsOrPaymentItems(details.shippingOptions(), excepti
onState); | |
264 if (exceptionState.hadException()) | |
265 return; | |
266 } | |
267 | 313 |
268 if (!data.isEmpty()) { | 314 if (!data.isEmpty()) { |
269 RefPtr<JSONValue> value = toJSONValue(data.context(), data.v8Value()); | 315 RefPtr<JSONValue> value = toJSONValue(data.context(), data.v8Value()); |
270 if (!value) { | 316 if (!value) { |
271 exceptionState.throwTypeError("Unable to parse payment method specif
ic data"); | 317 exceptionState.throwTypeError("Unable to parse payment method specif
ic data"); |
272 return; | 318 return; |
273 } | 319 } |
274 if (!value->isNull()) { | 320 if (!value->isNull()) { |
275 if (value->getType() != JSONValue::TypeObject) { | 321 if (value->getType() != JSONValue::TypeObject) { |
276 exceptionState.throwTypeError("Payment method specific data shou
ld be a JSON-serializable object"); | 322 exceptionState.throwTypeError("Payment method specific data shou
ld be a JSON-serializable object"); |
(...skipping 16 matching lines...) Loading... |
293 } | 339 } |
294 } | 340 } |
295 | 341 |
296 // Set the currently selected option if only one option is passed and shippi
ng is requested. | 342 // Set the currently selected option if only one option is passed and shippi
ng is requested. |
297 if (options.requestShipping() && details.hasShippingOptions() && details.shi
ppingOptions().size() == 1) | 343 if (options.requestShipping() && details.hasShippingOptions() && details.shi
ppingOptions().size() == 1) |
298 m_shippingOption = details.shippingOptions().begin()->id(); | 344 m_shippingOption = details.shippingOptions().begin()->id(); |
299 } | 345 } |
300 | 346 |
301 void PaymentRequest::contextDestroyed() | 347 void PaymentRequest::contextDestroyed() |
302 { | 348 { |
303 cleanUp(); | 349 clearResolversAndCloseMojoConnection(); |
304 } | 350 } |
305 | 351 |
306 void PaymentRequest::OnShippingAddressChange(mojom::blink::ShippingAddressPtr ad
dress) | 352 void PaymentRequest::OnShippingAddressChange(mojom::blink::ShippingAddressPtr ad
dress) |
307 { | 353 { |
308 DCHECK(m_showResolver); | 354 DCHECK(m_showResolver); |
309 DCHECK(!m_completeResolver); | 355 DCHECK(!m_completeResolver); |
310 | 356 |
311 String errorMessage; | 357 String errorMessage; |
312 if (!PaymentsValidators::isValidShippingAddress(address, &errorMessage)) { | 358 if (!PaymentsValidators::isValidShippingAddress(address, &errorMessage)) { |
313 m_showResolver->reject(DOMException::create(SyntaxError, errorMessage)); | 359 m_showResolver->reject(DOMException::create(SyntaxError, errorMessage)); |
314 cleanUp(); | 360 clearResolversAndCloseMojoConnection(); |
315 return; | 361 return; |
316 } | 362 } |
317 | 363 |
318 m_shippingAddress = new ShippingAddress(std::move(address)); | 364 m_shippingAddress = new ShippingAddress(std::move(address)); |
319 Event* event = Event::create(EventTypeNames::shippingaddresschange); | 365 PaymentRequestUpdateEvent* event = PaymentRequestUpdateEvent::create(EventTy
peNames::shippingaddresschange); |
320 event->setTarget(this); | 366 event->setTarget(this); |
321 getExecutionContext()->getEventQueue()->enqueueEvent(event); | 367 event->setPaymentDetailsUpdater(this); |
| 368 bool success = getExecutionContext()->getEventQueue()->enqueueEvent(event); |
| 369 DCHECK(success); |
| 370 ALLOW_UNUSED_LOCAL(success); |
322 } | 371 } |
323 | 372 |
324 void PaymentRequest::OnShippingOptionChange(const String& shippingOptionId) | 373 void PaymentRequest::OnShippingOptionChange(const String& shippingOptionId) |
325 { | 374 { |
326 DCHECK(m_showResolver); | 375 DCHECK(m_showResolver); |
327 DCHECK(!m_completeResolver); | 376 DCHECK(!m_completeResolver); |
328 m_shippingOption = shippingOptionId; | 377 m_shippingOption = shippingOptionId; |
329 Event* event = Event::create(EventTypeNames::shippingoptionchange); | 378 PaymentRequestUpdateEvent* event = PaymentRequestUpdateEvent::create(EventTy
peNames::shippingoptionchange); |
330 event->setTarget(this); | 379 event->setTarget(this); |
331 getExecutionContext()->getEventQueue()->enqueueEvent(event); | 380 event->setPaymentDetailsUpdater(this); |
| 381 bool success = getExecutionContext()->getEventQueue()->enqueueEvent(event); |
| 382 DCHECK(success); |
| 383 ALLOW_UNUSED_LOCAL(success); |
332 } | 384 } |
333 | 385 |
334 void PaymentRequest::OnPaymentResponse(mojom::blink::PaymentResponsePtr response
) | 386 void PaymentRequest::OnPaymentResponse(mojom::blink::PaymentResponsePtr response
) |
335 { | 387 { |
336 DCHECK(m_showResolver); | 388 DCHECK(m_showResolver); |
337 DCHECK(!m_completeResolver); | 389 DCHECK(!m_completeResolver); |
338 | 390 |
339 if (response->shipping_address) { | 391 if (response->shipping_address) { |
340 String errorMessage; | 392 String errorMessage; |
341 if (!PaymentsValidators::isValidShippingAddress(response->shipping_addre
ss, &errorMessage)) { | 393 if (!PaymentsValidators::isValidShippingAddress(response->shipping_addre
ss, &errorMessage)) { |
342 m_showResolver->reject(DOMException::create(SyntaxError, errorMessag
e)); | 394 m_showResolver->reject(DOMException::create(SyntaxError, errorMessag
e)); |
343 cleanUp(); | 395 clearResolversAndCloseMojoConnection(); |
344 return; | 396 return; |
345 } | 397 } |
346 | 398 |
347 m_shippingAddress = new ShippingAddress(std::move(response->shipping_add
ress)); | 399 m_shippingAddress = new ShippingAddress(std::move(response->shipping_add
ress)); |
348 m_shippingOption = response->shipping_option_id; | 400 m_shippingOption = response->shipping_option_id; |
349 } | 401 } |
350 | 402 |
351 m_showResolver->resolve(new PaymentResponse(std::move(response), this)); | 403 m_showResolver->resolve(new PaymentResponse(std::move(response), this)); |
| 404 |
| 405 // Do not close the mojo connection here. The merchant website should call |
| 406 // PaymentResponse::complete(boolean), which will be forwarded over the mojo |
| 407 // connection to display a success or failure message to the user. |
| 408 m_showResolver.clear(); |
352 } | 409 } |
353 | 410 |
354 void PaymentRequest::OnError() | 411 void PaymentRequest::OnError() |
355 { | 412 { |
356 if (m_completeResolver) | 413 if (m_completeResolver) |
357 m_completeResolver->reject(DOMException::create(SyntaxError, "Request ca
ncelled")); | 414 m_completeResolver->reject(DOMException::create(SyntaxError, "Request ca
ncelled")); |
358 if (m_showResolver) | 415 if (m_showResolver) |
359 m_showResolver->reject(DOMException::create(SyntaxError, "Request cancel
led")); | 416 m_showResolver->reject(DOMException::create(SyntaxError, "Request cancel
led")); |
360 cleanUp(); | 417 clearResolversAndCloseMojoConnection(); |
361 } | 418 } |
362 | 419 |
363 void PaymentRequest::OnComplete() | 420 void PaymentRequest::OnComplete() |
364 { | 421 { |
365 DCHECK(m_completeResolver); | 422 DCHECK(m_completeResolver); |
366 m_completeResolver->resolve(); | 423 m_completeResolver->resolve(); |
367 cleanUp(); | 424 clearResolversAndCloseMojoConnection(); |
368 } | 425 } |
369 | 426 |
370 void PaymentRequest::cleanUp() | 427 void PaymentRequest::clearResolversAndCloseMojoConnection() |
371 { | 428 { |
372 m_completeResolver.clear(); | 429 m_completeResolver.clear(); |
373 m_showResolver.clear(); | 430 m_showResolver.clear(); |
374 if (m_clientBinding.is_bound()) | 431 if (m_clientBinding.is_bound()) |
375 m_clientBinding.Close(); | 432 m_clientBinding.Close(); |
376 m_paymentProvider.reset(); | 433 m_paymentProvider.reset(); |
377 } | 434 } |
378 | 435 |
379 } // namespace blink | 436 } // namespace blink |
OLD | NEW |