Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Side by Side Diff: third_party/WebKit/Source/modules/payments/PaymentRequest.cpp

Issue 1753543002: PaymentRequest Mojo bindings. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@interface
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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"
13 #include "modules/payments/ShippingAddress.h" 19 #include "modules/payments/ShippingAddress.h"
20 #include "modules/payments/ShippingOption.h"
21 #include "mojo/public/cpp/bindings/interface_request.h"
22 #include "public/platform/Platform.h"
23 #include <utility>
14 24
15 namespace blink { 25 namespace blink {
26 namespace {
27
28 template <typename OutputPtr, typename OutputType, typename Input>
29 OutputPtr toMojoItem(const Input& input)
30 {
31 OutputPtr output = OutputType::New();
32 output->id = input.id();
33 output->label = input.label();
34 output->amount = mojom::CurrencyAmount::New();
35 if (input.hasAmount()) {
36 output->amount->currency_code = input.amount().currencyCode();
37 output->amount->value = input.amount().value();
38 }
39 return output;
40 }
41
42 mojom::PaymentDetailsPtr toMojoPaymentDetails(const PaymentDetails& input)
43 {
44 mojom::PaymentDetailsPtr output = mojom::PaymentDetails::New();
45 output->items = mojo::WTFArray<mojom::PaymentItemPtr>::New(input.hasItems() ? input.items().size() : 0);
46 if (input.hasItems()) {
47 for (size_t i = 0; i < input.items().size(); ++i)
48 output->items[i] = toMojoItem<mojom::PaymentItemPtr, mojom::PaymentI tem, PaymentItem>(input.items()[i]);
49 }
50 output->shipping_options = mojo::WTFArray<mojom::ShippingOptionPtr>::New(inp ut.hasShippingOptions() ? input.shippingOptions().size() : 0);
51 if (input.hasShippingOptions()) {
52 for (size_t i = 0; i < input.shippingOptions().size(); ++i)
53 output->shipping_options[i] = toMojoItem<mojom::ShippingOptionPtr, m ojom::ShippingOption, ShippingOption>(input.shippingOptions()[i]);
54 }
55 return output;
56 }
57
58 mojom::PaymentOptionsPtr toMojoPaymentOptions(const PaymentOptions& input)
59 {
60 mojom::PaymentOptionsPtr output = mojom::PaymentOptions::New();
61 output->request_shipping = input.requestShipping();
62 return output;
63 }
64
65 bool isValidCurrencyCode(const String& code, ExceptionState& exceptionState)
66 {
67 if (code.length() != 3) {
68 exceptionState.throwTypeError("'" + code + "' is not a valid ISO 4217 cu rrency code, should be 3 letters");
69 return false;
70 }
71
72 for (size_t i = 0; i < code.length(); ++i) {
73 if (code[i] < 'A' || code[i] > 'Z') {
74 exceptionState.throwTypeError("'" + code + "' is not a valid ISO 421 7 currency code, should be upper case letters [A-Z]");
75 return false;
76 }
77 }
78
79 return true;
80 }
81
82 bool isValidAmount(const String& amount, ExceptionState& exceptionState)
83 {
84 if (amount.isEmpty()) {
85 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 20022 2 CurrencyAnd30Amount currency amount, should be nonempty");
86 return false;
87 }
88
89 if (amount.length() > 32) {
90 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 20022 2 CurrencyAnd30Amount currency amount, should be at most 32 characters");
91 return false;
92 }
93
94 int numberOfPeriods = 0;
95 int numberOfDigits = 0;
96 int numberOfFractionDigits = 0;
97 for (size_t i = 0; i < amount.length(); ++i) {
98 if (i == 0 && amount[i] == '-')
99 continue;
100
101 bool isPeriod = amount[i] == '.';
102 bool isDigit = amount[i] >= '0' && amount[i] <= '9';
103 if (!isPeriod && !isDigit) {
104 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 2 00222 CurrencyAnd30Amount currency amount, should contain only '-', '.', and [0- 9]");
105 return false;
106 }
107
108 if (isPeriod)
109 ++numberOfPeriods;
110
111 if (isDigit) {
112 ++numberOfDigits;
113 if (numberOfPeriods > 0)
114 ++numberOfFractionDigits;
115 }
116 }
117
118 if (numberOfPeriods > 1) {
119 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 20022 2 CurrencyAnd30Amount currency amount, should contain only one '.'");
120 return false;
121 }
122
123 if (numberOfDigits > 30) {
124 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 20022 2 CurrencyAnd30Amount currency amount, should contain at most 30 digits");
125 return false;
126 }
127
128 if (numberOfFractionDigits > 10) {
129 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 20022 2 CurrencyAnd30Amount currency amount, should contain at most 10 fraction digits ");
130 return false;
131 }
132
133 return true;
134 }
135
136 template <typename T>
137 bool areValidCurrencyAndAmounts(HeapVector<T> items, ExceptionState& exceptionSt ate)
138 {
139 for (const auto& item : items) {
140 if (item.hasAmount()) {
141 if (item.amount().hasCurrencyCode() && !isValidCurrencyCode(item.amo unt().currencyCode(), exceptionState))
142 return false;
143
144 if (item.amount().hasValue() && isValidAmount(item.amount().value(), exceptionState))
145 return false;
146 }
147 }
148
149 return true;
150 }
151
152 } // namespace
16 153
17 // static 154 // static
18 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, ExceptionState& exceptio nState) 155 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, ExceptionState& exceptio nState)
19 { 156 {
20 return new PaymentRequest(scriptState, supportedMethods, details, PaymentOpt ions(), ScriptValue(), exceptionState); 157 return new PaymentRequest(scriptState, supportedMethods, details, PaymentOpt ions(), ScriptValue(), exceptionState);
21 } 158 }
22 159
23 // static 160 // static
24 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, ExceptionState& exceptionState) 161 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, ExceptionState& exceptionState)
25 { 162 {
26 return new PaymentRequest(scriptState, supportedMethods, details, options, S criptValue(), exceptionState); 163 return new PaymentRequest(scriptState, supportedMethods, details, options, S criptValue(), exceptionState);
27 } 164 }
28 165
29 // static 166 // static
30 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, const ScriptValue& data, ExceptionState& exceptionState) 167 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, const ScriptValue& data, ExceptionState& exceptionState)
31 { 168 {
32 return new PaymentRequest(scriptState, supportedMethods, details, options, d ata, exceptionState); 169 return new PaymentRequest(scriptState, supportedMethods, details, options, d ata, exceptionState);
33 } 170 }
34 171
35 PaymentRequest::~PaymentRequest() 172 PaymentRequest::~PaymentRequest()
36 { 173 {
37 } 174 }
38 175
39 ScriptPromise PaymentRequest::show(ScriptState* scriptState) 176 ScriptPromise PaymentRequest::show(ScriptState* scriptState)
40 { 177 {
41 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::crea te(NotSupportedError, "Not implemented.")); 178 if (m_showResolver)
179 return m_showResolver->promise().rejectWithDOMException(scriptState, DOM Exception::create(InvalidStateError, "Already called show() once"));
180
181 ASSERT(!m_paymentProvider.is_bound());
182 blink::Platform::current()->connectToRemoteService(mojo::GetProxy(&m_payment Provider));
183 if (!m_paymentProvider)
184 return m_showResolver->promise().rejectWithDOMException(scriptState, DOM Exception::create(SyntaxError, "Not implemented on this platform"));
185
186 m_paymentProvider->SetClient(m_clientBinding.CreateInterfacePtrAndBind());
187 m_paymentProvider->Show(std::move(m_supportedMethods), toMojoPaymentDetails( m_details), toMojoPaymentOptions(m_options), m_stringifiedData.isNull() ? "" : m _stringifiedData);
188
189 m_showResolver = ScriptPromiseResolver::create(scriptState);
190 return m_showResolver->promise();
42 } 191 }
43 192
44 void PaymentRequest::abort() 193 void PaymentRequest::abort(ExceptionState& exceptionState)
45 { 194 {
195 if (!m_showResolver) {
196 exceptionState.throwDOMException(InvalidStateError, "Never called show() , so nothing to abort");
197 return;
198 }
199
200 m_paymentProvider->Abort();
46 } 201 }
47 202
48 const AtomicString& PaymentRequest::interfaceName() const 203 const AtomicString& PaymentRequest::interfaceName() const
49 { 204 {
50 return EventTargetNames::PaymentRequest; 205 return EventTargetNames::PaymentRequest;
51 } 206 }
52 207
53 ExecutionContext* PaymentRequest::getExecutionContext() const 208 ExecutionContext* PaymentRequest::getExecutionContext() const
54 { 209 {
55 return m_scriptState->getExecutionContext(); 210 return m_scriptState->getExecutionContext();
56 } 211 }
57 212
213 ScriptPromise PaymentRequest::complete(ScriptState* scriptState, bool success)
214 {
215 if (m_completeResolver)
216 return m_completeResolver->promise().rejectWithDOMException(scriptState, DOMException::create(InvalidStateError, "Already called complete() once"));
217
218 m_completeResolver = ScriptPromiseResolver::create(scriptState);
219 m_paymentProvider->Complete(success);
220
221 return m_completeResolver->promise();
222 }
223
58 DEFINE_TRACE(PaymentRequest) 224 DEFINE_TRACE(PaymentRequest)
59 { 225 {
60 visitor->trace(m_details); 226 visitor->trace(m_details);
61 visitor->trace(m_options); 227 visitor->trace(m_options);
62 visitor->trace(m_shippingAddress); 228 visitor->trace(m_shippingAddress);
229 visitor->trace(m_showResolver);
230 visitor->trace(m_completeResolver);
63 RefCountedGarbageCollectedEventTargetWithInlineData<PaymentRequest>::trace(v isitor); 231 RefCountedGarbageCollectedEventTargetWithInlineData<PaymentRequest>::trace(v isitor);
64 } 232 }
65 233
66 PaymentRequest::PaymentRequest(ScriptState* scriptState, const Vector<String>& s upportedMethods, const PaymentDetails& details, const PaymentOptions& options, c onst ScriptValue& data, ExceptionState& exceptionState) 234 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) 235 : m_scriptState(scriptState)
68 , m_supportedMethods(supportedMethods) 236 , m_supportedMethods(supportedMethods)
69 , m_details(details) 237 , m_details(details)
70 , m_options(options) 238 , m_options(options)
239 , m_clientBinding(this)
71 { 240 {
241 // TODO(rouslan): Also check for a top-level browsing context.
242 // https://github.com/w3c/browser-payment-api/issues/2
243 if (!scriptState->getExecutionContext()->isSecureContext()) {
244 exceptionState.throwSecurityError("Must be in a secure context");
245 return;
246 }
247
248 if (supportedMethods.isEmpty()) {
249 exceptionState.throwTypeError("No payment methods identifiers provided") ;
250 return;
251 }
252
253 if (details.hasItems() && !areValidCurrencyAndAmounts(details.items(), excep tionState))
254 return;
255
256 if (details.hasShippingOptions() && !areValidCurrencyAndAmounts(details.ship pingOptions(), exceptionState))
257 return;
258
72 if (!data.isEmpty()) { 259 if (!data.isEmpty()) {
73 RefPtr<JSONValue> value = toJSONValue(data.context(), data.v8Value()); 260 RefPtr<JSONValue> value = toJSONValue(data.context(), data.v8Value());
74 if (value && value->getType() == JSONValue::TypeObject) 261 if (value && !value->isNull()) {
75 m_stringifiedData = JSONObject::cast(value)->toJSONString(); 262 if (value->getType() != JSONValue::TypeObject) {
263 exceptionState.throwTypeError("Payment method specific data shou ld be a JSON-serializable object");
264 return;
265 }
266
267 RefPtr<JSONObject> jsonData = JSONObject::cast(value);
268 for (const auto& paymentMethodSpecificKeyValue : *jsonData) {
269 if (!supportedMethods.contains(paymentMethodSpecificKeyValue.key )) {
270 exceptionState.throwTypeError("'" + paymentMethodSpecificKey Value.key + "' does not match one of the payment method identifiers");
271 return;
272 }
273 if (!paymentMethodSpecificKeyValue.value || paymentMethodSpecifi cKeyValue.value->isNull() || paymentMethodSpecificKeyValue.value->getType() != J SONValue::TypeObject) {
274 exceptionState.throwTypeError("Data for '" + paymentMethodSp ecificKeyValue.key + "' should be a JSON-serializable object");
275 return;
276 }
277 }
278
279 m_stringifiedData = jsonData->toJSONString();
280 }
76 } 281 }
282
283 if (details.hasShippingOptions() && details.shippingOptions().size() == 1 && details.shippingOptions()[0].hasId())
284 m_shippingOption = details.shippingOptions()[0].id();
285 }
286
287 void PaymentRequest::OnShippingAddressChange(mojom::ShippingAddressPtr address)
288 {
289 ASSERT(m_showResolver);
290 ASSERT(!m_completeResolver);
291 // TODO(rouslan): Should the merchant website be notified of invalid shippin g address
292 // from the browser or the payment app?
293 m_shippingAddress = new ShippingAddress(std::move(address));
294 if (!m_shippingAddress->isValidRegionCode()) {
295 m_showResolver->reject(DOMException::create(SyntaxError, "Invalid ISO 31 66 country code"));
296 return;
297 }
298 if (!m_shippingAddress->isValidLanguageCode()) {
299 m_showResolver->reject(DOMException::create(SyntaxError, "Invalid ISO 63 9 language code"));
300 return;
301 }
302 if (!m_shippingAddress->isValidScriptCode()) {
303 m_showResolver->reject(DOMException::create(SyntaxError, "Invalid ISO 15 24 script code"));
304 return;
305 }
306 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::shippingaddr esschange);
307 event->setTarget(this);
308 getExecutionContext()->getEventQueue()->enqueueEvent(event);
309 }
310
311 void PaymentRequest::OnShippingOptionChange(const String& shippingOptionId)
312 {
313 ASSERT(m_showResolver);
314 ASSERT(!m_completeResolver);
315 m_shippingOption = shippingOptionId;
316 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::shippingopti onchange);
317 event->setTarget(this);
318 getExecutionContext()->getEventQueue()->enqueueEvent(event);
319 }
320
321 void PaymentRequest::OnPaymentResponse(mojom::PaymentResponsePtr response)
322 {
323 ASSERT(m_showResolver);
324 ASSERT(!m_completeResolver);
325 m_showResolver->resolve(new PaymentResponse(std::move(response), this));
326 }
327
328 void PaymentRequest::OnError()
329 {
330 if (m_completeResolver) {
331 m_completeResolver->reject(DOMException::create(SyntaxError, "Request ca ncelled"));
332 return;
333 }
334 ASSERT(m_showResolver);
335 m_showResolver->reject(DOMException::create(SyntaxError, "Request cancelled" ));
336 }
337
338 void PaymentRequest::OnComplete()
339 {
340 ASSERT(m_completeResolver);
341 m_completeResolver->resolve();
77 } 342 }
78 343
79 } // namespace blink 344 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698