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 "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 19 matching lines...) Expand all Loading... | |
| 30 #include "public/platform/InterfaceProvider.h" | 30 #include "public/platform/InterfaceProvider.h" |
| 31 #include "public/platform/Platform.h" | 31 #include "public/platform/Platform.h" |
| 32 #include "public/platform/WebTraceLocation.h" | 32 #include "public/platform/WebTraceLocation.h" |
| 33 #include "wtf/HashSet.h" | 33 #include "wtf/HashSet.h" |
| 34 #include <utility> | 34 #include <utility> |
| 35 | 35 |
| 36 namespace mojo { | 36 namespace mojo { |
| 37 | 37 |
| 38 using blink::mojom::blink::PaymentCurrencyAmount; | 38 using blink::mojom::blink::PaymentCurrencyAmount; |
| 39 using blink::mojom::blink::PaymentCurrencyAmountPtr; | 39 using blink::mojom::blink::PaymentCurrencyAmountPtr; |
| 40 using blink::mojom::blink::PaymentDetails; | |
| 41 using blink::mojom::blink::PaymentDetailsModifier; | |
| 42 using blink::mojom::blink::PaymentDetailsModifierPtr; | |
| 43 using blink::mojom::blink::PaymentDetailsPtr; | |
| 44 using blink::mojom::blink::PaymentErrorReason; | |
| 45 using blink::mojom::blink::PaymentItem; | 40 using blink::mojom::blink::PaymentItem; |
| 46 using blink::mojom::blink::PaymentItemPtr; | 41 using blink::mojom::blink::PaymentItemPtr; |
| 47 using blink::mojom::blink::PaymentMethodData; | |
| 48 using blink::mojom::blink::PaymentMethodDataPtr; | |
| 49 using blink::mojom::blink::PaymentOptions; | 42 using blink::mojom::blink::PaymentOptions; |
| 50 using blink::mojom::blink::PaymentOptionsPtr; | 43 using blink::mojom::blink::PaymentOptionsPtr; |
| 51 using blink::mojom::blink::PaymentShippingOption; | 44 using blink::mojom::blink::PaymentShippingOption; |
| 52 using blink::mojom::blink::PaymentShippingOptionPtr; | 45 using blink::mojom::blink::PaymentShippingOptionPtr; |
| 53 using blink::mojom::blink::PaymentShippingType; | 46 using blink::mojom::blink::PaymentShippingType; |
| 54 | 47 |
| 55 template <> | 48 template <> |
| 56 struct TypeConverter<PaymentCurrencyAmountPtr, blink::PaymentCurrencyAmount> { | 49 struct TypeConverter<PaymentCurrencyAmountPtr, blink::PaymentCurrencyAmount> { |
| 57 static PaymentCurrencyAmountPtr Convert( | 50 static PaymentCurrencyAmountPtr Convert( |
| 58 const blink::PaymentCurrencyAmount& input) { | 51 const blink::PaymentCurrencyAmount& input) { |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 81 PaymentShippingOptionPtr output = PaymentShippingOption::New(); | 74 PaymentShippingOptionPtr output = PaymentShippingOption::New(); |
| 82 output->id = input.id(); | 75 output->id = input.id(); |
| 83 output->label = input.label(); | 76 output->label = input.label(); |
| 84 output->amount = PaymentCurrencyAmount::From(input.amount()); | 77 output->amount = PaymentCurrencyAmount::From(input.amount()); |
| 85 output->selected = input.hasSelected() && input.selected(); | 78 output->selected = input.hasSelected() && input.selected(); |
| 86 return output; | 79 return output; |
| 87 } | 80 } |
| 88 }; | 81 }; |
| 89 | 82 |
| 90 template <> | 83 template <> |
| 91 struct TypeConverter<PaymentDetailsModifierPtr, blink::PaymentDetailsModifier> { | |
| 92 static PaymentDetailsModifierPtr Convert( | |
| 93 const blink::PaymentDetailsModifier& input) { | |
| 94 PaymentDetailsModifierPtr output = PaymentDetailsModifier::New(); | |
| 95 output->supported_methods = | |
| 96 WTF::Vector<WTF::String>(input.supportedMethods()); | |
| 97 | |
| 98 if (input.hasTotal()) | |
| 99 output->total = PaymentItem::From(input.total()); | |
| 100 | |
| 101 if (input.hasAdditionalDisplayItems()) { | |
| 102 for (size_t i = 0; i < input.additionalDisplayItems().size(); ++i) { | |
| 103 output->additional_display_items.append( | |
| 104 PaymentItem::From(input.additionalDisplayItems()[i])); | |
| 105 } | |
| 106 } | |
| 107 return output; | |
| 108 } | |
| 109 }; | |
| 110 | |
| 111 template <> | |
| 112 struct TypeConverter<PaymentDetailsPtr, blink::PaymentDetails> { | |
| 113 static PaymentDetailsPtr Convert(const blink::PaymentDetails& input) { | |
| 114 PaymentDetailsPtr output = PaymentDetails::New(); | |
| 115 output->total = PaymentItem::From(input.total()); | |
| 116 | |
| 117 if (input.hasDisplayItems()) { | |
| 118 for (size_t i = 0; i < input.displayItems().size(); ++i) { | |
| 119 output->display_items.append( | |
| 120 PaymentItem::From(input.displayItems()[i])); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 if (input.hasShippingOptions()) { | |
| 125 for (size_t i = 0; i < input.shippingOptions().size(); ++i) { | |
| 126 output->shipping_options.append( | |
| 127 PaymentShippingOption::From(input.shippingOptions()[i])); | |
| 128 } | |
| 129 } | |
| 130 | |
| 131 if (input.hasModifiers()) { | |
| 132 for (size_t i = 0; i < input.modifiers().size(); ++i) { | |
| 133 output->modifiers.append( | |
| 134 PaymentDetailsModifier::From(input.modifiers()[i])); | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 if (input.hasError()) | |
| 139 output->error = input.error(); | |
| 140 else | |
| 141 output->error = WTF::emptyString(); | |
| 142 | |
| 143 return output; | |
| 144 } | |
| 145 }; | |
| 146 | |
| 147 template <> | |
| 148 struct TypeConverter<PaymentOptionsPtr, blink::PaymentOptions> { | 84 struct TypeConverter<PaymentOptionsPtr, blink::PaymentOptions> { |
| 149 static PaymentOptionsPtr Convert(const blink::PaymentOptions& input) { | 85 static PaymentOptionsPtr Convert(const blink::PaymentOptions& input) { |
| 150 PaymentOptionsPtr output = PaymentOptions::New(); | 86 PaymentOptionsPtr output = PaymentOptions::New(); |
| 151 output->request_payer_name = input.requestPayerName(); | 87 output->request_payer_name = input.requestPayerName(); |
| 152 output->request_payer_email = input.requestPayerEmail(); | 88 output->request_payer_email = input.requestPayerEmail(); |
| 153 output->request_payer_phone = input.requestPayerPhone(); | 89 output->request_payer_phone = input.requestPayerPhone(); |
| 154 output->request_shipping = input.requestShipping(); | 90 output->request_shipping = input.requestShipping(); |
| 155 | 91 |
| 156 if (input.shippingType() == "delivery") | 92 if (input.shippingType() == "delivery") |
| 157 output->shipping_type = PaymentShippingType::DELIVERY; | 93 output->shipping_type = PaymentShippingType::DELIVERY; |
| 158 else if (input.shippingType() == "pickup") | 94 else if (input.shippingType() == "pickup") |
| 159 output->shipping_type = PaymentShippingType::PICKUP; | 95 output->shipping_type = PaymentShippingType::PICKUP; |
| 160 else | 96 else |
| 161 output->shipping_type = PaymentShippingType::SHIPPING; | 97 output->shipping_type = PaymentShippingType::SHIPPING; |
| 162 | 98 |
| 163 return output; | 99 return output; |
| 164 } | 100 } |
| 165 }; | 101 }; |
| 166 | 102 |
| 167 template <> | |
| 168 struct TypeConverter<WTFArray<PaymentMethodDataPtr>, | |
| 169 WTF::Vector<blink::PaymentRequest::MethodData>> { | |
| 170 static WTFArray<PaymentMethodDataPtr> Convert( | |
| 171 const WTF::Vector<blink::PaymentRequest::MethodData>& input) { | |
| 172 WTFArray<PaymentMethodDataPtr> output(input.size()); | |
| 173 for (size_t i = 0; i < input.size(); ++i) { | |
| 174 output[i] = PaymentMethodData::New(); | |
| 175 output[i]->supported_methods = | |
| 176 WTF::Vector<WTF::String>(input[i].supportedMethods); | |
| 177 output[i]->stringified_data = input[i].stringifiedData; | |
| 178 } | |
| 179 return output; | |
| 180 } | |
| 181 }; | |
| 182 | |
| 183 } // namespace mojo | 103 } // namespace mojo |
| 184 | 104 |
| 185 namespace blink { | 105 namespace blink { |
| 186 namespace { | 106 namespace { |
| 187 | 107 |
| 188 // If the website does not call complete() 60 seconds after show() has been | 108 // If the website does not call complete() 60 seconds after show() has been |
| 189 // resolved, then behave as if the website called complete("fail"). | 109 // resolved, then behave as if the website called complete("fail"). |
| 190 static const int completeTimeoutSeconds = 60; | 110 static const int completeTimeoutSeconds = 60; |
| 191 | 111 |
| 192 // Validates ShippingOption or PaymentItem, which happen to have identical | 112 // Validates ShippingOption or PaymentItem, which happen to have identical |
| 193 // fields, except for "id", which is present only in ShippingOption. | 113 // fields, except for "id", which is present only in ShippingOption. |
| 194 template <typename T> | 114 template <typename T> |
| 195 void validateShippingOptionOrPaymentItem(const T& item, | 115 void validateShippingOptionOrPaymentItem(const T& item, |
|
Mathieu
2016/11/01 14:39:16
curious: are you worried that the exception is goi
please use gerrit instead
2016/11/02 16:27:09
Improving these error messages are a nice to have,
| |
| 196 ExceptionState& exceptionState) { | 116 ExceptionState& exceptionState) { |
| 197 if (!item.hasLabel() || item.label().isEmpty()) { | 117 if (!item.hasLabel() || item.label().isEmpty()) { |
| 198 exceptionState.throwTypeError("Item label required"); | 118 exceptionState.throwTypeError("Item label required"); |
| 199 return; | 119 return; |
| 200 } | 120 } |
| 201 | 121 |
| 202 if (!item.hasAmount()) { | 122 if (!item.hasAmount()) { |
| 203 exceptionState.throwTypeError("Currency amount required"); | 123 exceptionState.throwTypeError("Currency amount required"); |
| 204 return; | 124 return; |
| 205 } | 125 } |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 222 return; | 142 return; |
| 223 } | 143 } |
| 224 | 144 |
| 225 if (!PaymentsValidators::isValidAmountFormat(item.amount().value(), | 145 if (!PaymentsValidators::isValidAmountFormat(item.amount().value(), |
| 226 &errorMessage)) { | 146 &errorMessage)) { |
| 227 exceptionState.throwTypeError(errorMessage); | 147 exceptionState.throwTypeError(errorMessage); |
| 228 return; | 148 return; |
| 229 } | 149 } |
| 230 } | 150 } |
| 231 | 151 |
| 232 void validateDisplayItems(const HeapVector<PaymentItem>& items, | 152 String stringifyData(const ScriptValue& data, ExceptionState& exceptionState) { |
|
Mathieu
2016/11/01 14:39:16
Not sure of the convention for documentation in Bl
please use gerrit instead
2016/11/02 16:27:09
Done.
| |
| 233 ExceptionState& exceptionState) { | 153 if (data.isEmpty()) |
| 234 for (const auto& item : items) { | 154 return ""; |
| 235 validateShippingOptionOrPaymentItem(item, exceptionState); | 155 |
| 236 if (exceptionState.hadException()) | 156 std::unique_ptr<JSONValue> value = |
| 237 return; | 157 toJSONValue(data.context(), data.v8Value()); |
| 158 if (!value) { | |
| 159 exceptionState.throwTypeError( | |
| 160 "Unable to parse payment method specific data"); | |
| 161 return ""; | |
| 238 } | 162 } |
| 163 | |
| 164 if (value->isNull()) | |
|
Mathieu
2016/11/01 14:39:16
combine with previous block?
if (!value || value-
please use gerrit instead
2016/11/02 16:27:09
That's right.
"!value" catches objects that canno
| |
| 165 return ""; | |
| 166 | |
| 167 if (value->getType() != JSONValue::TypeObject) { | |
| 168 exceptionState.throwTypeError("Data should be a JSON-serializable object"); | |
| 169 return ""; | |
| 170 } | |
| 171 | |
| 172 return JSONObject::cast(value.get())->toJSONString(); | |
| 239 } | 173 } |
| 240 | 174 |
| 241 // Returns false if |options| should be ignored, even if an exception was not | 175 void validateAndConvertPaymentDetails(const PaymentDetails& input, |
| 242 // thrown. TODO(rouslan): Clear shipping options instead of ignoring them when | 176 bool requestShipping, |
| 243 // http://crbug.com/601193 is fixed. | 177 mojom::blink::PaymentDetailsPtr& output, |
| 244 bool validateShippingOptions(const HeapVector<PaymentShippingOption>& options, | 178 ExceptionState& exceptionState) { |
| 245 ExceptionState& exceptionState) { | 179 if (!input.hasTotal()) { |
| 246 HashSet<String> uniqueIds; | 180 exceptionState.throwTypeError("Must specify total"); |
| 247 for (const auto& option : options) { | |
| 248 if (!option.hasId() || option.id().isEmpty()) { | |
| 249 exceptionState.throwTypeError("ShippingOption id required"); | |
| 250 return false; | |
| 251 } | |
| 252 | |
| 253 if (uniqueIds.contains(option.id())) | |
| 254 return false; | |
| 255 | |
| 256 uniqueIds.add(option.id()); | |
| 257 | |
| 258 validateShippingOptionOrPaymentItem(option, exceptionState); | |
| 259 if (exceptionState.hadException()) | |
| 260 return false; | |
| 261 } | |
| 262 | |
| 263 return true; | |
| 264 } | |
| 265 | |
| 266 void validatePaymentDetailsModifiers( | |
| 267 const HeapVector<PaymentDetailsModifier>& modifiers, | |
| 268 ExceptionState& exceptionState) { | |
| 269 if (modifiers.isEmpty()) { | |
| 270 exceptionState.throwTypeError( | |
| 271 "Must specify at least one payment details modifier"); | |
| 272 return; | 181 return; |
| 273 } | 182 } |
| 274 | 183 |
| 275 for (const auto& modifier : modifiers) { | 184 validateShippingOptionOrPaymentItem(input.total(), exceptionState); |
| 276 if (modifier.supportedMethods().isEmpty()) { | 185 if (exceptionState.hadException()) |
| 277 exceptionState.throwTypeError( | 186 return; |
| 278 "Must specify at least one payment method identifier"); | 187 |
| 188 if (input.total().amount().value()[0] == '-') { | |
| 189 exceptionState.throwTypeError("Total amount value should be non-negative"); | |
| 190 return; | |
| 191 } | |
| 192 | |
| 193 output->total = mojom::blink::PaymentItem::From(input.total()); | |
| 194 | |
| 195 if (input.hasDisplayItems()) { | |
| 196 output->display_items.resize(input.displayItems().size()); | |
| 197 for (size_t i = 0; i < input.displayItems().size(); ++i) { | |
| 198 const PaymentItem& item = input.displayItems()[i]; | |
| 199 validateShippingOptionOrPaymentItem(item, exceptionState); | |
| 200 if (exceptionState.hadException()) | |
| 201 return; | |
| 202 | |
| 203 output->display_items[i] = mojom::blink::PaymentItem::From(item); | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 if (input.hasShippingOptions() && requestShipping) { | |
| 208 HashSet<String> uniqueIds; | |
| 209 output->shipping_options.resize(input.shippingOptions().size()); | |
| 210 for (size_t i = 0; i < input.shippingOptions().size(); ++i) { | |
| 211 const PaymentShippingOption& option = input.shippingOptions()[i]; | |
| 212 if (!option.hasId() || option.id().isEmpty()) { | |
| 213 exceptionState.throwTypeError("ShippingOption id required"); | |
| 214 return; | |
| 215 } | |
| 216 | |
| 217 if (uniqueIds.contains(option.id())) { | |
| 218 // Duplicate identifiers cause all shipping options to be ignored. | |
| 219 output->shipping_options.resize(0); | |
| 220 break; | |
| 221 } | |
| 222 | |
| 223 uniqueIds.add(option.id()); | |
| 224 | |
| 225 validateShippingOptionOrPaymentItem(option, exceptionState); | |
| 226 if (exceptionState.hadException()) | |
| 227 return; | |
| 228 | |
| 229 output->shipping_options[i] = | |
| 230 mojom::blink::PaymentShippingOption::From(option); | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 if (input.hasModifiers()) { | |
| 235 if (input.modifiers().isEmpty()) { | |
| 236 exceptionState.throwTypeError("At least one modifier expected"); | |
| 279 return; | 237 return; |
| 280 } | 238 } |
| 281 | 239 |
| 282 if (modifier.hasTotal()) { | 240 output->modifiers.resize(input.modifiers().size()); |
| 283 validateShippingOptionOrPaymentItem(modifier.total(), exceptionState); | 241 for (size_t i = 0; i < input.modifiers().size(); ++i) { |
| 242 const PaymentDetailsModifier& modifier = input.modifiers()[i]; | |
| 243 output->modifiers[i] = mojom::blink::PaymentDetailsModifier::New(); | |
| 244 if (modifier.supportedMethods().isEmpty()) { | |
| 245 exceptionState.throwTypeError( | |
| 246 "Modifier must specify at least one payment method identifier"); | |
| 247 return; | |
| 248 } | |
| 249 | |
| 250 output->modifiers[i]->supported_methods = modifier.supportedMethods(); | |
| 251 | |
| 252 if (modifier.hasTotal()) { | |
| 253 validateShippingOptionOrPaymentItem(modifier.total(), exceptionState); | |
| 254 if (exceptionState.hadException()) | |
| 255 return; | |
| 256 | |
| 257 if (modifier.total().amount().value()[0] == '-') { | |
| 258 exceptionState.throwTypeError( | |
| 259 "Modifier total amount value should be non-negative"); | |
| 260 return; | |
| 261 } | |
| 262 | |
| 263 output->modifiers[i]->total = | |
| 264 mojom::blink::PaymentItem::From(modifier.total()); | |
| 265 } | |
| 266 | |
| 267 output->modifiers[i]->stringified_data = | |
| 268 modifier.hasData() ? stringifyData(modifier.data(), exceptionState) | |
| 269 : ""; | |
| 284 if (exceptionState.hadException()) | 270 if (exceptionState.hadException()) |
| 285 return; | 271 return; |
| 286 | 272 |
| 287 if (modifier.total().amount().value()[0] == '-') { | 273 if (modifier.hasAdditionalDisplayItems()) { |
| 288 exceptionState.throwTypeError( | 274 output->modifiers[i]->additional_display_items.resize( |
| 289 "Total amount value should be non-negative"); | 275 modifier.additionalDisplayItems().size()); |
| 290 return; | 276 for (size_t j = 0; j < modifier.additionalDisplayItems().size(); ++j) { |
| 277 const PaymentItem& item = modifier.additionalDisplayItems()[j]; | |
| 278 validateShippingOptionOrPaymentItem(item, exceptionState); | |
| 279 if (exceptionState.hadException()) | |
| 280 return; | |
| 281 | |
| 282 output->modifiers[i]->additional_display_items[j] = | |
| 283 mojom::blink::PaymentItem::From(item); | |
| 284 } | |
| 291 } | 285 } |
| 292 } | 286 } |
| 293 | |
| 294 if (modifier.hasAdditionalDisplayItems()) { | |
| 295 validateDisplayItems(modifier.additionalDisplayItems(), exceptionState); | |
| 296 if (exceptionState.hadException()) | |
| 297 return; | |
| 298 } | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 // Returns false if the shipping options should be ignored without throwing an | |
| 303 // exception. | |
| 304 bool validatePaymentDetails(const PaymentDetails& details, | |
| 305 ExceptionState& exceptionState) { | |
| 306 bool keepShippingOptions = true; | |
| 307 if (!details.hasTotal()) { | |
| 308 exceptionState.throwTypeError("Must specify total"); | |
| 309 return keepShippingOptions; | |
| 310 } | |
| 311 | |
| 312 validateShippingOptionOrPaymentItem(details.total(), exceptionState); | |
| 313 if (exceptionState.hadException()) | |
| 314 return keepShippingOptions; | |
| 315 | |
| 316 if (details.total().amount().value()[0] == '-') { | |
| 317 exceptionState.throwTypeError("Total amount value should be non-negative"); | |
| 318 return keepShippingOptions; | |
| 319 } | |
| 320 | |
| 321 if (details.hasDisplayItems()) { | |
| 322 validateDisplayItems(details.displayItems(), exceptionState); | |
| 323 if (exceptionState.hadException()) | |
| 324 return keepShippingOptions; | |
| 325 } | |
| 326 | |
| 327 if (details.hasShippingOptions()) { | |
| 328 keepShippingOptions = | |
| 329 validateShippingOptions(details.shippingOptions(), exceptionState); | |
| 330 | |
| 331 if (exceptionState.hadException()) | |
| 332 return keepShippingOptions; | |
| 333 } | |
| 334 | |
| 335 if (details.hasModifiers()) { | |
| 336 validatePaymentDetailsModifiers(details.modifiers(), exceptionState); | |
| 337 if (exceptionState.hadException()) | |
| 338 return keepShippingOptions; | |
| 339 } | 287 } |
| 340 | 288 |
| 341 String errorMessage; | 289 String errorMessage; |
| 342 if (!PaymentsValidators::isValidErrorMsgFormat(details.error(), | 290 if (!PaymentsValidators::isValidErrorMsgFormat(input.error(), |
| 343 &errorMessage)) { | 291 &errorMessage)) { |
| 344 exceptionState.throwTypeError(errorMessage); | 292 exceptionState.throwTypeError(errorMessage); |
| 293 return; | |
| 345 } | 294 } |
| 346 | 295 |
| 347 return keepShippingOptions; | 296 output->error = |
| 297 input.hasError() && !input.error().isNull() ? input.error() : ""; | |
| 348 } | 298 } |
| 349 | 299 |
| 350 void validateAndConvertPaymentMethodData( | 300 void validateAndConvertPaymentMethodData( |
|
Mathieu
2016/11/01 14:39:16
would benefit from a comment such as
// Takes th
please use gerrit instead
2016/11/02 16:27:09
Done. Also added a similar comment to validatedAnd
| |
| 351 const HeapVector<PaymentMethodData>& paymentMethodData, | 301 const HeapVector<PaymentMethodData>& input, |
| 352 Vector<PaymentRequest::MethodData>* methodData, | 302 Vector<mojom::blink::PaymentMethodDataPtr>& output, |
| 353 ExceptionState& exceptionState) { | 303 ExceptionState& exceptionState) { |
| 354 if (paymentMethodData.isEmpty()) { | 304 if (input.isEmpty()) { |
| 355 exceptionState.throwTypeError( | 305 exceptionState.throwTypeError( |
| 356 "Must specify at least one payment method identifier"); | 306 "Must specify at least one payment method identifier"); |
| 357 return; | 307 return; |
| 358 } | 308 } |
| 359 | 309 |
| 360 for (const auto& pmd : paymentMethodData) { | 310 output.resize(input.size()); |
| 311 for (size_t i = 0; i < input.size(); ++i) { | |
| 312 const PaymentMethodData& pmd = input[i]; | |
| 361 if (pmd.supportedMethods().isEmpty()) { | 313 if (pmd.supportedMethods().isEmpty()) { |
| 362 exceptionState.throwTypeError( | 314 exceptionState.throwTypeError( |
| 363 "Must specify at least one payment method identifier"); | 315 "Must specify at least one payment method identifier"); |
| 364 return; | 316 return; |
| 365 } | 317 } |
| 366 | 318 |
| 367 String stringifiedData = ""; | 319 output[i] = mojom::blink::PaymentMethodData::New(); |
| 368 if (pmd.hasData() && !pmd.data().isEmpty()) { | 320 output[i]->supported_methods = pmd.supportedMethods(); |
| 369 std::unique_ptr<JSONValue> value = | 321 output[i]->stringified_data = |
| 370 toJSONValue(pmd.data().context(), pmd.data().v8Value()); | 322 pmd.hasData() ? stringifyData(pmd.data(), exceptionState) : ""; |
| 371 if (!value) { | 323 if (exceptionState.hadException()) |
| 372 exceptionState.throwTypeError( | 324 return; |
| 373 "Unable to parse payment method specific data"); | |
| 374 return; | |
| 375 } | |
| 376 if (!value->isNull()) { | |
| 377 if (value->getType() != JSONValue::TypeObject) { | |
| 378 exceptionState.throwTypeError( | |
| 379 "Data should be a JSON-serializable object"); | |
| 380 return; | |
| 381 } | |
| 382 stringifiedData = JSONObject::cast(value.get())->toJSONString(); | |
| 383 } | |
| 384 } | |
| 385 methodData->append( | |
| 386 PaymentRequest::MethodData(pmd.supportedMethods(), stringifiedData)); | |
| 387 } | 325 } |
| 388 } | 326 } |
| 389 | 327 |
| 390 String getSelectedShippingOption(const PaymentDetails& details) { | 328 String getSelectedShippingOption( |
| 391 String result; | 329 const mojom::blink::PaymentDetailsPtr& details) { |
| 392 if (!details.hasShippingOptions()) | 330 if (details->shipping_options.isEmpty()) |
| 393 return result; | 331 return String(); |
| 394 | 332 |
| 395 for (int i = details.shippingOptions().size() - 1; i >= 0; --i) { | 333 for (int i = details->shipping_options.size() - 1; i >= 0; --i) { |
| 396 if (details.shippingOptions()[i].hasSelected() && | 334 if (details->shipping_options[i]->selected) |
| 397 details.shippingOptions()[i].selected()) { | 335 return details->shipping_options[i]->id; |
| 398 return details.shippingOptions()[i].id(); | |
| 399 } | |
| 400 } | 336 } |
| 401 | 337 |
| 402 return result; | 338 return String(); |
| 403 } | 339 } |
| 404 | 340 |
| 405 String getValidShippingType(const String& shippingType) { | 341 String getValidShippingType(const String& shippingType) { |
| 406 static const char* const validValues[] = { | 342 static const char* const validValues[] = { |
| 407 "shipping", "delivery", "pickup", | 343 "shipping", "delivery", "pickup", |
| 408 }; | 344 }; |
| 409 for (size_t i = 0; i < WTF_ARRAY_LENGTH(validValues); i++) { | 345 for (size_t i = 0; i < WTF_ARRAY_LENGTH(validValues); i++) { |
| 410 if (shippingType == validValues[i]) | 346 if (shippingType == validValues[i]) |
| 411 return shippingType; | 347 return shippingType; |
| 412 } | 348 } |
| 413 return validValues[0]; | 349 return validValues[0]; |
| 414 } | 350 } |
| 415 | 351 |
| 416 mojom::blink::PaymentDetailsPtr maybeKeepShippingOptions( | |
| 417 mojom::blink::PaymentDetailsPtr details, | |
| 418 bool keep) { | |
| 419 if (!keep) | |
| 420 details->shipping_options.resize(0); | |
| 421 | |
| 422 return details; | |
| 423 } | |
| 424 | |
| 425 bool allowedToUsePaymentRequest(const Frame* frame) { | 352 bool allowedToUsePaymentRequest(const Frame* frame) { |
| 426 // To determine whether a Document object |document| is allowed to use the | 353 // To determine whether a Document object |document| is allowed to use the |
| 427 // feature indicated by attribute name |allowpaymentrequest|, run these steps: | 354 // feature indicated by attribute name |allowpaymentrequest|, run these steps: |
| 428 | 355 |
| 429 // 1. If |document| has no browsing context, then return false. | 356 // 1. If |document| has no browsing context, then return false. |
| 430 if (!frame) | 357 if (!frame) |
| 431 return false; | 358 return false; |
| 432 | 359 |
| 433 // 2. If |document|'s browsing context is a top-level browsing context, then | 360 // 2. If |document|'s browsing context is a top-level browsing context, then |
| 434 // return true. | 361 // return true. |
| 435 if (frame->isMainFrame()) | 362 if (frame->isMainFrame()) |
| 436 return true; | 363 return true; |
| 437 | 364 |
| 438 // 3. If |document|'s browsing context has a browsing context container that | 365 // 3. If |document|'s browsing context has a browsing context container that |
| 439 // is an iframe element with an |allowpaymentrequest| attribute specified, and | 366 // is an iframe element with an |allowpaymentrequest| attribute specified, and |
| 440 // whose node document is allowed to use the feature indicated by | 367 // whose node document is allowed to use the feature indicated by |
| 441 // |allowpaymentrequest|, then return true. | 368 // |allowpaymentrequest|, then return true. |
| 442 HTMLFrameOwnerElement* ownerElement = toHTMLFrameOwnerElement(frame->owner()); | 369 HTMLFrameOwnerElement* ownerElement = toHTMLFrameOwnerElement(frame->owner()); |
| 443 if (ownerElement && isHTMLIFrameElement(ownerElement)) { | 370 if (ownerElement && isHTMLIFrameElement(ownerElement)) { |
| 444 HTMLIFrameElement* iframe = toHTMLIFrameElement(ownerElement); | 371 HTMLIFrameElement* iframe = toHTMLIFrameElement(ownerElement); |
| 445 if (HTMLIFrameElementPayments::from(*iframe).allowPaymentRequest(*iframe)) | 372 if (HTMLIFrameElementPayments::from(*iframe).allowPaymentRequest(*iframe)) |
| 446 return allowedToUsePaymentRequest(frame->tree().parent()); | 373 return allowedToUsePaymentRequest(frame->tree().parent()); |
| 447 } | 374 } |
| 448 | 375 |
| 449 // 4. Return false. | 376 // 4. Return false. |
| 450 return false; | 377 return false; |
| 451 } | 378 } |
| 452 | 379 |
| 453 WTF::Vector<mojom::blink::PaymentMethodDataPtr> ConvertPaymentMethodData( | |
| 454 const Vector<PaymentRequest::MethodData>& blinkMethods) { | |
| 455 WTF::Vector<mojom::blink::PaymentMethodDataPtr> mojoMethods( | |
| 456 blinkMethods.size()); | |
| 457 for (size_t i = 0; i < blinkMethods.size(); ++i) { | |
| 458 mojoMethods[i] = mojom::blink::PaymentMethodData::New(); | |
| 459 mojoMethods[i]->supported_methods = | |
| 460 WTF::Vector<WTF::String>(blinkMethods[i].supportedMethods); | |
| 461 mojoMethods[i]->stringified_data = blinkMethods[i].stringifiedData; | |
| 462 } | |
| 463 return mojoMethods; | |
| 464 } | |
| 465 | |
| 466 } // namespace | 380 } // namespace |
| 467 | 381 |
| 468 PaymentRequest* PaymentRequest::create( | 382 PaymentRequest* PaymentRequest::create( |
| 469 ScriptState* scriptState, | 383 ScriptState* scriptState, |
| 470 const HeapVector<PaymentMethodData>& methodData, | 384 const HeapVector<PaymentMethodData>& methodData, |
| 471 const PaymentDetails& details, | 385 const PaymentDetails& details, |
| 472 ExceptionState& exceptionState) { | 386 ExceptionState& exceptionState) { |
| 473 return new PaymentRequest(scriptState, methodData, details, PaymentOptions(), | 387 return new PaymentRequest(scriptState, methodData, details, PaymentOptions(), |
| 474 exceptionState); | 388 exceptionState); |
| 475 } | 389 } |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 573 V8PaymentDetails::toImpl(detailsScriptValue.isolate(), | 487 V8PaymentDetails::toImpl(detailsScriptValue.isolate(), |
| 574 detailsScriptValue.v8Value(), details, | 488 detailsScriptValue.v8Value(), details, |
| 575 exceptionState); | 489 exceptionState); |
| 576 if (exceptionState.hadException()) { | 490 if (exceptionState.hadException()) { |
| 577 m_showResolver->reject( | 491 m_showResolver->reject( |
| 578 DOMException::create(SyntaxError, exceptionState.message())); | 492 DOMException::create(SyntaxError, exceptionState.message())); |
| 579 clearResolversAndCloseMojoConnection(); | 493 clearResolversAndCloseMojoConnection(); |
| 580 return; | 494 return; |
| 581 } | 495 } |
| 582 | 496 |
| 583 bool keepShippingOptions = validatePaymentDetails(details, exceptionState); | 497 mojom::blink::PaymentDetailsPtr validatedDetails = |
| 498 mojom::blink::PaymentDetails::New(); | |
| 499 validateAndConvertPaymentDetails(details, m_options.requestShipping(), | |
| 500 validatedDetails, exceptionState); | |
| 584 if (exceptionState.hadException()) { | 501 if (exceptionState.hadException()) { |
| 585 m_showResolver->reject( | 502 m_showResolver->reject( |
| 586 DOMException::create(SyntaxError, exceptionState.message())); | 503 DOMException::create(SyntaxError, exceptionState.message())); |
| 587 clearResolversAndCloseMojoConnection(); | 504 clearResolversAndCloseMojoConnection(); |
| 588 return; | 505 return; |
| 589 } | 506 } |
| 590 | 507 |
| 591 if (m_options.requestShipping()) { | 508 if (m_options.requestShipping()) |
| 592 if (keepShippingOptions) | 509 m_shippingOption = getSelectedShippingOption(validatedDetails); |
| 593 m_shippingOption = getSelectedShippingOption(details); | |
| 594 else | |
| 595 m_shippingOption = String(); | |
| 596 } | |
| 597 | 510 |
| 598 m_paymentProvider->UpdateWith(maybeKeepShippingOptions( | 511 m_paymentProvider->UpdateWith(std::move(validatedDetails)); |
| 599 mojom::blink::PaymentDetails::From(details), keepShippingOptions)); | |
| 600 } | 512 } |
| 601 | 513 |
| 602 void PaymentRequest::onUpdatePaymentDetailsFailure(const String& error) { | 514 void PaymentRequest::onUpdatePaymentDetailsFailure(const String& error) { |
| 603 if (m_showResolver) | 515 if (m_showResolver) |
| 604 m_showResolver->reject(DOMException::create(AbortError, error)); | 516 m_showResolver->reject(DOMException::create(AbortError, error)); |
| 605 if (m_completeResolver) | 517 if (m_completeResolver) |
| 606 m_completeResolver->reject(DOMException::create(AbortError, error)); | 518 m_completeResolver->reject(DOMException::create(AbortError, error)); |
| 607 clearResolversAndCloseMojoConnection(); | 519 clearResolversAndCloseMojoConnection(); |
| 608 } | 520 } |
| 609 | 521 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 625 PaymentRequest::PaymentRequest(ScriptState* scriptState, | 537 PaymentRequest::PaymentRequest(ScriptState* scriptState, |
| 626 const HeapVector<PaymentMethodData>& methodData, | 538 const HeapVector<PaymentMethodData>& methodData, |
| 627 const PaymentDetails& details, | 539 const PaymentDetails& details, |
| 628 const PaymentOptions& options, | 540 const PaymentOptions& options, |
| 629 ExceptionState& exceptionState) | 541 ExceptionState& exceptionState) |
| 630 : ContextLifecycleObserver(scriptState->getExecutionContext()), | 542 : ContextLifecycleObserver(scriptState->getExecutionContext()), |
| 631 ActiveScriptWrappable(this), | 543 ActiveScriptWrappable(this), |
| 632 m_options(options), | 544 m_options(options), |
| 633 m_clientBinding(this), | 545 m_clientBinding(this), |
| 634 m_completeTimer(this, &PaymentRequest::onCompleteTimeout) { | 546 m_completeTimer(this, &PaymentRequest::onCompleteTimeout) { |
| 635 Vector<MethodData> validatedMethodData; | 547 Vector<mojom::blink::PaymentMethodDataPtr> validatedMethodData; |
| 636 validateAndConvertPaymentMethodData(methodData, &validatedMethodData, | 548 validateAndConvertPaymentMethodData(methodData, validatedMethodData, |
| 637 exceptionState); | 549 exceptionState); |
| 638 if (exceptionState.hadException()) | 550 if (exceptionState.hadException()) |
| 639 return; | 551 return; |
| 640 | 552 |
| 641 if (!scriptState->getExecutionContext()->isSecureContext()) { | 553 if (!scriptState->getExecutionContext()->isSecureContext()) { |
| 642 exceptionState.throwSecurityError("Must be in a secure context"); | 554 exceptionState.throwSecurityError("Must be in a secure context"); |
| 643 return; | 555 return; |
| 644 } | 556 } |
| 645 | 557 |
| 646 if (!allowedToUsePaymentRequest(scriptState->domWindow()->frame())) { | 558 if (!allowedToUsePaymentRequest(scriptState->domWindow()->frame())) { |
| 647 exceptionState.throwSecurityError( | 559 exceptionState.throwSecurityError( |
| 648 "Must be in a top-level browsing context or an iframe needs to specify " | 560 "Must be in a top-level browsing context or an iframe needs to specify " |
| 649 "'allowpaymentrequest' explicitly"); | 561 "'allowpaymentrequest' explicitly"); |
| 650 return; | 562 return; |
| 651 } | 563 } |
| 652 | 564 |
| 653 bool keepShippingOptions = validatePaymentDetails(details, exceptionState); | 565 mojom::blink::PaymentDetailsPtr validatedDetails = |
| 566 mojom::blink::PaymentDetails::New(); | |
| 567 validateAndConvertPaymentDetails(details, m_options.requestShipping(), | |
| 568 validatedDetails, exceptionState); | |
| 654 if (exceptionState.hadException()) | 569 if (exceptionState.hadException()) |
| 655 return; | 570 return; |
| 656 | 571 |
| 657 if (details.hasError() && !details.error().isEmpty()) { | 572 if (!validatedDetails->error.isEmpty()) { |
| 658 exceptionState.throwTypeError("Error value should be empty"); | 573 exceptionState.throwTypeError("Error value should be empty"); |
| 659 return; | 574 return; |
| 660 } | 575 } |
| 661 | 576 |
| 662 if (m_options.requestShipping()) { | 577 if (m_options.requestShipping()) { |
| 663 if (keepShippingOptions) | 578 m_shippingOption = getSelectedShippingOption(validatedDetails); |
| 664 m_shippingOption = getSelectedShippingOption(details); | |
| 665 m_shippingType = getValidShippingType(m_options.shippingType()); | 579 m_shippingType = getValidShippingType(m_options.shippingType()); |
| 666 } | 580 } |
| 667 | 581 |
| 668 scriptState->domWindow()->frame()->interfaceProvider()->getInterface( | 582 scriptState->domWindow()->frame()->interfaceProvider()->getInterface( |
| 669 mojo::GetProxy(&m_paymentProvider)); | 583 mojo::GetProxy(&m_paymentProvider)); |
| 670 m_paymentProvider.set_connection_error_handler(convertToBaseCallback( | 584 m_paymentProvider.set_connection_error_handler(convertToBaseCallback( |
| 671 WTF::bind(&PaymentRequest::OnError, wrapWeakPersistent(this), | 585 WTF::bind(&PaymentRequest::OnError, wrapWeakPersistent(this), |
| 672 mojom::blink::PaymentErrorReason::UNKNOWN))); | 586 mojom::blink::PaymentErrorReason::UNKNOWN))); |
| 673 m_paymentProvider->Init( | 587 m_paymentProvider->Init(m_clientBinding.CreateInterfacePtrAndBind(), |
| 674 m_clientBinding.CreateInterfacePtrAndBind(), | 588 std::move(validatedMethodData), |
| 675 ConvertPaymentMethodData(validatedMethodData), | 589 std::move(validatedDetails), |
| 676 maybeKeepShippingOptions( | 590 mojom::blink::PaymentOptions::From(m_options)); |
| 677 mojom::blink::PaymentDetails::From(details), | |
| 678 keepShippingOptions && m_options.requestShipping()), | |
| 679 mojom::blink::PaymentOptions::From(m_options)); | |
| 680 } | 591 } |
| 681 | 592 |
| 682 void PaymentRequest::contextDestroyed() { | 593 void PaymentRequest::contextDestroyed() { |
| 683 clearResolversAndCloseMojoConnection(); | 594 clearResolversAndCloseMojoConnection(); |
| 684 } | 595 } |
| 685 | 596 |
| 686 void PaymentRequest::OnShippingAddressChange( | 597 void PaymentRequest::OnShippingAddressChange( |
| 687 mojom::blink::PaymentAddressPtr address) { | 598 mojom::blink::PaymentAddressPtr address) { |
| 688 DCHECK(m_showResolver); | 599 DCHECK(m_showResolver); |
| 689 DCHECK(!m_completeResolver); | 600 DCHECK(!m_completeResolver); |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 763 m_completeTimer.startOneShot(completeTimeoutSeconds, BLINK_FROM_HERE); | 674 m_completeTimer.startOneShot(completeTimeoutSeconds, BLINK_FROM_HERE); |
| 764 | 675 |
| 765 m_showResolver->resolve(new PaymentResponse(std::move(response), this)); | 676 m_showResolver->resolve(new PaymentResponse(std::move(response), this)); |
| 766 | 677 |
| 767 // Do not close the mojo connection here. The merchant website should call | 678 // Do not close the mojo connection here. The merchant website should call |
| 768 // PaymentResponse::complete(String), which will be forwarded over the mojo | 679 // PaymentResponse::complete(String), which will be forwarded over the mojo |
| 769 // connection to display a success or failure message to the user. | 680 // connection to display a success or failure message to the user. |
| 770 m_showResolver.clear(); | 681 m_showResolver.clear(); |
| 771 } | 682 } |
| 772 | 683 |
| 773 void PaymentRequest::OnError(mojo::PaymentErrorReason error) { | 684 void PaymentRequest::OnError(mojom::blink::PaymentErrorReason error) { |
| 774 if (!Platform::current()) { | 685 if (!Platform::current()) { |
| 775 // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. | 686 // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. |
| 776 return; | 687 return; |
| 777 } | 688 } |
| 778 | 689 |
| 779 bool isError = false; | 690 bool isError = false; |
| 780 ExceptionCode ec = UnknownError; | 691 ExceptionCode ec = UnknownError; |
| 781 String message; | 692 String message; |
| 782 | 693 |
| 783 switch (error) { | 694 switch (error) { |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 851 m_completeTimer.stop(); | 762 m_completeTimer.stop(); |
| 852 m_completeResolver.clear(); | 763 m_completeResolver.clear(); |
| 853 m_showResolver.clear(); | 764 m_showResolver.clear(); |
| 854 m_abortResolver.clear(); | 765 m_abortResolver.clear(); |
| 855 if (m_clientBinding.is_bound()) | 766 if (m_clientBinding.is_bound()) |
| 856 m_clientBinding.Close(); | 767 m_clientBinding.Close(); |
| 857 m_paymentProvider.reset(); | 768 m_paymentProvider.reset(); |
| 858 } | 769 } |
| 859 | 770 |
| 860 } // namespace blink | 771 } // namespace blink |
| OLD | NEW |