| 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 |