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 |