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/ScriptPromiseResolver.h" | 8 #include "bindings/core/v8/ScriptPromiseResolver.h" |
| 9 #include "bindings/core/v8/ScriptState.h" | 9 #include "bindings/core/v8/ScriptState.h" |
| 10 #include "bindings/core/v8/V8StringResource.h" | 10 #include "bindings/core/v8/V8StringResource.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 |
| (...skipping 29 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 // Converts a JSON-serializable object into a JSON string. For example, the |
| 233 ExceptionState& exceptionState) { | 153 // object |
| 234 for (const auto& item : items) { | 154 // var data = {"foo": "bar"}; |
| 235 validateShippingOptionOrPaymentItem(item, exceptionState); | 155 // is converted into the string |
| 236 if (exceptionState.hadException()) | 156 // '{"foo": "bar"}' |
| 157 String stringifyData(const ScriptValue& data, ExceptionState& exceptionState) { | |
| 158 if (data.isEmpty()) | |
| 159 return ""; | |
| 160 | |
| 161 if (!data.v8Value()->IsObject() || data.v8Value()->IsArray()) { | |
| 162 exceptionState.throwTypeError("Data should be a JSON-serializable object"); | |
| 163 return ""; | |
| 164 } | |
| 165 | |
| 166 v8::MaybeLocal<v8::String> value = | |
| 167 v8::JSON::Stringify(data.context(), data.v8Value().As<v8::Object>()); | |
|
haraken
2016/11/03 15:21:32
Can you use ToLocal instead of MaybeLocal?
| |
| 168 if (value.IsEmpty()) { | |
| 169 exceptionState.throwTypeError( | |
| 170 "Unable to parse payment method specific data"); | |
| 171 return ""; | |
| 172 } | |
| 173 | |
| 174 return v8StringToWebCoreString<String>(value.ToLocalChecked(), | |
| 175 DoNotExternalize); | |
| 176 } | |
| 177 | |
| 178 // Takes the PaymentDetails and requestShipping option as specified by the | |
| 179 // website/javascript and creates a validated Mojo PaymentDetailsPtr to e passed | |
| 180 // to the embedder/browser. | |
| 181 void validateAndConvertPaymentDetails(const PaymentDetails& input, | |
| 182 bool requestShipping, | |
| 183 mojom::blink::PaymentDetailsPtr& output, | |
| 184 ExceptionState& exceptionState) { | |
| 185 if (!input.hasTotal()) { | |
| 186 exceptionState.throwTypeError("Must specify total"); | |
| 187 return; | |
| 188 } | |
| 189 | |
| 190 validateShippingOptionOrPaymentItem(input.total(), exceptionState); | |
| 191 if (exceptionState.hadException()) | |
| 192 return; | |
| 193 | |
| 194 if (input.total().amount().value()[0] == '-') { | |
| 195 exceptionState.throwTypeError("Total amount value should be non-negative"); | |
| 196 return; | |
| 197 } | |
| 198 | |
| 199 output->total = mojom::blink::PaymentItem::From(input.total()); | |
| 200 | |
| 201 if (input.hasDisplayItems()) { | |
| 202 output->display_items.resize(input.displayItems().size()); | |
| 203 for (size_t i = 0; i < input.displayItems().size(); ++i) { | |
| 204 const PaymentItem& item = input.displayItems()[i]; | |
| 205 validateShippingOptionOrPaymentItem(item, exceptionState); | |
| 206 if (exceptionState.hadException()) | |
| 207 return; | |
| 208 | |
| 209 output->display_items[i] = mojom::blink::PaymentItem::From(item); | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 if (input.hasShippingOptions() && requestShipping) { | |
| 214 HashSet<String> uniqueIds; | |
| 215 output->shipping_options.resize(input.shippingOptions().size()); | |
| 216 for (size_t i = 0; i < input.shippingOptions().size(); ++i) { | |
| 217 const PaymentShippingOption& option = input.shippingOptions()[i]; | |
| 218 if (!option.hasId() || option.id().isEmpty()) { | |
| 219 exceptionState.throwTypeError("ShippingOption id required"); | |
| 220 return; | |
| 221 } | |
| 222 | |
| 223 if (uniqueIds.contains(option.id())) { | |
| 224 // Duplicate identifiers cause all shipping options to be ignored. | |
| 225 output->shipping_options.resize(0); | |
| 226 break; | |
| 227 } | |
| 228 | |
| 229 uniqueIds.add(option.id()); | |
| 230 | |
| 231 validateShippingOptionOrPaymentItem(option, exceptionState); | |
| 232 if (exceptionState.hadException()) | |
| 233 return; | |
| 234 | |
| 235 output->shipping_options[i] = | |
| 236 mojom::blink::PaymentShippingOption::From(option); | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 if (input.hasModifiers()) { | |
| 241 if (input.modifiers().isEmpty()) { | |
| 242 exceptionState.throwTypeError("At least one modifier expected"); | |
| 237 return; | 243 return; |
| 238 } | 244 } |
| 239 } | 245 |
| 240 | 246 output->modifiers.resize(input.modifiers().size()); |
| 241 // Returns false if |options| should be ignored, even if an exception was not | 247 for (size_t i = 0; i < input.modifiers().size(); ++i) { |
| 242 // thrown. TODO(rouslan): Clear shipping options instead of ignoring them when | 248 const PaymentDetailsModifier& modifier = input.modifiers()[i]; |
| 243 // http://crbug.com/601193 is fixed. | 249 output->modifiers[i] = mojom::blink::PaymentDetailsModifier::New(); |
| 244 bool validateShippingOptions(const HeapVector<PaymentShippingOption>& options, | 250 if (modifier.supportedMethods().isEmpty()) { |
| 245 ExceptionState& exceptionState) { | 251 exceptionState.throwTypeError( |
| 246 HashSet<String> uniqueIds; | 252 "Modifier must specify at least one payment method identifier"); |
| 247 for (const auto& option : options) { | 253 return; |
| 248 if (!option.hasId() || option.id().isEmpty()) { | 254 } |
| 249 exceptionState.throwTypeError("ShippingOption id required"); | 255 |
| 250 return false; | 256 output->modifiers[i]->supported_methods = modifier.supportedMethods(); |
| 251 } | 257 |
| 252 | 258 if (modifier.hasTotal()) { |
| 253 if (uniqueIds.contains(option.id())) | 259 validateShippingOptionOrPaymentItem(modifier.total(), exceptionState); |
| 254 return false; | 260 if (exceptionState.hadException()) |
| 255 | 261 return; |
| 256 uniqueIds.add(option.id()); | 262 |
| 257 | 263 if (modifier.total().amount().value()[0] == '-') { |
| 258 validateShippingOptionOrPaymentItem(option, exceptionState); | 264 exceptionState.throwTypeError( |
| 259 if (exceptionState.hadException()) | 265 "Modifier total amount value should be non-negative"); |
| 260 return false; | 266 return; |
| 261 } | 267 } |
| 262 | 268 |
| 263 return true; | 269 output->modifiers[i]->total = |
| 264 } | 270 mojom::blink::PaymentItem::From(modifier.total()); |
| 265 | 271 } |
| 266 void validatePaymentDetailsModifiers( | 272 |
| 267 const HeapVector<PaymentDetailsModifier>& modifiers, | 273 output->modifiers[i]->stringified_data = |
| 268 ExceptionState& exceptionState) { | 274 modifier.hasData() ? stringifyData(modifier.data(), exceptionState) |
| 269 if (modifiers.isEmpty()) { | 275 : ""; |
| 270 exceptionState.throwTypeError( | |
| 271 "Must specify at least one payment details modifier"); | |
| 272 return; | |
| 273 } | |
| 274 | |
| 275 for (const auto& modifier : modifiers) { | |
| 276 if (modifier.supportedMethods().isEmpty()) { | |
| 277 exceptionState.throwTypeError( | |
| 278 "Must specify at least one payment method identifier"); | |
| 279 return; | |
| 280 } | |
| 281 | |
| 282 if (modifier.hasTotal()) { | |
| 283 validateShippingOptionOrPaymentItem(modifier.total(), exceptionState); | |
| 284 if (exceptionState.hadException()) | 276 if (exceptionState.hadException()) |
| 285 return; | 277 return; |
| 286 | 278 |
| 287 if (modifier.total().amount().value()[0] == '-') { | 279 if (modifier.hasAdditionalDisplayItems()) { |
| 288 exceptionState.throwTypeError( | 280 output->modifiers[i]->additional_display_items.resize( |
| 289 "Total amount value should be non-negative"); | 281 modifier.additionalDisplayItems().size()); |
| 290 return; | 282 for (size_t j = 0; j < modifier.additionalDisplayItems().size(); ++j) { |
| 291 } | 283 const PaymentItem& item = modifier.additionalDisplayItems()[j]; |
| 292 } | 284 validateShippingOptionOrPaymentItem(item, exceptionState); |
| 293 | 285 if (exceptionState.hadException()) |
| 294 if (modifier.hasAdditionalDisplayItems()) { | 286 return; |
| 295 validateDisplayItems(modifier.additionalDisplayItems(), exceptionState); | 287 |
| 296 if (exceptionState.hadException()) | 288 output->modifiers[i]->additional_display_items[j] = |
| 297 return; | 289 mojom::blink::PaymentItem::From(item); |
| 298 } | 290 } |
| 299 } | 291 } |
| 300 } | 292 } |
| 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 } | 293 } |
| 340 | 294 |
| 341 String errorMessage; | 295 String errorMessage; |
| 342 if (!PaymentsValidators::isValidErrorMsgFormat(details.error(), | 296 if (!PaymentsValidators::isValidErrorMsgFormat(input.error(), |
| 343 &errorMessage)) { | 297 &errorMessage)) { |
| 344 exceptionState.throwTypeError(errorMessage); | 298 exceptionState.throwTypeError(errorMessage); |
| 345 } | 299 return; |
| 346 | 300 } |
| 347 return keepShippingOptions; | 301 |
| 348 } | 302 output->error = |
| 349 | 303 input.hasError() && !input.error().isNull() ? input.error() : ""; |
| 304 } | |
| 305 | |
| 306 // Takes the vector of PaymentMethodData objects as specified by the | |
| 307 // website/javascript and creates a vector of validated Mojo | |
| 308 // PaymentMethodDataPtr objects to be passed to the embedder/browser. | |
| 350 void validateAndConvertPaymentMethodData( | 309 void validateAndConvertPaymentMethodData( |
| 351 const HeapVector<PaymentMethodData>& paymentMethodData, | 310 const HeapVector<PaymentMethodData>& input, |
| 352 Vector<PaymentRequest::MethodData>* methodData, | 311 Vector<mojom::blink::PaymentMethodDataPtr>& output, |
| 353 ExceptionState& exceptionState) { | 312 ExceptionState& exceptionState) { |
| 354 if (paymentMethodData.isEmpty()) { | 313 if (input.isEmpty()) { |
| 355 exceptionState.throwTypeError( | 314 exceptionState.throwTypeError( |
| 356 "Must specify at least one payment method identifier"); | 315 "Must specify at least one payment method identifier"); |
| 357 return; | 316 return; |
| 358 } | 317 } |
| 359 | 318 |
| 360 for (const auto& pmd : paymentMethodData) { | 319 output.resize(input.size()); |
| 320 for (size_t i = 0; i < input.size(); ++i) { | |
| 321 const PaymentMethodData& pmd = input[i]; | |
| 361 if (pmd.supportedMethods().isEmpty()) { | 322 if (pmd.supportedMethods().isEmpty()) { |
| 362 exceptionState.throwTypeError( | 323 exceptionState.throwTypeError( |
| 363 "Must specify at least one payment method identifier"); | 324 "Must specify at least one payment method identifier"); |
| 364 return; | 325 return; |
| 365 } | 326 } |
| 366 | 327 |
| 367 String stringifiedData = ""; | 328 output[i] = mojom::blink::PaymentMethodData::New(); |
| 368 if (pmd.hasData() && !pmd.data().isEmpty()) { | 329 output[i]->supported_methods = pmd.supportedMethods(); |
| 369 if (!pmd.data().v8Value()->IsObject() || | 330 output[i]->stringified_data = |
| 370 pmd.data().v8Value()->IsArray()) { | 331 pmd.hasData() ? stringifyData(pmd.data(), exceptionState) : ""; |
| 371 exceptionState.throwTypeError( | 332 if (exceptionState.hadException()) |
| 372 "Data should be a JSON-serializable object"); | 333 return; |
| 373 return; | 334 } |
| 374 } | 335 } |
| 375 | 336 |
| 376 v8::MaybeLocal<v8::String> value = v8::JSON::Stringify( | 337 String getSelectedShippingOption( |
| 377 pmd.data().context(), pmd.data().v8Value().As<v8::Object>()); | 338 const mojom::blink::PaymentDetailsPtr& details) { |
| 378 if (value.IsEmpty()) { | 339 if (details->shipping_options.isEmpty()) |
| 379 exceptionState.throwTypeError( | 340 return String(); |
| 380 "Unable to parse payment method specific data"); | 341 |
| 381 return; | 342 for (int i = details->shipping_options.size() - 1; i >= 0; --i) { |
| 382 } | 343 if (details->shipping_options[i]->selected) |
| 383 stringifiedData = v8StringToWebCoreString<String>(value.ToLocalChecked(), | 344 return details->shipping_options[i]->id; |
| 384 DoNotExternalize); | 345 } |
| 385 } | 346 |
| 386 methodData->append( | 347 return String(); |
| 387 PaymentRequest::MethodData(pmd.supportedMethods(), stringifiedData)); | |
| 388 } | |
| 389 } | |
| 390 | |
| 391 String getSelectedShippingOption(const PaymentDetails& details) { | |
| 392 String result; | |
| 393 if (!details.hasShippingOptions()) | |
| 394 return result; | |
| 395 | |
| 396 for (int i = details.shippingOptions().size() - 1; i >= 0; --i) { | |
| 397 if (details.shippingOptions()[i].hasSelected() && | |
| 398 details.shippingOptions()[i].selected()) { | |
| 399 return details.shippingOptions()[i].id(); | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 return result; | |
| 404 } | 348 } |
| 405 | 349 |
| 406 String getValidShippingType(const String& shippingType) { | 350 String getValidShippingType(const String& shippingType) { |
| 407 static const char* const validValues[] = { | 351 static const char* const validValues[] = { |
| 408 "shipping", "delivery", "pickup", | 352 "shipping", "delivery", "pickup", |
| 409 }; | 353 }; |
| 410 for (size_t i = 0; i < WTF_ARRAY_LENGTH(validValues); i++) { | 354 for (size_t i = 0; i < WTF_ARRAY_LENGTH(validValues); i++) { |
| 411 if (shippingType == validValues[i]) | 355 if (shippingType == validValues[i]) |
| 412 return shippingType; | 356 return shippingType; |
| 413 } | 357 } |
| 414 return validValues[0]; | 358 return validValues[0]; |
| 415 } | 359 } |
| 416 | 360 |
| 417 mojom::blink::PaymentDetailsPtr maybeKeepShippingOptions( | |
| 418 mojom::blink::PaymentDetailsPtr details, | |
| 419 bool keep) { | |
| 420 if (!keep) | |
| 421 details->shipping_options.resize(0); | |
| 422 | |
| 423 return details; | |
| 424 } | |
| 425 | |
| 426 bool allowedToUsePaymentRequest(const Frame* frame) { | 361 bool allowedToUsePaymentRequest(const Frame* frame) { |
| 427 // To determine whether a Document object |document| is allowed to use the | 362 // To determine whether a Document object |document| is allowed to use the |
| 428 // feature indicated by attribute name |allowpaymentrequest|, run these steps: | 363 // feature indicated by attribute name |allowpaymentrequest|, run these steps: |
| 429 | 364 |
| 430 // 1. If |document| has no browsing context, then return false. | 365 // 1. If |document| has no browsing context, then return false. |
| 431 if (!frame) | 366 if (!frame) |
| 432 return false; | 367 return false; |
| 433 | 368 |
| 434 // 2. If |document|'s browsing context is a top-level browsing context, then | 369 // 2. If |document|'s browsing context is a top-level browsing context, then |
| 435 // return true. | 370 // return true. |
| 436 if (frame->isMainFrame()) | 371 if (frame->isMainFrame()) |
| 437 return true; | 372 return true; |
| 438 | 373 |
| 439 // 3. If |document|'s browsing context has a browsing context container that | 374 // 3. If |document|'s browsing context has a browsing context container that |
| 440 // is an iframe element with an |allowpaymentrequest| attribute specified, and | 375 // is an iframe element with an |allowpaymentrequest| attribute specified, and |
| 441 // whose node document is allowed to use the feature indicated by | 376 // whose node document is allowed to use the feature indicated by |
| 442 // |allowpaymentrequest|, then return true. | 377 // |allowpaymentrequest|, then return true. |
| 443 HTMLFrameOwnerElement* ownerElement = toHTMLFrameOwnerElement(frame->owner()); | 378 HTMLFrameOwnerElement* ownerElement = toHTMLFrameOwnerElement(frame->owner()); |
| 444 if (ownerElement && isHTMLIFrameElement(ownerElement)) { | 379 if (ownerElement && isHTMLIFrameElement(ownerElement)) { |
| 445 HTMLIFrameElement* iframe = toHTMLIFrameElement(ownerElement); | 380 HTMLIFrameElement* iframe = toHTMLIFrameElement(ownerElement); |
| 446 if (HTMLIFrameElementPayments::from(*iframe).allowPaymentRequest(*iframe)) | 381 if (HTMLIFrameElementPayments::from(*iframe).allowPaymentRequest(*iframe)) |
| 447 return allowedToUsePaymentRequest(frame->tree().parent()); | 382 return allowedToUsePaymentRequest(frame->tree().parent()); |
| 448 } | 383 } |
| 449 | 384 |
| 450 // 4. Return false. | 385 // 4. Return false. |
| 451 return false; | 386 return false; |
| 452 } | 387 } |
| 453 | 388 |
| 454 WTF::Vector<mojom::blink::PaymentMethodDataPtr> ConvertPaymentMethodData( | |
| 455 const Vector<PaymentRequest::MethodData>& blinkMethods) { | |
| 456 WTF::Vector<mojom::blink::PaymentMethodDataPtr> mojoMethods( | |
| 457 blinkMethods.size()); | |
| 458 for (size_t i = 0; i < blinkMethods.size(); ++i) { | |
| 459 mojoMethods[i] = mojom::blink::PaymentMethodData::New(); | |
| 460 mojoMethods[i]->supported_methods = | |
| 461 WTF::Vector<WTF::String>(blinkMethods[i].supportedMethods); | |
| 462 mojoMethods[i]->stringified_data = blinkMethods[i].stringifiedData; | |
| 463 } | |
| 464 return mojoMethods; | |
| 465 } | |
| 466 | |
| 467 } // namespace | 389 } // namespace |
| 468 | 390 |
| 469 PaymentRequest* PaymentRequest::create( | 391 PaymentRequest* PaymentRequest::create( |
| 470 ScriptState* scriptState, | 392 ScriptState* scriptState, |
| 471 const HeapVector<PaymentMethodData>& methodData, | 393 const HeapVector<PaymentMethodData>& methodData, |
| 472 const PaymentDetails& details, | 394 const PaymentDetails& details, |
| 473 ExceptionState& exceptionState) { | 395 ExceptionState& exceptionState) { |
| 474 return new PaymentRequest(scriptState, methodData, details, PaymentOptions(), | 396 return new PaymentRequest(scriptState, methodData, details, PaymentOptions(), |
| 475 exceptionState); | 397 exceptionState); |
| 476 } | 398 } |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 574 V8PaymentDetails::toImpl(detailsScriptValue.isolate(), | 496 V8PaymentDetails::toImpl(detailsScriptValue.isolate(), |
| 575 detailsScriptValue.v8Value(), details, | 497 detailsScriptValue.v8Value(), details, |
| 576 exceptionState); | 498 exceptionState); |
| 577 if (exceptionState.hadException()) { | 499 if (exceptionState.hadException()) { |
| 578 m_showResolver->reject( | 500 m_showResolver->reject( |
| 579 DOMException::create(SyntaxError, exceptionState.message())); | 501 DOMException::create(SyntaxError, exceptionState.message())); |
| 580 clearResolversAndCloseMojoConnection(); | 502 clearResolversAndCloseMojoConnection(); |
| 581 return; | 503 return; |
| 582 } | 504 } |
| 583 | 505 |
| 584 bool keepShippingOptions = validatePaymentDetails(details, exceptionState); | 506 mojom::blink::PaymentDetailsPtr validatedDetails = |
| 507 mojom::blink::PaymentDetails::New(); | |
| 508 validateAndConvertPaymentDetails(details, m_options.requestShipping(), | |
| 509 validatedDetails, exceptionState); | |
| 585 if (exceptionState.hadException()) { | 510 if (exceptionState.hadException()) { |
| 586 m_showResolver->reject( | 511 m_showResolver->reject( |
| 587 DOMException::create(SyntaxError, exceptionState.message())); | 512 DOMException::create(SyntaxError, exceptionState.message())); |
| 588 clearResolversAndCloseMojoConnection(); | 513 clearResolversAndCloseMojoConnection(); |
| 589 return; | 514 return; |
| 590 } | 515 } |
| 591 | 516 |
| 592 if (m_options.requestShipping()) { | 517 if (m_options.requestShipping()) |
| 593 if (keepShippingOptions) | 518 m_shippingOption = getSelectedShippingOption(validatedDetails); |
| 594 m_shippingOption = getSelectedShippingOption(details); | |
| 595 else | |
| 596 m_shippingOption = String(); | |
| 597 } | |
| 598 | 519 |
| 599 m_paymentProvider->UpdateWith(maybeKeepShippingOptions( | 520 m_paymentProvider->UpdateWith(std::move(validatedDetails)); |
| 600 mojom::blink::PaymentDetails::From(details), keepShippingOptions)); | |
| 601 } | 521 } |
| 602 | 522 |
| 603 void PaymentRequest::onUpdatePaymentDetailsFailure(const String& error) { | 523 void PaymentRequest::onUpdatePaymentDetailsFailure(const String& error) { |
| 604 if (m_showResolver) | 524 if (m_showResolver) |
| 605 m_showResolver->reject(DOMException::create(AbortError, error)); | 525 m_showResolver->reject(DOMException::create(AbortError, error)); |
| 606 if (m_completeResolver) | 526 if (m_completeResolver) |
| 607 m_completeResolver->reject(DOMException::create(AbortError, error)); | 527 m_completeResolver->reject(DOMException::create(AbortError, error)); |
| 608 clearResolversAndCloseMojoConnection(); | 528 clearResolversAndCloseMojoConnection(); |
| 609 } | 529 } |
| 610 | 530 |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 626 PaymentRequest::PaymentRequest(ScriptState* scriptState, | 546 PaymentRequest::PaymentRequest(ScriptState* scriptState, |
| 627 const HeapVector<PaymentMethodData>& methodData, | 547 const HeapVector<PaymentMethodData>& methodData, |
| 628 const PaymentDetails& details, | 548 const PaymentDetails& details, |
| 629 const PaymentOptions& options, | 549 const PaymentOptions& options, |
| 630 ExceptionState& exceptionState) | 550 ExceptionState& exceptionState) |
| 631 : ContextLifecycleObserver(scriptState->getExecutionContext()), | 551 : ContextLifecycleObserver(scriptState->getExecutionContext()), |
| 632 ActiveScriptWrappable(this), | 552 ActiveScriptWrappable(this), |
| 633 m_options(options), | 553 m_options(options), |
| 634 m_clientBinding(this), | 554 m_clientBinding(this), |
| 635 m_completeTimer(this, &PaymentRequest::onCompleteTimeout) { | 555 m_completeTimer(this, &PaymentRequest::onCompleteTimeout) { |
| 636 Vector<MethodData> validatedMethodData; | 556 Vector<mojom::blink::PaymentMethodDataPtr> validatedMethodData; |
| 637 validateAndConvertPaymentMethodData(methodData, &validatedMethodData, | 557 validateAndConvertPaymentMethodData(methodData, validatedMethodData, |
| 638 exceptionState); | 558 exceptionState); |
| 639 if (exceptionState.hadException()) | 559 if (exceptionState.hadException()) |
| 640 return; | 560 return; |
| 641 | 561 |
| 642 if (!scriptState->getExecutionContext()->isSecureContext()) { | 562 if (!scriptState->getExecutionContext()->isSecureContext()) { |
| 643 exceptionState.throwSecurityError("Must be in a secure context"); | 563 exceptionState.throwSecurityError("Must be in a secure context"); |
| 644 return; | 564 return; |
| 645 } | 565 } |
| 646 | 566 |
| 647 if (!allowedToUsePaymentRequest(scriptState->domWindow()->frame())) { | 567 if (!allowedToUsePaymentRequest(scriptState->domWindow()->frame())) { |
| 648 exceptionState.throwSecurityError( | 568 exceptionState.throwSecurityError( |
| 649 "Must be in a top-level browsing context or an iframe needs to specify " | 569 "Must be in a top-level browsing context or an iframe needs to specify " |
| 650 "'allowpaymentrequest' explicitly"); | 570 "'allowpaymentrequest' explicitly"); |
| 651 return; | 571 return; |
| 652 } | 572 } |
| 653 | 573 |
| 654 bool keepShippingOptions = validatePaymentDetails(details, exceptionState); | 574 mojom::blink::PaymentDetailsPtr validatedDetails = |
| 575 mojom::blink::PaymentDetails::New(); | |
| 576 validateAndConvertPaymentDetails(details, m_options.requestShipping(), | |
| 577 validatedDetails, exceptionState); | |
| 655 if (exceptionState.hadException()) | 578 if (exceptionState.hadException()) |
| 656 return; | 579 return; |
| 657 | 580 |
| 658 if (details.hasError() && !details.error().isEmpty()) { | 581 if (!validatedDetails->error.isEmpty()) { |
| 659 exceptionState.throwTypeError("Error value should be empty"); | 582 exceptionState.throwTypeError("Error value should be empty"); |
| 660 return; | 583 return; |
| 661 } | 584 } |
| 662 | 585 |
| 663 if (m_options.requestShipping()) { | 586 if (m_options.requestShipping()) { |
| 664 if (keepShippingOptions) | 587 m_shippingOption = getSelectedShippingOption(validatedDetails); |
| 665 m_shippingOption = getSelectedShippingOption(details); | |
| 666 m_shippingType = getValidShippingType(m_options.shippingType()); | 588 m_shippingType = getValidShippingType(m_options.shippingType()); |
| 667 } | 589 } |
| 668 | 590 |
| 669 scriptState->domWindow()->frame()->interfaceProvider()->getInterface( | 591 scriptState->domWindow()->frame()->interfaceProvider()->getInterface( |
| 670 mojo::GetProxy(&m_paymentProvider)); | 592 mojo::GetProxy(&m_paymentProvider)); |
| 671 m_paymentProvider.set_connection_error_handler(convertToBaseCallback( | 593 m_paymentProvider.set_connection_error_handler(convertToBaseCallback( |
| 672 WTF::bind(&PaymentRequest::OnError, wrapWeakPersistent(this), | 594 WTF::bind(&PaymentRequest::OnError, wrapWeakPersistent(this), |
| 673 mojom::blink::PaymentErrorReason::UNKNOWN))); | 595 mojom::blink::PaymentErrorReason::UNKNOWN))); |
| 674 m_paymentProvider->Init( | 596 m_paymentProvider->Init(m_clientBinding.CreateInterfacePtrAndBind(), |
| 675 m_clientBinding.CreateInterfacePtrAndBind(), | 597 std::move(validatedMethodData), |
| 676 ConvertPaymentMethodData(validatedMethodData), | 598 std::move(validatedDetails), |
| 677 maybeKeepShippingOptions( | 599 mojom::blink::PaymentOptions::From(m_options)); |
| 678 mojom::blink::PaymentDetails::From(details), | |
| 679 keepShippingOptions && m_options.requestShipping()), | |
| 680 mojom::blink::PaymentOptions::From(m_options)); | |
| 681 } | 600 } |
| 682 | 601 |
| 683 void PaymentRequest::contextDestroyed() { | 602 void PaymentRequest::contextDestroyed() { |
| 684 clearResolversAndCloseMojoConnection(); | 603 clearResolversAndCloseMojoConnection(); |
| 685 } | 604 } |
| 686 | 605 |
| 687 void PaymentRequest::OnShippingAddressChange( | 606 void PaymentRequest::OnShippingAddressChange( |
| 688 mojom::blink::PaymentAddressPtr address) { | 607 mojom::blink::PaymentAddressPtr address) { |
| 689 DCHECK(m_showResolver); | 608 DCHECK(m_showResolver); |
| 690 DCHECK(!m_completeResolver); | 609 DCHECK(!m_completeResolver); |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 764 m_completeTimer.startOneShot(completeTimeoutSeconds, BLINK_FROM_HERE); | 683 m_completeTimer.startOneShot(completeTimeoutSeconds, BLINK_FROM_HERE); |
| 765 | 684 |
| 766 m_showResolver->resolve(new PaymentResponse(std::move(response), this)); | 685 m_showResolver->resolve(new PaymentResponse(std::move(response), this)); |
| 767 | 686 |
| 768 // Do not close the mojo connection here. The merchant website should call | 687 // Do not close the mojo connection here. The merchant website should call |
| 769 // PaymentResponse::complete(String), which will be forwarded over the mojo | 688 // PaymentResponse::complete(String), which will be forwarded over the mojo |
| 770 // connection to display a success or failure message to the user. | 689 // connection to display a success or failure message to the user. |
| 771 m_showResolver.clear(); | 690 m_showResolver.clear(); |
| 772 } | 691 } |
| 773 | 692 |
| 774 void PaymentRequest::OnError(mojo::PaymentErrorReason error) { | 693 void PaymentRequest::OnError(mojom::blink::PaymentErrorReason error) { |
| 775 if (!Platform::current()) { | 694 if (!Platform::current()) { |
| 776 // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. | 695 // TODO(rockot): Clean this up once renderer shutdown sequence is fixed. |
| 777 return; | 696 return; |
| 778 } | 697 } |
| 779 | 698 |
| 780 bool isError = false; | 699 bool isError = false; |
| 781 ExceptionCode ec = UnknownError; | 700 ExceptionCode ec = UnknownError; |
| 782 String message; | 701 String message; |
| 783 | 702 |
| 784 switch (error) { | 703 switch (error) { |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 852 m_completeTimer.stop(); | 771 m_completeTimer.stop(); |
| 853 m_completeResolver.clear(); | 772 m_completeResolver.clear(); |
| 854 m_showResolver.clear(); | 773 m_showResolver.clear(); |
| 855 m_abortResolver.clear(); | 774 m_abortResolver.clear(); |
| 856 if (m_clientBinding.is_bound()) | 775 if (m_clientBinding.is_bound()) |
| 857 m_clientBinding.Close(); | 776 m_clientBinding.Close(); |
| 858 m_paymentProvider.reset(); | 777 m_paymentProvider.reset(); |
| 859 } | 778 } |
| 860 | 779 |
| 861 } // namespace blink | 780 } // namespace blink |
| OLD | NEW |