Chromium Code Reviews| 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( |
| 156 ConsoleMessage::Create(kJSMessageSource, kErrorMessageLevel, | |
|
Marijn Kruisselbrink
2017/05/03 23:10:59
nit: would it make sense for this message to expla
please use gerrit instead
2017/05/04 15:17:15
Done.
| |
| 157 "Empty " + item_name + " label is discouraged")); | |
| 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( | |
| 204 ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel, | |
| 205 "Empty shipping option ID is discouraged")); | |
|
Marijn Kruisselbrink
2017/05/03 23:10:59
nit: same here, why is an empty ID a problem? If I
please use gerrit instead
2017/05/04 15:17:15
Done.
| |
| 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() || input.V8Value()->IsArray() || |
|
haraken
2017/05/04 09:47:51
Is it worth checking IsArray? To check whether the
please use gerrit instead
2017/05/04 15:17:15
Good point, the spec does prohibit arrays. Added a
| |
| 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 SetAndroidPayMethodData(input, output, exception_state); | 426 SetAndroidPayMethodData(input, output, exception_state); |
| 422 if (exception_state.HadException()) | 427 if (exception_state.HadException()) |
| 423 exception_state.ClearException(); | 428 exception_state.ClearException(); |
| 424 } | 429 } |
| 425 if (RuntimeEnabledFeatures::paymentRequestBasicCardEnabled() && | 430 if (RuntimeEnabledFeatures::paymentRequestBasicCardEnabled() && |
| 426 supported_methods.Contains("basic-card")) { | 431 supported_methods.Contains("basic-card")) { |
| 427 SetBasicCardMethodData(input, output, execution_context, exception_state); | 432 SetBasicCardMethodData(input, output, execution_context, exception_state); |
| 428 if (exception_state.HadException()) | 433 if (exception_state.HadException()) |
| 429 exception_state.ClearException(); | 434 exception_state.ClearException(); |
| 430 } | 435 } |
| 431 } | 436 } |
| 432 | 437 |
| 433 void ValidateAndConvertPaymentDetailsModifiers( | 438 void ValidateAndConvertPaymentDetailsModifiers( |
| 434 const HeapVector<PaymentDetailsModifier>& input, | 439 const HeapVector<PaymentDetailsModifier>& input, |
| 435 Vector<PaymentDetailsModifierPtr>& output, | 440 Vector<PaymentDetailsModifierPtr>& output, |
| 436 ExecutionContext& execution_context, | 441 ExecutionContext& execution_context, |
| 437 ExceptionState& exception_state) { | 442 ExceptionState& exception_state) { |
| 438 if (input.IsEmpty()) { | |
| 439 exception_state.ThrowTypeError( | |
| 440 "Must specify at least one payment details modifier"); | |
| 441 return; | |
| 442 } | |
| 443 | |
| 444 for (const PaymentDetailsModifier& modifier : input) { | 443 for (const PaymentDetailsModifier& modifier : input) { |
| 445 output.push_back(payments::mojom::blink::PaymentDetailsModifier::New()); | 444 output.push_back(payments::mojom::blink::PaymentDetailsModifier::New()); |
| 446 if (modifier.hasTotal()) { | 445 if (modifier.hasTotal()) { |
| 447 ValidateAndConvertTotal(modifier.total(), output.back()->total, | 446 ValidateAndConvertTotal(modifier.total(), "modifier total", |
| 447 output.back()->total, execution_context, | |
| 448 exception_state); | 448 exception_state); |
| 449 if (exception_state.HadException()) | 449 if (exception_state.HadException()) |
| 450 return; | 450 return; |
| 451 } | 451 } |
| 452 | 452 |
| 453 if (modifier.hasAdditionalDisplayItems()) { | 453 if (modifier.hasAdditionalDisplayItems()) { |
| 454 ValidateAndConvertDisplayItems(modifier.additionalDisplayItems(), | 454 ValidateAndConvertDisplayItems(modifier.additionalDisplayItems(), |
| 455 "additional display items in modifier", | |
| 455 output.back()->additional_display_items, | 456 output.back()->additional_display_items, |
| 456 exception_state); | 457 execution_context, exception_state); |
| 457 if (exception_state.HadException()) | 458 if (exception_state.HadException()) |
| 458 return; | 459 return; |
| 459 } | 460 } |
| 460 | 461 |
| 461 if (modifier.supportedMethods().IsEmpty()) { | 462 if (modifier.supportedMethods().IsEmpty()) { |
| 462 exception_state.ThrowTypeError( | 463 exception_state.ThrowTypeError( |
| 463 "Must specify at least one payment method identifier"); | 464 "Must specify at least one payment method identifier"); |
| 464 return; | 465 return; |
| 465 } | 466 } |
| 466 | 467 |
| 467 output.back()->method_data = | 468 output.back()->method_data = |
| 468 payments::mojom::blink::PaymentMethodData::New(); | 469 payments::mojom::blink::PaymentMethodData::New(); |
| 469 output.back()->method_data->supported_methods = modifier.supportedMethods(); | 470 output.back()->method_data->supported_methods = modifier.supportedMethods(); |
| 470 | 471 |
| 471 if (modifier.hasData() && !modifier.data().IsEmpty()) { | 472 if (modifier.hasData() && !modifier.data().IsEmpty()) { |
| 472 StringifyAndParseMethodSpecificData( | 473 StringifyAndParseMethodSpecificData( |
| 473 modifier.supportedMethods(), modifier.data(), | 474 modifier.supportedMethods(), modifier.data(), |
| 474 output.back()->method_data, execution_context, exception_state); | 475 output.back()->method_data, execution_context, exception_state); |
| 475 } else { | 476 } else { |
| 476 output.back()->method_data->stringified_data = ""; | 477 output.back()->method_data->stringified_data = ""; |
| 477 } | 478 } |
| 478 } | 479 } |
| 479 } | 480 } |
| 480 | 481 |
| 481 String GetSelectedShippingOption( | |
| 482 const Vector<PaymentShippingOptionPtr>& shipping_options) { | |
| 483 String result; | |
| 484 for (const PaymentShippingOptionPtr& shipping_option : shipping_options) { | |
| 485 if (shipping_option->selected) | |
| 486 result = shipping_option->id; | |
| 487 } | |
| 488 return result; | |
| 489 } | |
| 490 | |
| 491 void ValidateAndConvertPaymentDetailsBase(const PaymentDetailsBase& input, | 482 void ValidateAndConvertPaymentDetailsBase(const PaymentDetailsBase& input, |
| 492 bool request_shipping, | |
| 493 PaymentDetailsPtr& output, | 483 PaymentDetailsPtr& output, |
| 494 String& shipping_option_output, | 484 String& shipping_option_output, |
| 495 ExecutionContext& execution_context, | 485 ExecutionContext& execution_context, |
| 496 ExceptionState& exception_state) { | 486 ExceptionState& exception_state) { |
| 497 if (input.hasDisplayItems()) { | 487 if (input.hasDisplayItems()) { |
| 498 ValidateAndConvertDisplayItems(input.displayItems(), output->display_items, | 488 ValidateAndConvertDisplayItems(input.displayItems(), "display items", |
| 489 output->display_items, execution_context, | |
| 499 exception_state); | 490 exception_state); |
| 500 if (exception_state.HadException()) | 491 if (exception_state.HadException()) |
| 501 return; | 492 return; |
| 502 } | 493 } |
| 503 | 494 |
| 504 if (input.hasShippingOptions() && request_shipping) { | 495 if (input.hasShippingOptions()) { |
| 505 ValidateAndConvertShippingOptions(input.shippingOptions(), | 496 ValidateAndConvertShippingOptions( |
| 506 output->shipping_options, | 497 input.shippingOptions(), output->shipping_options, |
| 507 execution_context, exception_state); | 498 shipping_option_output, execution_context, exception_state); |
| 508 if (exception_state.HadException()) | 499 if (exception_state.HadException()) |
| 509 return; | 500 return; |
| 501 } else { | |
| 502 shipping_option_output = String(); | |
| 510 } | 503 } |
| 511 | 504 |
| 512 shipping_option_output = GetSelectedShippingOption(output->shipping_options); | |
| 513 | |
| 514 if (input.hasModifiers()) { | 505 if (input.hasModifiers()) { |
| 515 ValidateAndConvertPaymentDetailsModifiers( | 506 ValidateAndConvertPaymentDetailsModifiers( |
| 516 input.modifiers(), output->modifiers, execution_context, | 507 input.modifiers(), output->modifiers, execution_context, |
| 517 exception_state); | 508 exception_state); |
| 518 if (exception_state.HadException()) | 509 if (exception_state.HadException()) |
| 519 return; | 510 return; |
| 520 } | 511 } |
| 521 } | 512 } |
| 522 | 513 |
| 523 void ValidateAndConvertPaymentDetailsInit(const PaymentDetailsInit& input, | 514 void ValidateAndConvertPaymentDetailsInit(const PaymentDetailsInit& input, |
| 524 bool request_shipping, | |
| 525 PaymentDetailsPtr& output, | 515 PaymentDetailsPtr& output, |
| 526 String& shipping_option_output, | 516 String& shipping_option_output, |
| 527 ExecutionContext& execution_context, | 517 ExecutionContext& execution_context, |
| 528 ExceptionState& exception_state) { | 518 ExceptionState& exception_state) { |
| 529 ValidateAndConvertPaymentDetailsBase(input, request_shipping, output, | 519 DCHECK(input.hasTotal()); |
| 530 shipping_option_output, | 520 ValidateAndConvertTotal(input.total(), "total", output->total, |
| 521 execution_context, exception_state); | |
| 522 if (exception_state.HadException()) | |
| 523 return; | |
| 524 | |
| 525 ValidateAndConvertPaymentDetailsBase(input, output, shipping_option_output, | |
| 531 execution_context, exception_state); | 526 execution_context, exception_state); |
| 532 if (exception_state.HadException()) | 527 if (exception_state.HadException()) |
| 533 return; | 528 return; |
| 534 | |
| 535 if (!input.hasTotal()) { | |
| 536 exception_state.ThrowTypeError("Must specify total"); | |
| 537 return; | |
| 538 } | |
| 539 | |
| 540 if (input.hasId()) | |
| 541 output->id = input.id(); | |
| 542 else | |
| 543 output->id = CreateCanonicalUUIDString(); | |
| 544 | |
| 545 ValidateAndConvertTotal(input.total(), output->total, exception_state); | |
| 546 } | 529 } |
| 547 | 530 |
| 548 void ValidateAndConvertPaymentDetailsUpdate(const PaymentDetailsUpdate& input, | 531 void ValidateAndConvertPaymentDetailsUpdate(const PaymentDetailsUpdate& input, |
| 549 bool request_shipping, | |
| 550 PaymentDetailsPtr& output, | 532 PaymentDetailsPtr& output, |
| 551 String& shipping_option_output, | 533 String& shipping_option_output, |
| 552 ExecutionContext& execution_context, | 534 ExecutionContext& execution_context, |
| 553 ExceptionState& exception_state) { | 535 ExceptionState& exception_state) { |
| 554 ValidateAndConvertPaymentDetailsBase(input, request_shipping, output, | 536 ValidateAndConvertPaymentDetailsBase(input, output, shipping_option_output, |
| 555 shipping_option_output, | |
| 556 execution_context, exception_state); | 537 execution_context, exception_state); |
| 557 if (exception_state.HadException()) | 538 if (exception_state.HadException()) |
| 558 return; | 539 return; |
| 559 | 540 |
| 560 if (input.hasTotal()) { | 541 if (input.hasTotal()) { |
| 561 ValidateAndConvertTotal(input.total(), output->total, exception_state); | 542 ValidateAndConvertTotal(input.total(), "total", output->total, |
| 543 execution_context, exception_state); | |
| 562 if (exception_state.HadException()) | 544 if (exception_state.HadException()) |
| 563 return; | 545 return; |
| 564 } | 546 } |
| 565 | 547 |
| 566 if (input.hasError() && !input.error().IsNull()) { | 548 if (input.hasError() && !input.error().IsNull()) { |
| 567 String error_message; | 549 String error_message; |
| 568 if (!PaymentsValidators::IsValidErrorMsgFormat(input.error(), | 550 if (!PaymentsValidators::IsValidErrorMsgFormat(input.error(), |
| 569 &error_message)) { | 551 &error_message)) { |
| 570 exception_state.ThrowTypeError(error_message); | 552 exception_state.ThrowTypeError(error_message); |
| 571 return; | 553 return; |
| 572 } | 554 } |
| 573 output->error = input.error(); | 555 output->error = input.error(); |
| 574 } else { | 556 } else { |
| 575 output->error = ""; | 557 output->error = ""; |
| 576 } | 558 } |
| 577 } | 559 } |
| 578 | 560 |
| 579 void ValidateAndConvertPaymentMethodData( | 561 void ValidateAndConvertPaymentMethodData( |
| 580 const HeapVector<PaymentMethodData>& input, | 562 const HeapVector<PaymentMethodData>& input, |
| 581 Vector<payments::mojom::blink::PaymentMethodDataPtr>& output, | 563 Vector<payments::mojom::blink::PaymentMethodDataPtr>& output, |
| 582 ExecutionContext& execution_context, | 564 ExecutionContext& execution_context, |
| 583 ExceptionState& exception_state) { | 565 ExceptionState& exception_state) { |
| 584 if (input.IsEmpty()) { | 566 if (input.IsEmpty()) { |
| 585 exception_state.ThrowTypeError( | 567 exception_state.ThrowTypeError("At least one payment method is required"); |
| 586 "Must specify at least one payment method identifier"); | |
| 587 return; | 568 return; |
| 588 } | 569 } |
| 589 | 570 |
| 590 for (const PaymentMethodData payment_method_data : input) { | 571 for (const PaymentMethodData payment_method_data : input) { |
| 591 if (payment_method_data.supportedMethods().IsEmpty()) { | 572 if (payment_method_data.supportedMethods().IsEmpty()) { |
| 592 exception_state.ThrowTypeError( | 573 exception_state.ThrowTypeError( |
| 593 "Must specify at least one payment method identifier"); | 574 "Each payment method needs to include at least one payment method " |
| 575 "identifier"); | |
| 594 return; | 576 return; |
| 595 } | 577 } |
| 596 | 578 |
| 597 output.push_back(payments::mojom::blink::PaymentMethodData::New()); | 579 output.push_back(payments::mojom::blink::PaymentMethodData::New()); |
| 598 output.back()->supported_methods = payment_method_data.supportedMethods(); | 580 output.back()->supported_methods = payment_method_data.supportedMethods(); |
| 599 | 581 |
| 600 if (payment_method_data.hasData() && | 582 if (payment_method_data.hasData() && |
| 601 !payment_method_data.data().IsEmpty()) { | 583 !payment_method_data.data().IsEmpty()) { |
| 602 StringifyAndParseMethodSpecificData( | 584 StringifyAndParseMethodSpecificData( |
| 603 payment_method_data.supportedMethods(), payment_method_data.data(), | 585 payment_method_data.supportedMethods(), payment_method_data.data(), |
| 604 output.back(), execution_context, exception_state); | 586 output.back(), execution_context, exception_state); |
| 605 } else { | 587 } else { |
| 606 output.back()->stringified_data = ""; | 588 output.back()->stringified_data = ""; |
| 607 } | 589 } |
| 608 } | 590 } |
| 609 } | 591 } |
| 610 | 592 |
| 611 String GetValidShippingType(const String& shipping_type) { | |
| 612 static const char* const kValidValues[] = { | |
| 613 "shipping", "delivery", "pickup", | |
| 614 }; | |
| 615 for (size_t i = 0; i < arraysize(kValidValues); i++) { | |
| 616 if (shipping_type == kValidValues[i]) | |
| 617 return shipping_type; | |
| 618 } | |
| 619 return kValidValues[0]; | |
| 620 } | |
| 621 | |
| 622 bool AllowedToUsePaymentRequest(const Frame* frame) { | 593 bool AllowedToUsePaymentRequest(const Frame* frame) { |
| 623 // To determine whether a Document object |document| is allowed to use the | 594 // To determine whether a Document object |document| is allowed to use the |
| 624 // feature indicated by attribute name |allowpaymentrequest|, run these steps: | 595 // feature indicated by attribute name |allowpaymentrequest|, run these steps: |
| 625 | 596 |
| 626 // 1. If |document| has no browsing context, then return false. | 597 // 1. If |document| has no browsing context, then return false. |
| 627 if (!frame) | 598 if (!frame) |
| 628 return false; | 599 return false; |
| 629 | 600 |
| 630 if (!RuntimeEnabledFeatures::featurePolicyEnabled()) { | 601 if (!RuntimeEnabledFeatures::featurePolicyEnabled()) { |
| 631 // 2. If |document|'s browsing context is a top-level browsing context, then | 602 // 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... | |
| 805 if (exception_state.HadException()) { | 776 if (exception_state.HadException()) { |
| 806 show_resolver_->Reject( | 777 show_resolver_->Reject( |
| 807 DOMException::Create(kSyntaxError, exception_state.Message())); | 778 DOMException::Create(kSyntaxError, exception_state.Message())); |
| 808 ClearResolversAndCloseMojoConnection(); | 779 ClearResolversAndCloseMojoConnection(); |
| 809 return; | 780 return; |
| 810 } | 781 } |
| 811 | 782 |
| 812 PaymentDetailsPtr validated_details = | 783 PaymentDetailsPtr validated_details = |
| 813 payments::mojom::blink::PaymentDetails::New(); | 784 payments::mojom::blink::PaymentDetails::New(); |
| 814 ValidateAndConvertPaymentDetailsUpdate( | 785 ValidateAndConvertPaymentDetailsUpdate( |
| 815 details, options_.requestShipping(), validated_details, shipping_option_, | 786 details, validated_details, shipping_option_, *GetExecutionContext(), |
| 816 *GetExecutionContext(), exception_state); | 787 exception_state); |
| 817 if (exception_state.HadException()) { | 788 if (exception_state.HadException()) { |
| 818 show_resolver_->Reject( | 789 show_resolver_->Reject( |
| 819 DOMException::Create(kSyntaxError, exception_state.Message())); | 790 DOMException::Create(kSyntaxError, exception_state.Message())); |
| 820 ClearResolversAndCloseMojoConnection(); | 791 ClearResolversAndCloseMojoConnection(); |
| 821 return; | 792 return; |
| 822 } | 793 } |
| 823 | 794 |
| 795 if (!options_.requestShipping()) | |
| 796 validated_details->shipping_options.clear(); | |
| 797 | |
| 824 payment_provider_->UpdateWith(std::move(validated_details)); | 798 payment_provider_->UpdateWith(std::move(validated_details)); |
| 825 } | 799 } |
| 826 | 800 |
| 827 void PaymentRequest::OnUpdatePaymentDetailsFailure(const String& error) { | 801 void PaymentRequest::OnUpdatePaymentDetailsFailure(const String& error) { |
| 828 if (show_resolver_) | 802 if (show_resolver_) |
| 829 show_resolver_->Reject(DOMException::Create(kAbortError, error)); | 803 show_resolver_->Reject(DOMException::Create(kAbortError, error)); |
| 830 if (complete_resolver_) | 804 if (complete_resolver_) |
| 831 complete_resolver_->Reject(DOMException::Create(kAbortError, error)); | 805 complete_resolver_->Reject(DOMException::Create(kAbortError, error)); |
| 832 ClearResolversAndCloseMojoConnection(); | 806 ClearResolversAndCloseMojoConnection(); |
| 833 } | 807 } |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 853 const PaymentDetailsInit& details, | 827 const PaymentDetailsInit& details, |
| 854 const PaymentOptions& options, | 828 const PaymentOptions& options, |
| 855 ExceptionState& exception_state) | 829 ExceptionState& exception_state) |
| 856 : ContextLifecycleObserver(execution_context), | 830 : ContextLifecycleObserver(execution_context), |
| 857 options_(options), | 831 options_(options), |
| 858 client_binding_(this), | 832 client_binding_(this), |
| 859 complete_timer_( | 833 complete_timer_( |
| 860 TaskRunnerHelper::Get(TaskType::kMiscPlatformAPI, GetFrame()), | 834 TaskRunnerHelper::Get(TaskType::kMiscPlatformAPI, GetFrame()), |
| 861 this, | 835 this, |
| 862 &PaymentRequest::OnCompleteTimeout) { | 836 &PaymentRequest::OnCompleteTimeout) { |
| 863 Vector<payments::mojom::blink::PaymentMethodDataPtr> validated_method_data; | |
| 864 ValidateAndConvertPaymentMethodData(method_data, validated_method_data, | |
| 865 *GetExecutionContext(), exception_state); | |
| 866 if (exception_state.HadException()) | |
| 867 return; | |
| 868 | |
| 869 if (!GetExecutionContext()->IsSecureContext()) { | 837 if (!GetExecutionContext()->IsSecureContext()) { |
| 870 exception_state.ThrowSecurityError("Must be in a secure context"); | 838 exception_state.ThrowSecurityError("Must be in a secure context"); |
| 871 return; | 839 return; |
| 872 } | 840 } |
| 873 | 841 |
| 874 if (!AllowedToUsePaymentRequest(GetFrame())) { | 842 if (!AllowedToUsePaymentRequest(GetFrame())) { |
| 875 exception_state.ThrowSecurityError( | 843 exception_state.ThrowSecurityError( |
| 876 "Must be in a top-level browsing context or an iframe needs to specify " | 844 "Must be in a top-level browsing context or an iframe needs to specify " |
| 877 "'allowpaymentrequest' explicitly"); | 845 "'allowpaymentrequest' explicitly"); |
| 878 return; | 846 return; |
| 879 } | 847 } |
| 880 | 848 |
| 881 PaymentDetailsPtr validated_details = | 849 PaymentDetailsPtr validated_details = |
| 882 payments::mojom::blink::PaymentDetails::New(); | 850 payments::mojom::blink::PaymentDetails::New(); |
| 883 ValidateAndConvertPaymentDetailsInit(details, options_.requestShipping(), | 851 validated_details->id = id_ = |
| 884 validated_details, shipping_option_, | 852 details.hasId() ? details.id() : CreateCanonicalUUIDString(); |
| 885 *GetExecutionContext(), exception_state); | 853 |
| 854 Vector<payments::mojom::blink::PaymentMethodDataPtr> validated_method_data; | |
| 855 ValidateAndConvertPaymentMethodData(method_data, validated_method_data, | |
| 856 *GetExecutionContext(), exception_state); | |
| 886 if (exception_state.HadException()) | 857 if (exception_state.HadException()) |
| 887 return; | 858 return; |
| 888 | 859 |
| 889 id_ = validated_details->id; | 860 ValidateAndConvertPaymentDetailsInit(details, validated_details, |
| 861 shipping_option_, *GetExecutionContext(), | |
| 862 exception_state); | |
| 863 if (exception_state.HadException()) | |
| 864 return; | |
| 890 | 865 |
| 891 if (options_.requestShipping()) | 866 if (options_.requestShipping()) |
| 892 shipping_type_ = GetValidShippingType(options_.shippingType()); | 867 shipping_type_ = options_.shippingType(); |
| 868 else | |
| 869 validated_details->shipping_options.clear(); | |
| 870 | |
| 871 DCHECK(shipping_type_.IsNull() || shipping_type_ == "shipping" || | |
| 872 shipping_type_ == "delivery" || shipping_type_ == "pickup"); | |
| 893 | 873 |
| 894 GetFrame()->GetInterfaceProvider()->GetInterface( | 874 GetFrame()->GetInterfaceProvider()->GetInterface( |
| 895 mojo::MakeRequest(&payment_provider_)); | 875 mojo::MakeRequest(&payment_provider_)); |
| 896 payment_provider_.set_connection_error_handler(ConvertToBaseCallback( | 876 payment_provider_.set_connection_error_handler(ConvertToBaseCallback( |
| 897 WTF::Bind(&PaymentRequest::OnError, WrapWeakPersistent(this), | 877 WTF::Bind(&PaymentRequest::OnError, WrapWeakPersistent(this), |
| 898 PaymentErrorReason::UNKNOWN))); | 878 PaymentErrorReason::UNKNOWN))); |
| 899 payment_provider_->Init( | 879 payment_provider_->Init( |
| 900 client_binding_.CreateInterfacePtrAndBind(), | 880 client_binding_.CreateInterfacePtrAndBind(), |
| 901 std::move(validated_method_data), std::move(validated_details), | 881 std::move(validated_method_data), std::move(validated_details), |
| 902 payments::mojom::blink::PaymentOptions::From(options_)); | 882 payments::mojom::blink::PaymentOptions::From(options_)); |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1088 complete_resolver_.Clear(); | 1068 complete_resolver_.Clear(); |
| 1089 show_resolver_.Clear(); | 1069 show_resolver_.Clear(); |
| 1090 abort_resolver_.Clear(); | 1070 abort_resolver_.Clear(); |
| 1091 can_make_payment_resolver_.Clear(); | 1071 can_make_payment_resolver_.Clear(); |
| 1092 if (client_binding_.is_bound()) | 1072 if (client_binding_.is_bound()) |
| 1093 client_binding_.Close(); | 1073 client_binding_.Close(); |
| 1094 payment_provider_.reset(); | 1074 payment_provider_.reset(); |
| 1095 } | 1075 } |
| 1096 | 1076 |
| 1097 } // namespace blink | 1077 } // namespace blink |
| OLD | NEW |