| 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 <stddef.h> | 7 #include <stddef.h> |
| 8 #include <utility> | 8 #include <utility> |
| 9 #include "bindings/core/v8/ExceptionState.h" | 9 #include "bindings/core/v8/ExceptionState.h" |
| 10 #include "bindings/core/v8/ScriptPromiseResolver.h" | 10 #include "bindings/core/v8/ScriptPromiseResolver.h" |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 129 namespace { | 129 namespace { |
| 130 | 130 |
| 131 // If the website does not call complete() 60 seconds after show() has been | 131 // If the website does not call complete() 60 seconds after show() has been |
| 132 // resolved, then behave as if the website called complete("fail"). | 132 // resolved, then behave as if the website called complete("fail"). |
| 133 static const int kCompleteTimeoutSeconds = 60; | 133 static const int kCompleteTimeoutSeconds = 60; |
| 134 | 134 |
| 135 // Validates ShippingOption or PaymentItem, which happen to have identical | 135 // Validates ShippingOption or PaymentItem, which happen to have identical |
| 136 // fields, except for "id", which is present only in ShippingOption. | 136 // fields, except for "id", which is present only in ShippingOption. |
| 137 template <typename T> | 137 template <typename T> |
| 138 void ValidateShippingOptionOrPaymentItem(const T& item, | 138 void ValidateShippingOptionOrPaymentItem(const T& item, |
| 139 const String& item_name, |
| 140 ExecutionContext& execution_context, |
| 139 ExceptionState& exception_state) { | 141 ExceptionState& exception_state) { |
| 140 if (!item.hasLabel() || item.label().IsEmpty()) { | 142 DCHECK(item.hasLabel()); |
| 141 exception_state.ThrowTypeError("Item label required"); | 143 DCHECK(item.hasAmount()); |
| 144 DCHECK(item.amount().hasValue()); |
| 145 DCHECK(item.amount().hasCurrency()); |
| 146 |
| 147 String error_message; |
| 148 if (!PaymentsValidators::IsValidAmountFormat(item.amount().value(), item_name, |
| 149 &error_message)) { |
| 150 exception_state.ThrowTypeError(error_message); |
| 142 return; | 151 return; |
| 143 } | 152 } |
| 144 | 153 |
| 145 if (!item.hasAmount()) { | 154 if (item.label().IsEmpty()) { |
| 146 exception_state.ThrowTypeError("Currency amount required"); | 155 execution_context.AddConsoleMessage(ConsoleMessage::Create( |
| 156 kJSMessageSource, kErrorMessageLevel, |
| 157 "Empty " + item_name + " label may be confusing the user")); |
| 147 return; | 158 return; |
| 148 } | 159 } |
| 149 | 160 |
| 150 if (!item.amount().hasCurrency()) { | |
| 151 exception_state.ThrowTypeError("Currency code required"); | |
| 152 return; | |
| 153 } | |
| 154 | |
| 155 if (!item.amount().hasValue()) { | |
| 156 exception_state.ThrowTypeError("Currency value required"); | |
| 157 return; | |
| 158 } | |
| 159 | |
| 160 String error_message; | |
| 161 if (!PaymentsValidators::IsValidCurrencyCodeFormat( | 161 if (!PaymentsValidators::IsValidCurrencyCodeFormat( |
| 162 item.amount().currency(), item.amount().currencySystem(), | 162 item.amount().currency(), item.amount().currencySystem(), |
| 163 &error_message)) { | 163 &error_message)) { |
| 164 exception_state.ThrowTypeError(error_message); | 164 exception_state.ThrowTypeError(error_message); |
| 165 return; | 165 return; |
| 166 } | 166 } |
| 167 | |
| 168 if (!PaymentsValidators::IsValidAmountFormat(item.amount().value(), | |
| 169 &error_message)) { | |
| 170 exception_state.ThrowTypeError(error_message); | |
| 171 return; | |
| 172 } | |
| 173 } | 167 } |
| 174 | 168 |
| 175 void ValidateAndConvertDisplayItems(const HeapVector<PaymentItem>& input, | 169 void ValidateAndConvertDisplayItems(const HeapVector<PaymentItem>& input, |
| 170 const String& item_names, |
| 176 Vector<PaymentItemPtr>& output, | 171 Vector<PaymentItemPtr>& output, |
| 172 ExecutionContext& execution_context, |
| 177 ExceptionState& exception_state) { | 173 ExceptionState& exception_state) { |
| 178 for (const PaymentItem& item : input) { | 174 for (const PaymentItem& item : input) { |
| 179 ValidateShippingOptionOrPaymentItem(item, exception_state); | 175 ValidateShippingOptionOrPaymentItem(item, item_names, execution_context, |
| 176 exception_state); |
| 180 if (exception_state.HadException()) | 177 if (exception_state.HadException()) |
| 181 return; | 178 return; |
| 182 output.push_back(payments::mojom::blink::PaymentItem::From(item)); | 179 output.push_back(payments::mojom::blink::PaymentItem::From(item)); |
| 183 } | 180 } |
| 184 } | 181 } |
| 185 | 182 |
| 186 // Validates and converts |input| shipping options into |output|. Throws an | 183 // Validates and converts |input| shipping options into |output|. Throws an |
| 187 // exception if the data is not valid, except for duplicate identifiers, which | 184 // exception if the data is not valid, except for duplicate identifiers, which |
| 188 // returns an empty |output| instead of throwing an exception. There's no need | 185 // returns an empty |output| instead of throwing an exception. There's no need |
| 189 // to clear |output| when an exception is thrown, because the caller takes care | 186 // to clear |output| when an exception is thrown, because the caller takes care |
| 190 // of deleting |output|. | 187 // of deleting |output|. |
| 191 void ValidateAndConvertShippingOptions( | 188 void ValidateAndConvertShippingOptions( |
| 192 const HeapVector<PaymentShippingOption>& input, | 189 const HeapVector<PaymentShippingOption>& input, |
| 193 Vector<PaymentShippingOptionPtr>& output, | 190 Vector<PaymentShippingOptionPtr>& output, |
| 191 String& shipping_option_output, |
| 194 ExecutionContext& execution_context, | 192 ExecutionContext& execution_context, |
| 195 ExceptionState& exception_state) { | 193 ExceptionState& exception_state) { |
| 196 HashSet<String> unique_ids; | 194 HashSet<String> unique_ids; |
| 197 for (const PaymentShippingOption& option : input) { | 195 for (const PaymentShippingOption& option : input) { |
| 198 if (!option.hasId() || option.id().IsEmpty()) { | 196 ValidateShippingOptionOrPaymentItem(option, "shippingOptions", |
| 199 exception_state.ThrowTypeError("ShippingOption id required"); | 197 execution_context, exception_state); |
| 198 if (exception_state.HadException()) |
| 199 return; |
| 200 |
| 201 DCHECK(option.hasId()); |
| 202 if (option.id().IsEmpty()) { |
| 203 execution_context.AddConsoleMessage(ConsoleMessage::Create( |
| 204 kJSMessageSource, kWarningMessageLevel, |
| 205 "Empty shipping option ID may be hard to debug")); |
| 200 return; | 206 return; |
| 201 } | 207 } |
| 202 | 208 |
| 203 if (unique_ids.Contains(option.id())) { | 209 if (unique_ids.Contains(option.id())) { |
| 204 execution_context.AddConsoleMessage(ConsoleMessage::Create( | 210 execution_context.AddConsoleMessage(ConsoleMessage::Create( |
| 205 kJSMessageSource, kWarningMessageLevel, | 211 kJSMessageSource, kWarningMessageLevel, |
| 206 "Duplicate shipping option identifier '" + option.id() + | 212 "Duplicate shipping option identifier '" + option.id() + |
| 207 "' is treated as an invalid address indicator.")); | 213 "' is treated as an invalid address indicator.")); |
| 208 // Clear |output| instead of throwing an exception. | 214 // Clear |output| instead of throwing an exception. |
| 209 output.clear(); | 215 output.clear(); |
| 216 shipping_option_output = String(); |
| 210 return; | 217 return; |
| 211 } | 218 } |
| 212 | 219 |
| 220 if (option.selected()) |
| 221 shipping_option_output = option.id(); |
| 222 |
| 213 unique_ids.insert(option.id()); | 223 unique_ids.insert(option.id()); |
| 214 | 224 |
| 215 ValidateShippingOptionOrPaymentItem(option, exception_state); | |
| 216 if (exception_state.HadException()) | |
| 217 return; | |
| 218 | |
| 219 output.push_back( | 225 output.push_back( |
| 220 payments::mojom::blink::PaymentShippingOption::From(option)); | 226 payments::mojom::blink::PaymentShippingOption::From(option)); |
| 221 } | 227 } |
| 222 } | 228 } |
| 223 | 229 |
| 224 void ValidateAndConvertTotal(const PaymentItem& input, | 230 void ValidateAndConvertTotal(const PaymentItem& input, |
| 231 const String& item_name, |
| 225 PaymentItemPtr& output, | 232 PaymentItemPtr& output, |
| 233 ExecutionContext& execution_context, |
| 226 ExceptionState& exception_state) { | 234 ExceptionState& exception_state) { |
| 227 ValidateShippingOptionOrPaymentItem(input, exception_state); | 235 ValidateShippingOptionOrPaymentItem(input, item_name, execution_context, |
| 236 exception_state); |
| 228 if (exception_state.HadException()) | 237 if (exception_state.HadException()) |
| 229 return; | 238 return; |
| 230 | 239 |
| 231 if (input.amount().value()[0] == '-') { | 240 if (input.amount().value()[0] == '-') { |
| 232 exception_state.ThrowTypeError("Total amount value should be non-negative"); | 241 exception_state.ThrowTypeError("Total amount value should be non-negative"); |
| 233 return; | 242 return; |
| 234 } | 243 } |
| 235 | 244 |
| 236 output = payments::mojom::blink::PaymentItem::From(input); | 245 output = payments::mojom::blink::PaymentItem::From(input); |
| 237 } | 246 } |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 391 } | 400 } |
| 392 } | 401 } |
| 393 | 402 |
| 394 void StringifyAndParseMethodSpecificData( | 403 void StringifyAndParseMethodSpecificData( |
| 395 const Vector<String>& supported_methods, | 404 const Vector<String>& supported_methods, |
| 396 const ScriptValue& input, | 405 const ScriptValue& input, |
| 397 PaymentMethodDataPtr& output, | 406 PaymentMethodDataPtr& output, |
| 398 ExecutionContext& execution_context, | 407 ExecutionContext& execution_context, |
| 399 ExceptionState& exception_state) { | 408 ExceptionState& exception_state) { |
| 400 DCHECK(!input.IsEmpty()); | 409 DCHECK(!input.IsEmpty()); |
| 401 if (!input.V8Value()->IsObject() || input.V8Value()->IsArray()) { | 410 v8::Local<v8::String> value; |
| 402 exception_state.ThrowTypeError("Data should be a JSON-serializable object"); | 411 if (!input.V8Value()->IsObject() || |
| 412 !v8::JSON::Stringify(input.GetContext(), input.V8Value().As<v8::Object>()) |
| 413 .ToLocal(&value)) { |
| 414 exception_state.ThrowTypeError( |
| 415 "Payment method data should be a JSON-serializable object"); |
| 403 return; | 416 return; |
| 404 } | 417 } |
| 405 | 418 |
| 406 v8::Local<v8::String> value; | |
| 407 if (!v8::JSON::Stringify(input.GetContext(), input.V8Value().As<v8::Object>()) | |
| 408 .ToLocal(&value)) { | |
| 409 exception_state.ThrowTypeError( | |
| 410 "Unable to parse payment method specific data"); | |
| 411 return; | |
| 412 } | |
| 413 | |
| 414 output->stringified_data = | 419 output->stringified_data = |
| 415 V8StringToWebCoreString<String>(value, kDoNotExternalize); | 420 V8StringToWebCoreString<String>(value, kDoNotExternalize); |
| 416 | 421 |
| 417 // Serialize payment method specific data to be sent to the payment apps. The | 422 // Serialize payment method specific data to be sent to the payment apps. The |
| 418 // payment apps are responsible for validating and processing their method | 423 // payment apps are responsible for validating and processing their method |
| 419 // data asynchronously. Do not throw exceptions here. | 424 // data asynchronously. Do not throw exceptions here. |
| 420 if (supported_methods.Contains("https://android.com/pay") || | 425 if (supported_methods.Contains("https://android.com/pay") || |
| 421 supported_methods.Contains("https://google.com/pay")) { | 426 supported_methods.Contains("https://google.com/pay")) { |
| 422 SetAndroidPayMethodData(input, output, exception_state); | 427 SetAndroidPayMethodData(input, output, exception_state); |
| 423 if (exception_state.HadException()) | 428 if (exception_state.HadException()) |
| 424 exception_state.ClearException(); | 429 exception_state.ClearException(); |
| 425 } | 430 } |
| 426 if (RuntimeEnabledFeatures::paymentRequestBasicCardEnabled() && | 431 if (RuntimeEnabledFeatures::paymentRequestBasicCardEnabled() && |
| 427 supported_methods.Contains("basic-card")) { | 432 supported_methods.Contains("basic-card")) { |
| 428 SetBasicCardMethodData(input, output, execution_context, exception_state); | 433 SetBasicCardMethodData(input, output, execution_context, exception_state); |
| 429 if (exception_state.HadException()) | 434 if (exception_state.HadException()) |
| 430 exception_state.ClearException(); | 435 exception_state.ClearException(); |
| 431 } | 436 } |
| 432 } | 437 } |
| 433 | 438 |
| 434 void ValidateAndConvertPaymentDetailsModifiers( | 439 void ValidateAndConvertPaymentDetailsModifiers( |
| 435 const HeapVector<PaymentDetailsModifier>& input, | 440 const HeapVector<PaymentDetailsModifier>& input, |
| 436 Vector<PaymentDetailsModifierPtr>& output, | 441 Vector<PaymentDetailsModifierPtr>& output, |
| 437 ExecutionContext& execution_context, | 442 ExecutionContext& execution_context, |
| 438 ExceptionState& exception_state) { | 443 ExceptionState& exception_state) { |
| 439 if (input.IsEmpty()) { | |
| 440 exception_state.ThrowTypeError( | |
| 441 "Must specify at least one payment details modifier"); | |
| 442 return; | |
| 443 } | |
| 444 | |
| 445 for (const PaymentDetailsModifier& modifier : input) { | 444 for (const PaymentDetailsModifier& modifier : input) { |
| 446 output.push_back(payments::mojom::blink::PaymentDetailsModifier::New()); | 445 output.push_back(payments::mojom::blink::PaymentDetailsModifier::New()); |
| 447 if (modifier.hasTotal()) { | 446 if (modifier.hasTotal()) { |
| 448 ValidateAndConvertTotal(modifier.total(), output.back()->total, | 447 ValidateAndConvertTotal(modifier.total(), "modifier total", |
| 448 output.back()->total, execution_context, |
| 449 exception_state); | 449 exception_state); |
| 450 if (exception_state.HadException()) | 450 if (exception_state.HadException()) |
| 451 return; | 451 return; |
| 452 } | 452 } |
| 453 | 453 |
| 454 if (modifier.hasAdditionalDisplayItems()) { | 454 if (modifier.hasAdditionalDisplayItems()) { |
| 455 ValidateAndConvertDisplayItems(modifier.additionalDisplayItems(), | 455 ValidateAndConvertDisplayItems(modifier.additionalDisplayItems(), |
| 456 "additional display items in modifier", |
| 456 output.back()->additional_display_items, | 457 output.back()->additional_display_items, |
| 457 exception_state); | 458 execution_context, exception_state); |
| 458 if (exception_state.HadException()) | 459 if (exception_state.HadException()) |
| 459 return; | 460 return; |
| 460 } | 461 } |
| 461 | 462 |
| 462 if (modifier.supportedMethods().IsEmpty()) { | 463 if (modifier.supportedMethods().IsEmpty()) { |
| 463 exception_state.ThrowTypeError( | 464 exception_state.ThrowTypeError( |
| 464 "Must specify at least one payment method identifier"); | 465 "Must specify at least one payment method identifier"); |
| 465 return; | 466 return; |
| 466 } | 467 } |
| 467 | 468 |
| 468 output.back()->method_data = | 469 output.back()->method_data = |
| 469 payments::mojom::blink::PaymentMethodData::New(); | 470 payments::mojom::blink::PaymentMethodData::New(); |
| 470 output.back()->method_data->supported_methods = modifier.supportedMethods(); | 471 output.back()->method_data->supported_methods = modifier.supportedMethods(); |
| 471 | 472 |
| 472 if (modifier.hasData() && !modifier.data().IsEmpty()) { | 473 if (modifier.hasData() && !modifier.data().IsEmpty()) { |
| 473 StringifyAndParseMethodSpecificData( | 474 StringifyAndParseMethodSpecificData( |
| 474 modifier.supportedMethods(), modifier.data(), | 475 modifier.supportedMethods(), modifier.data(), |
| 475 output.back()->method_data, execution_context, exception_state); | 476 output.back()->method_data, execution_context, exception_state); |
| 476 } else { | 477 } else { |
| 477 output.back()->method_data->stringified_data = ""; | 478 output.back()->method_data->stringified_data = ""; |
| 478 } | 479 } |
| 479 } | 480 } |
| 480 } | 481 } |
| 481 | 482 |
| 482 String GetSelectedShippingOption( | |
| 483 const Vector<PaymentShippingOptionPtr>& shipping_options) { | |
| 484 String result; | |
| 485 for (const PaymentShippingOptionPtr& shipping_option : shipping_options) { | |
| 486 if (shipping_option->selected) | |
| 487 result = shipping_option->id; | |
| 488 } | |
| 489 return result; | |
| 490 } | |
| 491 | |
| 492 void ValidateAndConvertPaymentDetailsBase(const PaymentDetailsBase& input, | 483 void ValidateAndConvertPaymentDetailsBase(const PaymentDetailsBase& input, |
| 493 bool request_shipping, | |
| 494 PaymentDetailsPtr& output, | 484 PaymentDetailsPtr& output, |
| 495 String& shipping_option_output, | 485 String& shipping_option_output, |
| 496 ExecutionContext& execution_context, | 486 ExecutionContext& execution_context, |
| 497 ExceptionState& exception_state) { | 487 ExceptionState& exception_state) { |
| 498 if (input.hasDisplayItems()) { | 488 if (input.hasDisplayItems()) { |
| 499 ValidateAndConvertDisplayItems(input.displayItems(), output->display_items, | 489 ValidateAndConvertDisplayItems(input.displayItems(), "display items", |
| 490 output->display_items, execution_context, |
| 500 exception_state); | 491 exception_state); |
| 501 if (exception_state.HadException()) | 492 if (exception_state.HadException()) |
| 502 return; | 493 return; |
| 503 } | 494 } |
| 504 | 495 |
| 505 if (input.hasShippingOptions() && request_shipping) { | 496 if (input.hasShippingOptions()) { |
| 506 ValidateAndConvertShippingOptions(input.shippingOptions(), | 497 ValidateAndConvertShippingOptions( |
| 507 output->shipping_options, | 498 input.shippingOptions(), output->shipping_options, |
| 508 execution_context, exception_state); | 499 shipping_option_output, execution_context, exception_state); |
| 509 if (exception_state.HadException()) | 500 if (exception_state.HadException()) |
| 510 return; | 501 return; |
| 502 } else { |
| 503 shipping_option_output = String(); |
| 511 } | 504 } |
| 512 | 505 |
| 513 shipping_option_output = GetSelectedShippingOption(output->shipping_options); | |
| 514 | |
| 515 if (input.hasModifiers()) { | 506 if (input.hasModifiers()) { |
| 516 ValidateAndConvertPaymentDetailsModifiers( | 507 ValidateAndConvertPaymentDetailsModifiers( |
| 517 input.modifiers(), output->modifiers, execution_context, | 508 input.modifiers(), output->modifiers, execution_context, |
| 518 exception_state); | 509 exception_state); |
| 519 if (exception_state.HadException()) | 510 if (exception_state.HadException()) |
| 520 return; | 511 return; |
| 521 } | 512 } |
| 522 } | 513 } |
| 523 | 514 |
| 524 void ValidateAndConvertPaymentDetailsInit(const PaymentDetailsInit& input, | 515 void ValidateAndConvertPaymentDetailsInit(const PaymentDetailsInit& input, |
| 525 bool request_shipping, | |
| 526 PaymentDetailsPtr& output, | 516 PaymentDetailsPtr& output, |
| 527 String& shipping_option_output, | 517 String& shipping_option_output, |
| 528 ExecutionContext& execution_context, | 518 ExecutionContext& execution_context, |
| 529 ExceptionState& exception_state) { | 519 ExceptionState& exception_state) { |
| 530 ValidateAndConvertPaymentDetailsBase(input, request_shipping, output, | 520 DCHECK(input.hasTotal()); |
| 531 shipping_option_output, | 521 ValidateAndConvertTotal(input.total(), "total", output->total, |
| 522 execution_context, exception_state); |
| 523 if (exception_state.HadException()) |
| 524 return; |
| 525 |
| 526 ValidateAndConvertPaymentDetailsBase(input, output, shipping_option_output, |
| 532 execution_context, exception_state); | 527 execution_context, exception_state); |
| 533 if (exception_state.HadException()) | 528 if (exception_state.HadException()) |
| 534 return; | 529 return; |
| 535 | |
| 536 if (!input.hasTotal()) { | |
| 537 exception_state.ThrowTypeError("Must specify total"); | |
| 538 return; | |
| 539 } | |
| 540 | |
| 541 if (input.hasId()) | |
| 542 output->id = input.id(); | |
| 543 else | |
| 544 output->id = CreateCanonicalUUIDString(); | |
| 545 | |
| 546 ValidateAndConvertTotal(input.total(), output->total, exception_state); | |
| 547 } | 530 } |
| 548 | 531 |
| 549 void ValidateAndConvertPaymentDetailsUpdate(const PaymentDetailsUpdate& input, | 532 void ValidateAndConvertPaymentDetailsUpdate(const PaymentDetailsUpdate& input, |
| 550 bool request_shipping, | |
| 551 PaymentDetailsPtr& output, | 533 PaymentDetailsPtr& output, |
| 552 String& shipping_option_output, | 534 String& shipping_option_output, |
| 553 ExecutionContext& execution_context, | 535 ExecutionContext& execution_context, |
| 554 ExceptionState& exception_state) { | 536 ExceptionState& exception_state) { |
| 555 ValidateAndConvertPaymentDetailsBase(input, request_shipping, output, | 537 ValidateAndConvertPaymentDetailsBase(input, output, shipping_option_output, |
| 556 shipping_option_output, | |
| 557 execution_context, exception_state); | 538 execution_context, exception_state); |
| 558 if (exception_state.HadException()) | 539 if (exception_state.HadException()) |
| 559 return; | 540 return; |
| 560 | 541 |
| 561 if (input.hasTotal()) { | 542 if (input.hasTotal()) { |
| 562 ValidateAndConvertTotal(input.total(), output->total, exception_state); | 543 ValidateAndConvertTotal(input.total(), "total", output->total, |
| 544 execution_context, exception_state); |
| 563 if (exception_state.HadException()) | 545 if (exception_state.HadException()) |
| 564 return; | 546 return; |
| 565 } | 547 } |
| 566 | 548 |
| 567 if (input.hasError() && !input.error().IsNull()) { | 549 if (input.hasError() && !input.error().IsNull()) { |
| 568 String error_message; | 550 String error_message; |
| 569 if (!PaymentsValidators::IsValidErrorMsgFormat(input.error(), | 551 if (!PaymentsValidators::IsValidErrorMsgFormat(input.error(), |
| 570 &error_message)) { | 552 &error_message)) { |
| 571 exception_state.ThrowTypeError(error_message); | 553 exception_state.ThrowTypeError(error_message); |
| 572 return; | 554 return; |
| 573 } | 555 } |
| 574 output->error = input.error(); | 556 output->error = input.error(); |
| 575 } else { | 557 } else { |
| 576 output->error = ""; | 558 output->error = ""; |
| 577 } | 559 } |
| 578 } | 560 } |
| 579 | 561 |
| 580 void ValidateAndConvertPaymentMethodData( | 562 void ValidateAndConvertPaymentMethodData( |
| 581 const HeapVector<PaymentMethodData>& input, | 563 const HeapVector<PaymentMethodData>& input, |
| 582 Vector<payments::mojom::blink::PaymentMethodDataPtr>& output, | 564 Vector<payments::mojom::blink::PaymentMethodDataPtr>& output, |
| 583 ExecutionContext& execution_context, | 565 ExecutionContext& execution_context, |
| 584 ExceptionState& exception_state) { | 566 ExceptionState& exception_state) { |
| 585 if (input.IsEmpty()) { | 567 if (input.IsEmpty()) { |
| 586 exception_state.ThrowTypeError( | 568 exception_state.ThrowTypeError("At least one payment method is required"); |
| 587 "Must specify at least one payment method identifier"); | |
| 588 return; | 569 return; |
| 589 } | 570 } |
| 590 | 571 |
| 591 for (const PaymentMethodData payment_method_data : input) { | 572 for (const PaymentMethodData payment_method_data : input) { |
| 592 if (payment_method_data.supportedMethods().IsEmpty()) { | 573 if (payment_method_data.supportedMethods().IsEmpty()) { |
| 593 exception_state.ThrowTypeError( | 574 exception_state.ThrowTypeError( |
| 594 "Must specify at least one payment method identifier"); | 575 "Each payment method needs to include at least one payment method " |
| 576 "identifier"); |
| 595 return; | 577 return; |
| 596 } | 578 } |
| 597 | 579 |
| 598 output.push_back(payments::mojom::blink::PaymentMethodData::New()); | 580 output.push_back(payments::mojom::blink::PaymentMethodData::New()); |
| 599 output.back()->supported_methods = payment_method_data.supportedMethods(); | 581 output.back()->supported_methods = payment_method_data.supportedMethods(); |
| 600 | 582 |
| 601 if (payment_method_data.hasData() && | 583 if (payment_method_data.hasData() && |
| 602 !payment_method_data.data().IsEmpty()) { | 584 !payment_method_data.data().IsEmpty()) { |
| 603 StringifyAndParseMethodSpecificData( | 585 StringifyAndParseMethodSpecificData( |
| 604 payment_method_data.supportedMethods(), payment_method_data.data(), | 586 payment_method_data.supportedMethods(), payment_method_data.data(), |
| 605 output.back(), execution_context, exception_state); | 587 output.back(), execution_context, exception_state); |
| 606 } else { | 588 } else { |
| 607 output.back()->stringified_data = ""; | 589 output.back()->stringified_data = ""; |
| 608 } | 590 } |
| 609 } | 591 } |
| 610 } | 592 } |
| 611 | 593 |
| 612 String GetValidShippingType(const String& shipping_type) { | |
| 613 static const char* const kValidValues[] = { | |
| 614 "shipping", "delivery", "pickup", | |
| 615 }; | |
| 616 for (size_t i = 0; i < arraysize(kValidValues); i++) { | |
| 617 if (shipping_type == kValidValues[i]) | |
| 618 return shipping_type; | |
| 619 } | |
| 620 return kValidValues[0]; | |
| 621 } | |
| 622 | |
| 623 bool AllowedToUsePaymentRequest(const Frame* frame) { | 594 bool AllowedToUsePaymentRequest(const Frame* frame) { |
| 624 // To determine whether a Document object |document| is allowed to use the | 595 // To determine whether a Document object |document| is allowed to use the |
| 625 // feature indicated by attribute name |allowpaymentrequest|, run these steps: | 596 // feature indicated by attribute name |allowpaymentrequest|, run these steps: |
| 626 | 597 |
| 627 // 1. If |document| has no browsing context, then return false. | 598 // 1. If |document| has no browsing context, then return false. |
| 628 if (!frame) | 599 if (!frame) |
| 629 return false; | 600 return false; |
| 630 | 601 |
| 631 if (!RuntimeEnabledFeatures::featurePolicyEnabled()) { | 602 if (!RuntimeEnabledFeatures::featurePolicyEnabled()) { |
| 632 // 2. If |document|'s browsing context is a top-level browsing context, then | 603 // 2. If |document|'s browsing context is a top-level browsing context, then |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 806 if (exception_state.HadException()) { | 777 if (exception_state.HadException()) { |
| 807 show_resolver_->Reject( | 778 show_resolver_->Reject( |
| 808 DOMException::Create(kSyntaxError, exception_state.Message())); | 779 DOMException::Create(kSyntaxError, exception_state.Message())); |
| 809 ClearResolversAndCloseMojoConnection(); | 780 ClearResolversAndCloseMojoConnection(); |
| 810 return; | 781 return; |
| 811 } | 782 } |
| 812 | 783 |
| 813 PaymentDetailsPtr validated_details = | 784 PaymentDetailsPtr validated_details = |
| 814 payments::mojom::blink::PaymentDetails::New(); | 785 payments::mojom::blink::PaymentDetails::New(); |
| 815 ValidateAndConvertPaymentDetailsUpdate( | 786 ValidateAndConvertPaymentDetailsUpdate( |
| 816 details, options_.requestShipping(), validated_details, shipping_option_, | 787 details, validated_details, shipping_option_, *GetExecutionContext(), |
| 817 *GetExecutionContext(), exception_state); | 788 exception_state); |
| 818 if (exception_state.HadException()) { | 789 if (exception_state.HadException()) { |
| 819 show_resolver_->Reject( | 790 show_resolver_->Reject( |
| 820 DOMException::Create(kSyntaxError, exception_state.Message())); | 791 DOMException::Create(kSyntaxError, exception_state.Message())); |
| 821 ClearResolversAndCloseMojoConnection(); | 792 ClearResolversAndCloseMojoConnection(); |
| 822 return; | 793 return; |
| 823 } | 794 } |
| 824 | 795 |
| 796 if (!options_.requestShipping()) |
| 797 validated_details->shipping_options.clear(); |
| 798 |
| 825 payment_provider_->UpdateWith(std::move(validated_details)); | 799 payment_provider_->UpdateWith(std::move(validated_details)); |
| 826 } | 800 } |
| 827 | 801 |
| 828 void PaymentRequest::OnUpdatePaymentDetailsFailure(const String& error) { | 802 void PaymentRequest::OnUpdatePaymentDetailsFailure(const String& error) { |
| 829 if (show_resolver_) | 803 if (show_resolver_) |
| 830 show_resolver_->Reject(DOMException::Create(kAbortError, error)); | 804 show_resolver_->Reject(DOMException::Create(kAbortError, error)); |
| 831 if (complete_resolver_) | 805 if (complete_resolver_) |
| 832 complete_resolver_->Reject(DOMException::Create(kAbortError, error)); | 806 complete_resolver_->Reject(DOMException::Create(kAbortError, error)); |
| 833 ClearResolversAndCloseMojoConnection(); | 807 ClearResolversAndCloseMojoConnection(); |
| 834 } | 808 } |
| (...skipping 19 matching lines...) Expand all Loading... |
| 854 const PaymentDetailsInit& details, | 828 const PaymentDetailsInit& details, |
| 855 const PaymentOptions& options, | 829 const PaymentOptions& options, |
| 856 ExceptionState& exception_state) | 830 ExceptionState& exception_state) |
| 857 : ContextLifecycleObserver(execution_context), | 831 : ContextLifecycleObserver(execution_context), |
| 858 options_(options), | 832 options_(options), |
| 859 client_binding_(this), | 833 client_binding_(this), |
| 860 complete_timer_( | 834 complete_timer_( |
| 861 TaskRunnerHelper::Get(TaskType::kMiscPlatformAPI, GetFrame()), | 835 TaskRunnerHelper::Get(TaskType::kMiscPlatformAPI, GetFrame()), |
| 862 this, | 836 this, |
| 863 &PaymentRequest::OnCompleteTimeout) { | 837 &PaymentRequest::OnCompleteTimeout) { |
| 864 Vector<payments::mojom::blink::PaymentMethodDataPtr> validated_method_data; | |
| 865 ValidateAndConvertPaymentMethodData(method_data, validated_method_data, | |
| 866 *GetExecutionContext(), exception_state); | |
| 867 if (exception_state.HadException()) | |
| 868 return; | |
| 869 | |
| 870 if (!GetExecutionContext()->IsSecureContext()) { | 838 if (!GetExecutionContext()->IsSecureContext()) { |
| 871 exception_state.ThrowSecurityError("Must be in a secure context"); | 839 exception_state.ThrowSecurityError("Must be in a secure context"); |
| 872 return; | 840 return; |
| 873 } | 841 } |
| 874 | 842 |
| 875 if (!AllowedToUsePaymentRequest(GetFrame())) { | 843 if (!AllowedToUsePaymentRequest(GetFrame())) { |
| 876 exception_state.ThrowSecurityError( | 844 exception_state.ThrowSecurityError( |
| 877 "Must be in a top-level browsing context or an iframe needs to specify " | 845 "Must be in a top-level browsing context or an iframe needs to specify " |
| 878 "'allowpaymentrequest' explicitly"); | 846 "'allowpaymentrequest' explicitly"); |
| 879 return; | 847 return; |
| 880 } | 848 } |
| 881 | 849 |
| 882 PaymentDetailsPtr validated_details = | 850 PaymentDetailsPtr validated_details = |
| 883 payments::mojom::blink::PaymentDetails::New(); | 851 payments::mojom::blink::PaymentDetails::New(); |
| 884 ValidateAndConvertPaymentDetailsInit(details, options_.requestShipping(), | 852 validated_details->id = id_ = |
| 885 validated_details, shipping_option_, | 853 details.hasId() ? details.id() : CreateCanonicalUUIDString(); |
| 886 *GetExecutionContext(), exception_state); | 854 |
| 855 Vector<payments::mojom::blink::PaymentMethodDataPtr> validated_method_data; |
| 856 ValidateAndConvertPaymentMethodData(method_data, validated_method_data, |
| 857 *GetExecutionContext(), exception_state); |
| 887 if (exception_state.HadException()) | 858 if (exception_state.HadException()) |
| 888 return; | 859 return; |
| 889 | 860 |
| 890 id_ = validated_details->id; | 861 ValidateAndConvertPaymentDetailsInit(details, validated_details, |
| 862 shipping_option_, *GetExecutionContext(), |
| 863 exception_state); |
| 864 if (exception_state.HadException()) |
| 865 return; |
| 891 | 866 |
| 892 if (options_.requestShipping()) | 867 if (options_.requestShipping()) |
| 893 shipping_type_ = GetValidShippingType(options_.shippingType()); | 868 shipping_type_ = options_.shippingType(); |
| 869 else |
| 870 validated_details->shipping_options.clear(); |
| 871 |
| 872 DCHECK(shipping_type_.IsNull() || shipping_type_ == "shipping" || |
| 873 shipping_type_ == "delivery" || shipping_type_ == "pickup"); |
| 894 | 874 |
| 895 GetFrame()->GetInterfaceProvider()->GetInterface( | 875 GetFrame()->GetInterfaceProvider()->GetInterface( |
| 896 mojo::MakeRequest(&payment_provider_)); | 876 mojo::MakeRequest(&payment_provider_)); |
| 897 payment_provider_.set_connection_error_handler(ConvertToBaseCallback( | 877 payment_provider_.set_connection_error_handler(ConvertToBaseCallback( |
| 898 WTF::Bind(&PaymentRequest::OnError, WrapWeakPersistent(this), | 878 WTF::Bind(&PaymentRequest::OnError, WrapWeakPersistent(this), |
| 899 PaymentErrorReason::UNKNOWN))); | 879 PaymentErrorReason::UNKNOWN))); |
| 900 payment_provider_->Init( | 880 payment_provider_->Init( |
| 901 client_binding_.CreateInterfacePtrAndBind(), | 881 client_binding_.CreateInterfacePtrAndBind(), |
| 902 std::move(validated_method_data), std::move(validated_details), | 882 std::move(validated_method_data), std::move(validated_details), |
| 903 payments::mojom::blink::PaymentOptions::From(options_)); | 883 payments::mojom::blink::PaymentOptions::From(options_)); |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1089 complete_resolver_.Clear(); | 1069 complete_resolver_.Clear(); |
| 1090 show_resolver_.Clear(); | 1070 show_resolver_.Clear(); |
| 1091 abort_resolver_.Clear(); | 1071 abort_resolver_.Clear(); |
| 1092 can_make_payment_resolver_.Clear(); | 1072 can_make_payment_resolver_.Clear(); |
| 1093 if (client_binding_.is_bound()) | 1073 if (client_binding_.is_bound()) |
| 1094 client_binding_.Close(); | 1074 client_binding_.Close(); |
| 1095 payment_provider_.reset(); | 1075 payment_provider_.reset(); |
| 1096 } | 1076 } |
| 1097 | 1077 |
| 1098 } // namespace blink | 1078 } // namespace blink |
| OLD | NEW |