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