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" |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
222 | 222 |
223 void validateDisplayItems(const HeapVector<PaymentItem>& items, | 223 void validateDisplayItems(const HeapVector<PaymentItem>& items, |
224 ExceptionState& exceptionState) { | 224 ExceptionState& exceptionState) { |
225 for (const auto& item : items) { | 225 for (const auto& item : items) { |
226 validateShippingOptionOrPaymentItem(item, exceptionState); | 226 validateShippingOptionOrPaymentItem(item, exceptionState); |
227 if (exceptionState.hadException()) | 227 if (exceptionState.hadException()) |
228 return; | 228 return; |
229 } | 229 } |
230 } | 230 } |
231 | 231 |
232 // Returns false if |options| should be ignored, even if an exception was not | 232 void validateShippingOptions(HeapVector<PaymentShippingOption>& options, |
233 // thrown. TODO(rouslan): Clear shipping options instead of ignoring them when | |
234 // http://crbug.com/601193 is fixed. | |
235 bool validateShippingOptions(const HeapVector<PaymentShippingOption>& options, | |
236 ExceptionState& exceptionState) { | 233 ExceptionState& exceptionState) { |
237 HashSet<String> uniqueIds; | 234 HashSet<String> uniqueIds; |
238 for (const auto& option : options) { | 235 for (const auto& option : options) { |
239 if (!option.hasId() || option.id().isEmpty()) { | 236 if (!option.hasId() || option.id().isEmpty()) { |
240 exceptionState.throwTypeError("ShippingOption id required"); | 237 exceptionState.throwTypeError("ShippingOption id required"); |
241 return false; | 238 return; |
242 } | 239 } |
243 | 240 |
244 if (uniqueIds.contains(option.id())) | 241 if (uniqueIds.contains(option.id())) { |
245 return false; | 242 options.clear(); |
| 243 return; |
| 244 } |
246 | 245 |
247 uniqueIds.add(option.id()); | 246 uniqueIds.add(option.id()); |
248 | 247 |
249 validateShippingOptionOrPaymentItem(option, exceptionState); | 248 validateShippingOptionOrPaymentItem(option, exceptionState); |
250 if (exceptionState.hadException()) | 249 if (exceptionState.hadException()) |
251 return false; | 250 return; |
252 } | 251 } |
253 | |
254 return true; | |
255 } | 252 } |
256 | 253 |
257 void validatePaymentDetailsModifiers( | 254 void validatePaymentDetailsModifiers( |
258 const HeapVector<PaymentDetailsModifier>& modifiers, | 255 const HeapVector<PaymentDetailsModifier>& modifiers, |
259 ExceptionState& exceptionState) { | 256 ExceptionState& exceptionState) { |
260 if (modifiers.isEmpty()) { | 257 if (modifiers.isEmpty()) { |
261 exceptionState.throwTypeError( | 258 exceptionState.throwTypeError( |
262 "Must specify at least one payment details modifier"); | 259 "Must specify at least one payment details modifier"); |
263 return; | 260 return; |
264 } | 261 } |
(...skipping 28 matching lines...) Expand all Loading... |
293 } | 290 } |
294 | 291 |
295 if (modifier.hasAdditionalDisplayItems()) { | 292 if (modifier.hasAdditionalDisplayItems()) { |
296 validateDisplayItems(modifier.additionalDisplayItems(), exceptionState); | 293 validateDisplayItems(modifier.additionalDisplayItems(), exceptionState); |
297 if (exceptionState.hadException()) | 294 if (exceptionState.hadException()) |
298 return; | 295 return; |
299 } | 296 } |
300 } | 297 } |
301 } | 298 } |
302 | 299 |
303 // Returns false if the shipping options should be ignored without throwing an | 300 void validatePaymentDetails(PaymentDetails& details, |
304 // exception. | |
305 bool validatePaymentDetails(const PaymentDetails& details, | |
306 ExceptionState& exceptionState) { | 301 ExceptionState& exceptionState) { |
307 bool keepShippingOptions = true; | |
308 if (!details.hasTotal()) { | 302 if (!details.hasTotal()) { |
309 exceptionState.throwTypeError("Must specify total"); | 303 exceptionState.throwTypeError("Must specify total"); |
310 return keepShippingOptions; | 304 return; |
311 } | 305 } |
312 | 306 |
313 validateShippingOptionOrPaymentItem(details.total(), exceptionState); | 307 validateShippingOptionOrPaymentItem(details.total(), exceptionState); |
314 if (exceptionState.hadException()) | 308 if (exceptionState.hadException()) |
315 return keepShippingOptions; | 309 return; |
316 | 310 |
317 if (details.total().amount().value()[0] == '-') { | 311 if (details.total().amount().value()[0] == '-') { |
318 exceptionState.throwTypeError("Total amount value should be non-negative"); | 312 exceptionState.throwTypeError("Total amount value should be non-negative"); |
319 return keepShippingOptions; | 313 return; |
320 } | 314 } |
321 | 315 |
322 if (details.hasDisplayItems()) { | 316 if (details.hasDisplayItems()) { |
323 validateDisplayItems(details.displayItems(), exceptionState); | 317 validateDisplayItems(details.displayItems(), exceptionState); |
324 if (exceptionState.hadException()) | 318 if (exceptionState.hadException()) |
325 return keepShippingOptions; | 319 return; |
326 } | 320 } |
327 | 321 |
328 if (details.hasShippingOptions()) { | 322 if (details.hasShippingOptions()) { |
329 keepShippingOptions = | 323 HeapVector<PaymentShippingOption> options = details.shippingOptions(); |
330 validateShippingOptions(details.shippingOptions(), exceptionState); | 324 validateShippingOptions(options, exceptionState); |
| 325 details.setShippingOptions(options); |
331 | 326 |
332 if (exceptionState.hadException()) | 327 if (exceptionState.hadException()) |
333 return keepShippingOptions; | 328 return; |
334 } | 329 } |
335 | 330 |
336 if (details.hasModifiers()) { | 331 if (details.hasModifiers()) { |
337 validatePaymentDetailsModifiers(details.modifiers(), exceptionState); | 332 validatePaymentDetailsModifiers(details.modifiers(), exceptionState); |
338 if (exceptionState.hadException()) | 333 if (exceptionState.hadException()) |
339 return keepShippingOptions; | 334 return; |
340 } | 335 } |
341 | 336 |
342 String errorMessage; | 337 String errorMessage; |
343 if (!PaymentsValidators::isValidErrorMsgFormat(details.error(), | 338 if (!PaymentsValidators::isValidErrorMsgFormat(details.error(), |
344 &errorMessage)) { | 339 &errorMessage)) { |
345 exceptionState.throwTypeError(errorMessage); | 340 exceptionState.throwTypeError(errorMessage); |
346 } | 341 } |
347 | |
348 return keepShippingOptions; | |
349 } | 342 } |
350 | 343 |
351 void validateAndConvertPaymentMethodData( | 344 void validateAndConvertPaymentMethodData( |
352 const HeapVector<PaymentMethodData>& paymentMethodData, | 345 const HeapVector<PaymentMethodData>& paymentMethodData, |
353 Vector<PaymentRequest::MethodData>* methodData, | 346 Vector<PaymentRequest::MethodData>* methodData, |
354 ExceptionState& exceptionState) { | 347 ExceptionState& exceptionState) { |
355 if (paymentMethodData.isEmpty()) { | 348 if (paymentMethodData.isEmpty()) { |
356 exceptionState.throwTypeError( | 349 exceptionState.throwTypeError( |
357 "Must specify at least one payment method identifier"); | 350 "Must specify at least one payment method identifier"); |
358 return; | 351 return; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
417 static const char* const validValues[] = { | 410 static const char* const validValues[] = { |
418 "shipping", "delivery", "pickup", | 411 "shipping", "delivery", "pickup", |
419 }; | 412 }; |
420 for (size_t i = 0; i < WTF_ARRAY_LENGTH(validValues); i++) { | 413 for (size_t i = 0; i < WTF_ARRAY_LENGTH(validValues); i++) { |
421 if (shippingType == validValues[i]) | 414 if (shippingType == validValues[i]) |
422 return shippingType; | 415 return shippingType; |
423 } | 416 } |
424 return validValues[0]; | 417 return validValues[0]; |
425 } | 418 } |
426 | 419 |
427 mojom::blink::PaymentDetailsPtr maybeKeepShippingOptions( | |
428 mojom::blink::PaymentDetailsPtr details, | |
429 bool keep) { | |
430 if (!keep) | |
431 details->shipping_options.resize(0); | |
432 | |
433 return details; | |
434 } | |
435 | |
436 bool allowedToUsePaymentRequest(const Frame* frame) { | 420 bool allowedToUsePaymentRequest(const Frame* frame) { |
437 // To determine whether a Document object |document| is allowed to use the | 421 // To determine whether a Document object |document| is allowed to use the |
438 // feature indicated by attribute name |allowpaymentrequest|, run these steps: | 422 // feature indicated by attribute name |allowpaymentrequest|, run these steps: |
439 | 423 |
440 // 1. If |document| has no browsing context, then return false. | 424 // 1. If |document| has no browsing context, then return false. |
441 if (!frame) | 425 if (!frame) |
442 return false; | 426 return false; |
443 | 427 |
444 // 2. If |document|'s browsing context is a top-level browsing context, then | 428 // 2. If |document|'s browsing context is a top-level browsing context, then |
445 // return true. | 429 // return true. |
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
584 V8PaymentDetails::toImpl(detailsScriptValue.isolate(), | 568 V8PaymentDetails::toImpl(detailsScriptValue.isolate(), |
585 detailsScriptValue.v8Value(), details, | 569 detailsScriptValue.v8Value(), details, |
586 exceptionState); | 570 exceptionState); |
587 if (exceptionState.hadException()) { | 571 if (exceptionState.hadException()) { |
588 m_showResolver->reject( | 572 m_showResolver->reject( |
589 DOMException::create(SyntaxError, exceptionState.message())); | 573 DOMException::create(SyntaxError, exceptionState.message())); |
590 clearResolversAndCloseMojoConnection(); | 574 clearResolversAndCloseMojoConnection(); |
591 return; | 575 return; |
592 } | 576 } |
593 | 577 |
594 bool keepShippingOptions = validatePaymentDetails(details, exceptionState); | 578 validatePaymentDetails(details, exceptionState); |
595 if (exceptionState.hadException()) { | 579 if (exceptionState.hadException()) { |
596 m_showResolver->reject( | 580 m_showResolver->reject( |
597 DOMException::create(SyntaxError, exceptionState.message())); | 581 DOMException::create(SyntaxError, exceptionState.message())); |
598 clearResolversAndCloseMojoConnection(); | 582 clearResolversAndCloseMojoConnection(); |
599 return; | 583 return; |
600 } | 584 } |
601 | 585 |
602 if (m_options.requestShipping()) { | 586 if (m_options.requestShipping()) |
603 if (keepShippingOptions) | 587 m_shippingOption = getSelectedShippingOption(details); |
604 m_shippingOption = getSelectedShippingOption(details); | |
605 else | |
606 m_shippingOption = String(); | |
607 } | |
608 | 588 |
609 m_paymentProvider->UpdateWith(maybeKeepShippingOptions( | 589 m_paymentProvider->UpdateWith(mojom::blink::PaymentDetails::From(details)); |
610 mojom::blink::PaymentDetails::From(details), keepShippingOptions)); | |
611 } | 590 } |
612 | 591 |
613 void PaymentRequest::onUpdatePaymentDetailsFailure(const String& error) { | 592 void PaymentRequest::onUpdatePaymentDetailsFailure(const String& error) { |
614 if (m_showResolver) | 593 if (m_showResolver) |
615 m_showResolver->reject(DOMException::create(AbortError, error)); | 594 m_showResolver->reject(DOMException::create(AbortError, error)); |
616 if (m_completeResolver) | 595 if (m_completeResolver) |
617 m_completeResolver->reject(DOMException::create(AbortError, error)); | 596 m_completeResolver->reject(DOMException::create(AbortError, error)); |
618 clearResolversAndCloseMojoConnection(); | 597 clearResolversAndCloseMojoConnection(); |
619 } | 598 } |
620 | 599 |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
654 return; | 633 return; |
655 } | 634 } |
656 | 635 |
657 if (!allowedToUsePaymentRequest(scriptState->domWindow()->frame())) { | 636 if (!allowedToUsePaymentRequest(scriptState->domWindow()->frame())) { |
658 exceptionState.throwSecurityError( | 637 exceptionState.throwSecurityError( |
659 "Must be in a top-level browsing context or an iframe needs to specify " | 638 "Must be in a top-level browsing context or an iframe needs to specify " |
660 "'allowpaymentrequest' explicitly"); | 639 "'allowpaymentrequest' explicitly"); |
661 return; | 640 return; |
662 } | 641 } |
663 | 642 |
664 bool keepShippingOptions = validatePaymentDetails(details, exceptionState); | 643 PaymentDetails fixedDetails = details; |
| 644 validatePaymentDetails(fixedDetails, exceptionState); |
665 if (exceptionState.hadException()) | 645 if (exceptionState.hadException()) |
666 return; | 646 return; |
667 | 647 |
668 if (details.hasError() && !details.error().isEmpty()) { | 648 if (fixedDetails.hasError() && !fixedDetails.error().isEmpty()) { |
669 exceptionState.throwTypeError("Error value should be empty"); | 649 exceptionState.throwTypeError("Error value should be empty"); |
670 return; | 650 return; |
671 } | 651 } |
672 | 652 |
673 if (m_options.requestShipping()) { | 653 if (m_options.requestShipping()) { |
674 if (keepShippingOptions) | 654 m_shippingOption = getSelectedShippingOption(fixedDetails); |
675 m_shippingOption = getSelectedShippingOption(details); | |
676 m_shippingType = getValidShippingType(m_options.shippingType()); | 655 m_shippingType = getValidShippingType(m_options.shippingType()); |
| 656 } else { |
| 657 fixedDetails.setShippingOptions(HeapVector<PaymentShippingOption>()); |
677 } | 658 } |
678 | 659 |
679 scriptState->domWindow()->frame()->interfaceProvider()->getInterface( | 660 scriptState->domWindow()->frame()->interfaceProvider()->getInterface( |
680 mojo::GetProxy(&m_paymentProvider)); | 661 mojo::GetProxy(&m_paymentProvider)); |
681 m_paymentProvider.set_connection_error_handler(convertToBaseCallback( | 662 m_paymentProvider.set_connection_error_handler(convertToBaseCallback( |
682 WTF::bind(&PaymentRequest::OnError, wrapWeakPersistent(this), | 663 WTF::bind(&PaymentRequest::OnError, wrapWeakPersistent(this), |
683 mojom::blink::PaymentErrorReason::UNKNOWN))); | 664 mojom::blink::PaymentErrorReason::UNKNOWN))); |
684 m_paymentProvider->Init( | 665 m_paymentProvider->Init(m_clientBinding.CreateInterfacePtrAndBind(), |
685 m_clientBinding.CreateInterfacePtrAndBind(), | 666 ConvertPaymentMethodData(validatedMethodData), |
686 ConvertPaymentMethodData(validatedMethodData), | 667 mojom::blink::PaymentDetails::From(fixedDetails), |
687 maybeKeepShippingOptions( | 668 mojom::blink::PaymentOptions::From(m_options)); |
688 mojom::blink::PaymentDetails::From(details), | |
689 keepShippingOptions && m_options.requestShipping()), | |
690 mojom::blink::PaymentOptions::From(m_options)); | |
691 } | 669 } |
692 | 670 |
693 void PaymentRequest::contextDestroyed() { | 671 void PaymentRequest::contextDestroyed() { |
694 clearResolversAndCloseMojoConnection(); | 672 clearResolversAndCloseMojoConnection(); |
695 } | 673 } |
696 | 674 |
697 void PaymentRequest::OnShippingAddressChange( | 675 void PaymentRequest::OnShippingAddressChange( |
698 mojom::blink::PaymentAddressPtr address) { | 676 mojom::blink::PaymentAddressPtr address) { |
699 DCHECK(m_showResolver); | 677 DCHECK(m_showResolver); |
700 DCHECK(!m_completeResolver); | 678 DCHECK(!m_completeResolver); |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
857 m_completeTimer.stop(); | 835 m_completeTimer.stop(); |
858 m_completeResolver.clear(); | 836 m_completeResolver.clear(); |
859 m_showResolver.clear(); | 837 m_showResolver.clear(); |
860 m_abortResolver.clear(); | 838 m_abortResolver.clear(); |
861 if (m_clientBinding.is_bound()) | 839 if (m_clientBinding.is_bound()) |
862 m_clientBinding.Close(); | 840 m_clientBinding.Close(); |
863 m_paymentProvider.reset(); | 841 m_paymentProvider.reset(); |
864 } | 842 } |
865 | 843 |
866 } // namespace blink | 844 } // namespace blink |
OLD | NEW |