| 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/ScriptState.h" | 10 #include "bindings/core/v8/ScriptState.h" | 
|  | 11 #include "core/EventTypeNames.h" | 
| 10 #include "core/dom/DOMException.h" | 12 #include "core/dom/DOMException.h" | 
| 11 #include "core/dom/ExceptionCode.h" | 13 #include "core/dom/ExceptionCode.h" | 
|  | 14 #include "core/events/Event.h" | 
|  | 15 #include "core/events/EventQueue.h" | 
| 12 #include "modules/EventTargetModulesNames.h" | 16 #include "modules/EventTargetModulesNames.h" | 
|  | 17 #include "modules/payments/PaymentItem.h" | 
|  | 18 #include "modules/payments/PaymentResponse.h" | 
|  | 19 #include "modules/payments/PaymentsValidators.h" | 
| 13 #include "modules/payments/ShippingAddress.h" | 20 #include "modules/payments/ShippingAddress.h" | 
|  | 21 #include "modules/payments/ShippingOption.h" | 
|  | 22 #include "mojo/public/cpp/bindings/interface_request.h" | 
|  | 23 #include "mojo/public/cpp/bindings/wtf_array.h" | 
|  | 24 #include "platform/MojoHelper.h" | 
|  | 25 #include "public/platform/Platform.h" | 
|  | 26 #include <utility> | 
|  | 27 | 
|  | 28 namespace mojo { | 
|  | 29 | 
|  | 30 template <> | 
|  | 31 struct TypeConverter<mojom::wtf::CurrencyAmountPtr, blink::CurrencyAmount> { | 
|  | 32     static mojom::wtf::CurrencyAmountPtr Convert(const blink::CurrencyAmount& in
     put) | 
|  | 33     { | 
|  | 34         mojom::wtf::CurrencyAmountPtr output = mojom::wtf::CurrencyAmount::New()
     ; | 
|  | 35         output->currency_code = input.currencyCode(); | 
|  | 36         output->value = input.value(); | 
|  | 37         return output; | 
|  | 38     } | 
|  | 39 }; | 
|  | 40 | 
|  | 41 template <> | 
|  | 42 struct TypeConverter<mojom::wtf::PaymentItemPtr, blink::PaymentItem> { | 
|  | 43     static mojom::wtf::PaymentItemPtr Convert(const blink::PaymentItem& input) | 
|  | 44     { | 
|  | 45         mojom::wtf::PaymentItemPtr output = mojom::wtf::PaymentItem::New(); | 
|  | 46         output->id = input.id(); | 
|  | 47         output->label = input.label(); | 
|  | 48         output->amount = mojom::wtf::CurrencyAmount::From(input.amount()); | 
|  | 49         return output; | 
|  | 50     } | 
|  | 51 }; | 
|  | 52 | 
|  | 53 template <> | 
|  | 54 struct TypeConverter<mojom::wtf::ShippingOptionPtr, blink::ShippingOption> { | 
|  | 55     static mojom::wtf::ShippingOptionPtr Convert(const blink::ShippingOption& in
     put) | 
|  | 56     { | 
|  | 57         mojom::wtf::ShippingOptionPtr output = mojom::wtf::ShippingOption::New()
     ; | 
|  | 58         output->id = input.id(); | 
|  | 59         output->label = input.label(); | 
|  | 60         output->amount = mojom::wtf::CurrencyAmount::From(input.amount()); | 
|  | 61         return output; | 
|  | 62     } | 
|  | 63 }; | 
|  | 64 | 
|  | 65 template <> | 
|  | 66 struct TypeConverter<mojom::wtf::PaymentDetailsPtr, blink::PaymentDetails> { | 
|  | 67     static mojom::wtf::PaymentDetailsPtr Convert(const blink::PaymentDetails& in
     put) | 
|  | 68     { | 
|  | 69         mojom::wtf::PaymentDetailsPtr output = mojom::wtf::PaymentDetails::New()
     ; | 
|  | 70         output->items = mojo::WTFArray<mojom::wtf::PaymentItemPtr>::From(input.i
     tems()); | 
|  | 71         if (input.hasShippingOptions()) | 
|  | 72             output->shipping_options = mojo::WTFArray<mojom::wtf::ShippingOption
     Ptr>::From(input.shippingOptions()); | 
|  | 73         else | 
|  | 74             output->shipping_options = mojo::WTFArray<mojom::wtf::ShippingOption
     Ptr>::New(0); | 
|  | 75         return output; | 
|  | 76     } | 
|  | 77 }; | 
|  | 78 | 
|  | 79 template <> | 
|  | 80 struct TypeConverter<mojom::wtf::PaymentOptionsPtr, blink::PaymentOptions> { | 
|  | 81     static mojom::wtf::PaymentOptionsPtr Convert(const blink::PaymentOptions& in
     put) | 
|  | 82     { | 
|  | 83         mojom::wtf::PaymentOptionsPtr output = mojom::wtf::PaymentOptions::New()
     ; | 
|  | 84         output->request_shipping = input.requestShipping(); | 
|  | 85         return output; | 
|  | 86     } | 
|  | 87 }; | 
|  | 88 | 
|  | 89 } // namespace mojo | 
| 14 | 90 | 
| 15 namespace blink { | 91 namespace blink { | 
|  | 92 namespace { | 
| 16 | 93 | 
| 17 // static | 94 // Validates ShippingOption and PaymentItem dictionaries, which happen to have i
     dentical fields. | 
|  | 95 template <typename T> | 
|  | 96 void validateShippingOptionsOrPaymentItems(HeapVector<T> items, ExceptionState& 
     exceptionState) | 
|  | 97 { | 
|  | 98     String errorMessage; | 
|  | 99     for (const auto& item : items) { | 
|  | 100         if (!item.hasId()) { | 
|  | 101             exceptionState.throwTypeError("Item id required"); | 
|  | 102             return; | 
|  | 103         } | 
|  | 104 | 
|  | 105         if (!item.hasLabel()) { | 
|  | 106             exceptionState.throwTypeError("Item label required"); | 
|  | 107             return; | 
|  | 108         } | 
|  | 109 | 
|  | 110         if (!item.hasAmount()) { | 
|  | 111             exceptionState.throwTypeError("Currency amount required"); | 
|  | 112             return; | 
|  | 113         } | 
|  | 114 | 
|  | 115         if (!item.amount().hasCurrencyCode()) { | 
|  | 116             exceptionState.throwTypeError("Currency code required"); | 
|  | 117             return; | 
|  | 118         } | 
|  | 119 | 
|  | 120         if (!item.amount().hasValue()) { | 
|  | 121             exceptionState.throwTypeError("Currency value required"); | 
|  | 122             return; | 
|  | 123         } | 
|  | 124 | 
|  | 125         if (!PaymentsValidators::isValidCurrencyCodeFormat(item.amount().currenc
     yCode(), &errorMessage)) { | 
|  | 126             exceptionState.throwTypeError(errorMessage); | 
|  | 127             return; | 
|  | 128         } | 
|  | 129 | 
|  | 130         if (!PaymentsValidators::isValidAmountFormat(item.amount().value(), &err
     orMessage)) { | 
|  | 131             exceptionState.throwTypeError(errorMessage); | 
|  | 132             return; | 
|  | 133         } | 
|  | 134     } | 
|  | 135 } | 
|  | 136 | 
|  | 137 } // namespace | 
|  | 138 | 
| 18 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St
     ring>& supportedMethods, const PaymentDetails& details, ExceptionState& exceptio
     nState) | 139 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St
     ring>& supportedMethods, const PaymentDetails& details, ExceptionState& exceptio
     nState) | 
