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

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: Disable WTF-Mojo string serialization for now. 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 bool isValidCurrencyCode(const String& code, ExceptionState& exceptionState)
esprehn 2016/03/18 00:20:50 Is this all covered by tests? Seems like we should
please use gerrit instead 2016/03/23 00:14:51 Covered in PaymentRequestDetailsTest.
29 {
30 if (code.length() != 3) {
31 exceptionState.throwTypeError("'" + code + "' is not a valid ISO 4217 cu rrency code, should be 3 letters");
haraken 2016/03/18 09:19:44 Just to confirm: Maybe should this be a DOMExcepti
please use gerrit instead 2016/03/23 00:14:51 Spec does not touch on this yet. It's using a lot
32 return false;
33 }
34
35 for (size_t i = 0; i < code.length(); ++i) {
36 if (code[i] < 'A' || code[i] > 'Z') {
37 exceptionState.throwTypeError("'" + code + "' is not a valid ISO 421 7 currency code, should be upper case letters [A-Z]");
38 return false;
39 }
40 }
41
42 return true;
43 }
44
45 bool isValidAmount(const String& amount, ExceptionState& exceptionState)
Marijn Kruisselbrink 2016/03/18 01:39:48 This doesn't seem to exactly match the (informativ
groby-ooo-7-16 2016/03/21 19:36:26 Hm. If we have a regexp, why not use ScriptRegex a
please use gerrit instead 2016/03/23 00:14:51 Using ScriptRegexp with the exact regular expressi
46 {
47 if (amount.isEmpty()) {
48 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 20022 2 CurrencyAnd30Amount currency amount, should be nonempty");
haraken 2016/03/18 09:19:44 Ditto.
please use gerrit instead 2016/03/23 00:14:51 Acknowledged.
49 return false;
50 }
51
52 if (amount.length() > 32) {
53 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 20022 2 CurrencyAnd30Amount currency amount, should be at most 32 characters");
haraken 2016/03/18 09:19:44 Ditto.
please use gerrit instead 2016/03/23 00:14:51 Acknowledged.
54 return false;
55 }
56
57 int numberOfPeriods = 0;
58 int numberOfDigits = 0;
59 int numberOfFractionDigits = 0;
60 for (size_t i = 0; i < amount.length(); ++i) {
61 if (i == 0 && amount[i] == '-')
62 continue;
63
64 bool isPeriod = amount[i] == '.';
65 bool isDigit = amount[i] >= '0' && amount[i] <= '9';
groby-ooo-7-16 2016/03/18 00:47:25 isdigit? (Well, OK, isASCIIDigit - I forgot we're
please use gerrit instead 2016/03/23 00:14:51 Using the regex from the spec instead.
66 if (!isPeriod && !isDigit) {
67 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 2 00222 CurrencyAnd30Amount currency amount, should contain only '-', '.', and [0- 9]");
groby-ooo-7-16 2016/03/18 00:47:25 CurrencyAnd30Amount? (Here and elsewhere)
please use gerrit instead 2016/03/23 00:14:51 That's a newer replacement for ISO 4217 that our W
68 return false;
69 }
70
71 if (isPeriod)
72 ++numberOfPeriods;
73
74 if (isDigit) {
75 ++numberOfDigits;
76 if (numberOfPeriods > 0)
77 ++numberOfFractionDigits;
78 }
79 }
80
81 if (numberOfPeriods > 1) {
82 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 20022 2 CurrencyAnd30Amount currency amount, should contain only one '.'");
83 return false;
84 }
85
86 if (numberOfDigits > 30) {
87 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 20022 2 CurrencyAnd30Amount currency amount, should contain at most 30 digits");
88 return false;
89 }
90
91 if (numberOfFractionDigits > 10) {
92 exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 20022 2 CurrencyAnd30Amount currency amount, should contain at most 10 fraction digits ");
93 return false;
94 }
95
96 return true;
97 }
98
99 template <typename T>
100 bool areValidCurrencyAndAmounts(HeapVector<T> items, ExceptionState& exceptionSt ate)
101 {
102 for (const auto& item : items) {
103 if (item.hasAmount()) {
104 if (item.amount().hasCurrencyCode() && !isValidCurrencyCode(item.amo unt().currencyCode(), exceptionState))
105 return false;
106
107 if (item.amount().hasValue() && isValidAmount(item.amount().value(), exceptionState))
Marijn Kruisselbrink 2016/03/18 01:39:48 Should this be !isValidAmount? And some kind of te
please use gerrit instead 2016/03/23 00:14:51 Good catch. Tested and fixed.
108 return false;
109 }
110 }
111
112 return true;
113 }
114
115 } // namespace
16 116
17 // static 117 // static
18 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, ExceptionState& exceptio nState) 118 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, ExceptionState& exceptio nState)
19 { 119 {
20 return new PaymentRequest(scriptState, supportedMethods, details, PaymentOpt ions(), ScriptValue(), exceptionState); 120 return new PaymentRequest(scriptState, supportedMethods, details, PaymentOpt ions(), ScriptValue(), exceptionState);
21 } 121 }
22 122
23 // static 123 // static
24 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, ExceptionState& exceptionState) 124 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, ExceptionState& exceptionState)
25 { 125 {
26 return new PaymentRequest(scriptState, supportedMethods, details, options, S criptValue(), exceptionState); 126 return new PaymentRequest(scriptState, supportedMethods, details, options, S criptValue(), exceptionState);
27 } 127 }
28 128
29 // static 129 // static
30 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, const ScriptValue& data, ExceptionState& exceptionState) 130 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, const ScriptValue& data, ExceptionState& exceptionState)
31 { 131 {
32 return new PaymentRequest(scriptState, supportedMethods, details, options, d ata, exceptionState); 132 return new PaymentRequest(scriptState, supportedMethods, details, options, d ata, exceptionState);
33 } 133 }
34 134
35 PaymentRequest::~PaymentRequest() 135 PaymentRequest::~PaymentRequest()
36 { 136 {
37 } 137 }
38 138
39 ScriptPromise PaymentRequest::show(ScriptState* scriptState) 139 ScriptPromise PaymentRequest::show(ScriptState* scriptState)
40 { 140 {
41 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::crea te(NotSupportedError, "Not implemented.")); 141 if (m_showResolver)
142 return m_showResolver->promise().rejectWithDOMException(scriptState, DOM Exception::create(InvalidStateError, "Already called show() once"));
Marijn Kruisselbrink 2016/03/18 01:39:48 This seems weird. rejectWithDOMException is a stat
please use gerrit instead 2016/03/23 00:14:51 Did not realize it's static. Calling it properly n
143
144 ASSERT(!m_paymentProvider.is_bound());
145 blink::Platform::current()->connectToRemoteService(mojo::GetProxy(&m_payment Provider));
146 if (!m_paymentProvider)
147 return m_showResolver->promise().rejectWithDOMException(scriptState, DOM Exception::create(SyntaxError, "Not implemented on this platform"));
Marijn Kruisselbrink 2016/03/18 01:39:48 Same here, should just be ScriptPromise::rejectWit
please use gerrit instead 2016/03/23 00:14:51 Removed the check from this patch. Not writing con
148
149 m_paymentProvider->SetClient(m_clientBinding.CreateInterfacePtrAndBind());
150 // TODO(rouslan): Call show() after WTF-Mojo serialization works.
151
152 m_showResolver = ScriptPromiseResolver::create(scriptState);
153 return m_showResolver->promise();
42 } 154 }
43 155
44 void PaymentRequest::abort() 156 void PaymentRequest::abort(ExceptionState& exceptionState)
45 { 157 {
158 if (!m_showResolver) {
159 exceptionState.throwDOMException(InvalidStateError, "Never called show() , so nothing to abort");
160 return;
161 }
162
163 m_paymentProvider->Abort();
46 } 164 }
47 165
48 const AtomicString& PaymentRequest::interfaceName() const 166 const AtomicString& PaymentRequest::interfaceName() const
49 { 167 {
50 return EventTargetNames::PaymentRequest; 168 return EventTargetNames::PaymentRequest;
51 } 169 }
52 170
53 ExecutionContext* PaymentRequest::getExecutionContext() const 171 ExecutionContext* PaymentRequest::getExecutionContext() const
54 { 172 {
55 return m_scriptState->getExecutionContext(); 173 return m_scriptState->getExecutionContext();
56 } 174 }
57 175
176 ScriptPromise PaymentRequest::complete(ScriptState* scriptState, bool success)
177 {
178 if (m_completeResolver)
179 return m_completeResolver->promise().rejectWithDOMException(scriptState, DOMException::create(InvalidStateError, "Already called complete() once"));
Marijn Kruisselbrink 2016/03/18 01:39:48 Same static method call thing.
please use gerrit instead 2016/03/23 00:14:51 Done.
180
181 m_completeResolver = ScriptPromiseResolver::create(scriptState);
182 m_paymentProvider->Complete(success);
183
184 return m_completeResolver->promise();
185 }
186
58 DEFINE_TRACE(PaymentRequest) 187 DEFINE_TRACE(PaymentRequest)
59 { 188 {
60 visitor->trace(m_details); 189 visitor->trace(m_details);
61 visitor->trace(m_options); 190 visitor->trace(m_options);
62 visitor->trace(m_shippingAddress); 191 visitor->trace(m_shippingAddress);
192 visitor->trace(m_showResolver);
193 visitor->trace(m_completeResolver);
63 RefCountedGarbageCollectedEventTargetWithInlineData<PaymentRequest>::trace(v isitor); 194 RefCountedGarbageCollectedEventTargetWithInlineData<PaymentRequest>::trace(v isitor);
64 } 195 }
65 196
66 PaymentRequest::PaymentRequest(ScriptState* scriptState, const Vector<String>& s upportedMethods, const PaymentDetails& details, const PaymentOptions& options, c onst ScriptValue& data, ExceptionState& exceptionState) 197 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) 198 : m_scriptState(scriptState)
68 , m_supportedMethods(supportedMethods) 199 , m_supportedMethods(supportedMethods)
69 , m_details(details) 200 , m_details(details)
70 , m_options(options) 201 , m_options(options)
202 , m_clientBinding(this)
71 { 203 {
204 // TODO(rouslan): Also check for a top-level browsing context.
205 // https://github.com/w3c/browser-payment-api/issues/2
206 if (!scriptState->getExecutionContext()->isSecureContext()) {
207 exceptionState.throwSecurityError("Must be in a secure context");
208 return;
209 }
210
211 if (supportedMethods.isEmpty()) {
212 exceptionState.throwTypeError("No payment methods identifiers provided") ;
213 return;
214 }
215
216 if (details.hasItems() && !areValidCurrencyAndAmounts(details.items(), excep tionState))
217 return;
218
219 if (details.hasShippingOptions() && !areValidCurrencyAndAmounts(details.ship pingOptions(), exceptionState))
220 return;
221
72 if (!data.isEmpty()) { 222 if (!data.isEmpty()) {
73 RefPtr<JSONValue> value = toJSONValue(data.context(), data.v8Value()); 223 RefPtr<JSONValue> value = toJSONValue(data.context(), data.v8Value());
74 if (value && value->getType() == JSONValue::TypeObject) 224 if (value && !value->isNull()) {
75 m_stringifiedData = JSONObject::cast(value)->toJSONString(); 225 if (value->getType() != JSONValue::TypeObject) {
226 exceptionState.throwTypeError("Payment method specific data shou ld be a JSON-serializable object");
227 return;
228 }
229
230 RefPtr<JSONObject> jsonData = JSONObject::cast(value);
231 for (const auto& paymentMethodSpecificKeyValue : *jsonData) {
232 if (!supportedMethods.contains(paymentMethodSpecificKeyValue.key )) {
233 exceptionState.throwTypeError("'" + paymentMethodSpecificKey Value.key + "' does not match one of the payment method identifiers");
234 return;
235 }
236 if (!paymentMethodSpecificKeyValue.value || paymentMethodSpecifi cKeyValue.value->isNull() || paymentMethodSpecificKeyValue.value->getType() != J SONValue::TypeObject) {
237 exceptionState.throwTypeError("Data for '" + paymentMethodSp ecificKeyValue.key + "' should be a JSON-serializable object");
238 return;
239 }
240 }
241
242 m_stringifiedData = jsonData->toJSONString();
243 }
76 } 244 }
245
246 if (details.hasShippingOptions() && details.shippingOptions().size() == 1 && details.shippingOptions()[0].hasId())
247 m_shippingOption = details.shippingOptions()[0].id();
248 }
249
250 void PaymentRequest::OnShippingAddressChange(mojom::blink::ShippingAddressPtr ad dress)
251 {
252 ASSERT(m_showResolver);
253 ASSERT(!m_completeResolver);
254 // TODO(rouslan): Should the merchant website be notified of invalid shippin g address
255 // from the browser or the payment app?
256 m_shippingAddress = new ShippingAddress(std::move(address));
257 if (!m_shippingAddress->isValidRegionCode()) {
258 m_showResolver->reject(DOMException::create(SyntaxError, "Invalid ISO 31 66 country code"));
259 return;
260 }
261 if (!m_shippingAddress->isValidLanguageCode()) {
262 m_showResolver->reject(DOMException::create(SyntaxError, "Invalid ISO 63 9 language code"));
263 return;
264 }
265 if (!m_shippingAddress->isValidScriptCode()) {
266 m_showResolver->reject(DOMException::create(SyntaxError, "Invalid ISO 15 24 script code"));
267 return;
268 }
269 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::shippingaddr esschange);
270 event->setTarget(this);
271 getExecutionContext()->getEventQueue()->enqueueEvent(event);
272 }
273
274 void PaymentRequest::OnShippingOptionChange(const String& shippingOptionId)
275 {
276 ASSERT(m_showResolver);
277 ASSERT(!m_completeResolver);
278 m_shippingOption = shippingOptionId;
279 RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::shippingopti onchange);
280 event->setTarget(this);
281 getExecutionContext()->getEventQueue()->enqueueEvent(event);
282 }
283
284 void PaymentRequest::OnPaymentResponse(mojom::blink::PaymentResponsePtr response )
285 {
286 ASSERT(m_showResolver);
287 ASSERT(!m_completeResolver);
288 m_showResolver->resolve(new PaymentResponse(std::move(response), this));
289 }
290
291 void PaymentRequest::OnError()
292 {
293 if (m_completeResolver) {
294 m_completeResolver->reject(DOMException::create(SyntaxError, "Request ca ncelled"));
295 return;
296 }
297 ASSERT(m_showResolver);
298 m_showResolver->reject(DOMException::create(SyntaxError, "Request cancelled" ));
299 }
300
301 void PaymentRequest::OnComplete()
302 {
303 ASSERT(m_completeResolver);
304 m_completeResolver->resolve();
77 } 305 }
78 306
79 } // namespace blink 307 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698