| 19 { | 140 { | 
| 20     return new PaymentRequest(scriptState, supportedMethods, details, PaymentOpt
     ions(), ScriptValue(), exceptionState); | 141     return new PaymentRequest(scriptState, supportedMethods, details, PaymentOpt
     ions(), ScriptValue(), exceptionState); | 
| 21 } | 142 } | 
| 22 | 143 | 
| 23 // static |  | 
| 24 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St
     ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op
     tions, ExceptionState& exceptionState) | 144 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St
     ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op
     tions, ExceptionState& exceptionState) | 
| 25 { | 145 { | 
| 26     return new PaymentRequest(scriptState, supportedMethods, details, options, S
     criptValue(), exceptionState); | 146     return new PaymentRequest(scriptState, supportedMethods, details, options, S
     criptValue(), exceptionState); | 
| 27 } | 147 } | 
| 28 | 148 | 
| 29 // static |  | 
| 30 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St
     ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op
     tions, const ScriptValue& data, ExceptionState& exceptionState) | 149 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St
     ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op
     tions, const ScriptValue& data, ExceptionState& exceptionState) | 
| 31 { | 150 { | 
| 32     return new PaymentRequest(scriptState, supportedMethods, details, options, d
     ata, exceptionState); | 151     return new PaymentRequest(scriptState, supportedMethods, details, options, d
     ata, exceptionState); | 
| 33 } | 152 } | 
| 34 | 153 | 
| 35 PaymentRequest::~PaymentRequest() | 154 PaymentRequest::~PaymentRequest() | 
| 36 { | 155 { | 
| 37 } | 156 } | 
| 38 | 157 | 
| 39 ScriptPromise PaymentRequest::show(ScriptState* scriptState) | 158 ScriptPromise PaymentRequest::show(ScriptState* scriptState) | 
| 40 { | 159 { | 
| 41     return ScriptPromise::rejectWithDOMException(scriptState, DOMException::crea
     te(NotSupportedError, "Not implemented.")); | 160     if (m_showResolver) | 
|  | 161         return ScriptPromise::rejectWithDOMException(scriptState, DOMException::
     create(InvalidStateError, "Already called show() once")); | 
|  | 162 | 
|  | 163     DCHECK(!m_paymentProvider.is_bound()); | 
|  | 164     blink::Platform::current()->connectToRemoteService(mojo::GetProxy(&m_payment
     Provider)); | 
|  | 165     m_paymentProvider.set_connection_error_handler(sameThreadBindForMojo(&Paymen
     tRequest::OnError, this)); | 
|  | 166     m_paymentProvider->SetClient(m_clientBinding.CreateInterfacePtrAndBind()); | 
|  | 167     m_paymentProvider->Show(std::move(m_supportedMethods), mojom::wtf::PaymentDe
     tails::From(m_details), mojom::wtf::PaymentOptions::From(m_options), m_stringifi
     edData.isNull() ? "" : m_stringifiedData); | 
|  | 168 | 
|  | 169     m_showResolver = ScriptPromiseResolver::create(scriptState); | 
|  | 170     return m_showResolver->promise(); | 
| 42 } | 171 } | 
| 43 | 172 | 
| 44 void PaymentRequest::abort() | 173 void PaymentRequest::abort(ExceptionState& exceptionState) | 
| 45 { | 174 { | 
|  | 175     if (!m_showResolver) { | 
|  | 176         exceptionState.throwDOMException(InvalidStateError, "Never called show()
     , so nothing to abort"); | 
|  | 177         return; | 
|  | 178     } | 
|  | 179 | 
|  | 180     m_paymentProvider->Abort(); | 
| 46 } | 181 } | 
| 47 | 182 | 
| 48 const AtomicString& PaymentRequest::interfaceName() const | 183 const AtomicString& PaymentRequest::interfaceName() const | 
| 49 { | 184 { | 
| 50     return EventTargetNames::PaymentRequest; | 185     return EventTargetNames::PaymentRequest; | 
| 51 } | 186 } | 
| 52 | 187 | 
| 53 ExecutionContext* PaymentRequest::getExecutionContext() const | 188 ExecutionContext* PaymentRequest::getExecutionContext() const | 
| 54 { | 189 { | 
| 55     return m_scriptState->getExecutionContext(); | 190     return m_scriptState->getExecutionContext(); | 
| 56 } | 191 } | 
| 57 | 192 | 
|  | 193 ScriptPromise PaymentRequest::complete(ScriptState* scriptState, bool success) | 
|  | 194 { | 
|  | 195     if (m_completeResolver) | 
|  | 196         return ScriptPromise::rejectWithDOMException(scriptState, DOMException::
     create(InvalidStateError, "Already called complete() once")); | 
|  | 197 | 
|  | 198     m_completeResolver = ScriptPromiseResolver::create(scriptState); | 
|  | 199     m_paymentProvider->Complete(success); | 
|  | 200 | 
|  | 201     return m_completeResolver->promise(); | 
|  | 202 } | 
|  | 203 | 
| 58 DEFINE_TRACE(PaymentRequest) | 204 DEFINE_TRACE(PaymentRequest) | 
| 59 { | 205 { | 
| 60     visitor->trace(m_details); | 206     visitor->trace(m_details); | 
| 61     visitor->trace(m_options); | 207     visitor->trace(m_options); | 
| 62     visitor->trace(m_shippingAddress); | 208     visitor->trace(m_shippingAddress); | 
|  | 209     visitor->trace(m_showResolver); | 
|  | 210     visitor->trace(m_completeResolver); | 
| 63     RefCountedGarbageCollectedEventTargetWithInlineData<PaymentRequest>::trace(v
     isitor); | 211     RefCountedGarbageCollectedEventTargetWithInlineData<PaymentRequest>::trace(v
     isitor); | 
| 64 } | 212 } | 
| 65 | 213 | 
| 66 PaymentRequest::PaymentRequest(ScriptState* scriptState, const Vector<String>& s
     upportedMethods, const PaymentDetails& details, const PaymentOptions& options, c
     onst ScriptValue& data, ExceptionState& exceptionState) | 214 PaymentRequest::PaymentRequest(ScriptState* scriptState, const Vector<String>& s
     upportedMethods, const PaymentDetails& details, const PaymentOptions& options, c
     onst ScriptValue& data, ExceptionState& exceptionState) | 
| 67     : m_scriptState(scriptState) | 215     : m_scriptState(scriptState) | 
| 68     , m_supportedMethods(supportedMethods) | 216     , m_supportedMethods(supportedMethods) | 
| 69     , m_details(details) | 217     , m_details(details) | 
| 70     , m_options(options) | 218     , m_options(options) | 
|  | 219     , m_clientBinding(this) | 
| 71 { | 220 { | 
|  | 221     // TODO(rouslan): Also check for a top-level browsing context. | 
|  | 222     // https://github.com/w3c/browser-payment-api/issues/2 | 
|  | 223     if (!scriptState->getExecutionContext()->isSecureContext()) { | 
|  | 224         exceptionState.throwSecurityError("Must be in a secure context"); | 
|  | 225         return; | 
|  | 226     } | 
|  | 227 | 
|  | 228     if (supportedMethods.isEmpty()) { | 
|  | 229         exceptionState.throwTypeError("Must specify at least one payment method 
     identifier"); | 
|  | 230         return; | 
|  | 231     } | 
|  | 232 | 
|  | 233     if (!details.hasItems()) { | 
|  | 234         exceptionState.throwTypeError("Must specify items"); | 
|  | 235         return; | 
|  | 236     } | 
|  | 237 | 
|  | 238     if (details.items().isEmpty()) { | 
|  | 239         exceptionState.throwTypeError("Must specify at least one item"); | 
|  | 240         return; | 
|  | 241     } | 
|  | 242 | 
|  | 243     validateShippingOptionsOrPaymentItems(details.items(), exceptionState); | 
|  | 244     if (exceptionState.hadException()) | 
|  | 245         return; | 
|  | 246 | 
|  | 247     if (details.hasShippingOptions()) { | 
|  | 248         validateShippingOptionsOrPaymentItems(details.shippingOptions(), excepti
     onState); | 
|  | 249         if (exceptionState.hadException()) | 
|  | 250             return; | 
|  | 251     } | 
|  | 252 | 
| 72     if (!data.isEmpty()) { | 253     if (!data.isEmpty()) { | 
| 73         RefPtr<JSONValue> value = toJSONValue(data.context(), data.v8Value()); | 254         RefPtr<JSONValue> value = toJSONValue(data.context(), data.v8Value()); | 
| 74         if (value && value->getType() == JSONValue::TypeObject) | 255         if (!value) { | 
| 75             m_stringifiedData = JSONObject::cast(value)->toJSONString(); | 256             exceptionState.throwTypeError("Unable to parse payment method specif
     ic data"); | 
|  | 257             return; | 
|  | 258         } | 
|  | 259         if (!value->isNull()) { | 
|  | 260             if (value->getType() != JSONValue::TypeObject) { | 
|  | 261                 exceptionState.throwTypeError("Payment method specific data shou
     ld be a JSON-serializable object"); | 
|  | 262                 return; | 
|  | 263             } | 
|  | 264 | 
|  | 265             RefPtr<JSONObject> jsonData = JSONObject::cast(value); | 
|  | 266             for (const auto& paymentMethodSpecificKeyValue : *jsonData) { | 
|  | 267                 if (!supportedMethods.contains(paymentMethodSpecificKeyValue.key
     )) { | 
|  | 268                     exceptionState.throwTypeError("'" + paymentMethodSpecificKey
     Value.key + "' should match one of the payment method identifiers"); | 
|  | 269                     return; | 
|  | 270                 } | 
|  | 271                 if (paymentMethodSpecificKeyValue.value->getType() != JSONValue:
     :TypeObject) { | 
|  | 272                     exceptionState.throwTypeError("Data for '" + paymentMethodSp
     ecificKeyValue.key + "' should be a JSON-serializable object"); | 
|  | 273                     return; | 
|  | 274                 } | 
|  | 275             } | 
|  | 276 | 
|  | 277             m_stringifiedData = jsonData->toJSONString(); | 
|  | 278         } | 
| 76     } | 279     } | 
|  | 280 | 
|  | 281     // Set the currently selected option if only one option was passed. | 
|  | 282     if (details.hasShippingOptions() && details.shippingOptions().size() == 1) | 
|  | 283         m_shippingOption = details.shippingOptions().begin()->id(); | 
|  | 284 } | 
|  | 285 | 
|  | 286 void PaymentRequest::OnShippingAddressChange(mojom::wtf::ShippingAddressPtr addr
     ess) | 
|  | 287 { | 
|  | 288     DCHECK(m_showResolver); | 
|  | 289     DCHECK(!m_completeResolver); | 
|  | 290 | 
|  | 291     String errorMessage; | 
|  | 292     if (!PaymentsValidators::isValidRegionCodeFormat(address->region_code, &erro
     rMessage) | 
|  | 293         || !PaymentsValidators::isValidLanguageCodeFormat(address->language_code
     , &errorMessage) | 
|  | 294         || !PaymentsValidators::isValidScriptCodeFormat(address->script_code, &e
     rrorMessage)) { | 
|  | 295         m_showResolver->reject(DOMException::create(SyntaxError, errorMessage)); | 
|  | 296         cleanUp(); | 
|  | 297         return; | 
|  | 298     } | 
|  | 299 | 
|  | 300     if (address->language_code.isEmpty() && !address->script_code.isEmpty()) { | 
|  | 301         m_showResolver->reject(DOMException::create(SyntaxError, "If language co
     de is empty, then script code should also be empty")); | 
|  | 302         cleanUp(); | 
|  | 303         return; | 
|  | 304     } | 
|  | 305 | 
|  | 306     m_shippingAddress = new ShippingAddress(std::move(address)); | 
|  | 307     RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::shippingaddr
     esschange); | 
|  | 308     event->setTarget(this); | 
|  | 309     getExecutionContext()->getEventQueue()->enqueueEvent(event); | 
|  | 310 } | 
|  | 311 | 
|  | 312 void PaymentRequest::OnShippingOptionChange(const String& shippingOptionId) | 
|  | 313 { | 
|  | 314     DCHECK(m_showResolver); | 
|  | 315     DCHECK(!m_completeResolver); | 
|  | 316     m_shippingOption = shippingOptionId; | 
|  | 317     RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::shippingopti
     onchange); | 
|  | 318     event->setTarget(this); | 
|  | 319     getExecutionContext()->getEventQueue()->enqueueEvent(event); | 
|  | 320 } | 
|  | 321 | 
|  | 322 void PaymentRequest::OnPaymentResponse(mojom::wtf::PaymentResponsePtr response) | 
|  | 323 { | 
|  | 324     DCHECK(m_showResolver); | 
|  | 325     DCHECK(!m_completeResolver); | 
|  | 326     m_showResolver->resolve(new PaymentResponse(std::move(response), this)); | 
|  | 327 } | 
|  | 328 | 
|  | 329 void PaymentRequest::OnError() | 
|  | 330 { | 
|  | 331     if (m_completeResolver) | 
|  | 332         m_completeResolver->reject(DOMException::create(SyntaxError, "Request ca
     ncelled")); | 
|  | 333     if (m_showResolver) | 
|  | 334         m_showResolver->reject(DOMException::create(SyntaxError, "Request cancel
     led")); | 
|  | 335     cleanUp(); | 
|  | 336 } | 
|  | 337 | 
|  | 338 void PaymentRequest::OnComplete() | 
|  | 339 { | 
|  | 340     DCHECK(m_completeResolver); | 
|  | 341     m_completeResolver->resolve(); | 
|  | 342     cleanUp(); | 
|  | 343 } | 
|  | 344 | 
|  | 345 void PaymentRequest::cleanUp() | 
|  | 346 { | 
|  | 347     m_completeResolver.clear(); | 
|  | 348     m_showResolver.clear(); | 
|  | 349     m_clientBinding.Close(); | 
|  | 350     m_paymentProvider.reset(); | 
| 77 } | 351 } | 
| 78 | 352 | 
| 79 } // namespace blink | 353 } // namespace blink | 
| OLD | NEW | 
|---|