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

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: Combine if statement. 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"
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 "public/platform/Platform.h"
24 #include <utility>
25
26 namespace mojo {
27
28 template <>
29 struct TypeConverter<mojom::wtf::CurrencyAmountPtr, blink::CurrencyAmount> {
30 static mojom::wtf::CurrencyAmountPtr Convert(const blink::CurrencyAmount& in put)
31 {
32 mojom::wtf::CurrencyAmountPtr output = mojom::wtf::CurrencyAmount::New() ;
33 output->currency_code = input.currencyCode();
34 output->value = input.value();
35 return output;
36 }
37 };
38
39 template <>
40 struct TypeConverter<mojom::wtf::PaymentItemPtr, blink::PaymentItem> {
41 static mojom::wtf::PaymentItemPtr Convert(const blink::PaymentItem& input)
42 {
43 mojom::wtf::PaymentItemPtr output = mojom::wtf::PaymentItem::New();
44 output->id = input.id();
45 output->label = input.label();
46 output->amount = mojom::wtf::CurrencyAmount::From(input.amount());
47 return output;
48 }
49 };
50
51 template <>
52 struct TypeConverter<mojom::wtf::ShippingOptionPtr, blink::ShippingOption> {
53 static mojom::wtf::ShippingOptionPtr Convert(const blink::ShippingOption& in put)
54 {
55 mojom::wtf::ShippingOptionPtr output = mojom::wtf::ShippingOption::New() ;
56 output->id = input.id();
57 output->label = input.label();
58 output->amount = mojom::wtf::CurrencyAmount::From(input.amount());
59 return output;
60 }
61 };
62
63 template <>
64 struct TypeConverter<mojom::wtf::PaymentDetailsPtr, blink::PaymentDetails> {
65 static mojom::wtf::PaymentDetailsPtr Convert(const blink::PaymentDetails& in put)
66 {
67 mojom::wtf::PaymentDetailsPtr output = mojom::wtf::PaymentDetails::New() ;
68 output->items = mojo::WTFArray<mojom::wtf::PaymentItemPtr>::New(input.it ems().size());
69 for (size_t i = 0; i < input.items().size(); ++i) {
70 output->items[i] = mojom::wtf::PaymentItem::From(input.items()[i]);
71 }
72 output->shipping_options = mojo::WTFArray<mojom::wtf::ShippingOptionPtr> ::New(input.hasShippingOptions() ? input.shippingOptions().size() : 0);
73 if (input.hasShippingOptions()) {
74 for (size_t i = 0; i < input.shippingOptions().size(); ++i) {
75 output->shipping_options[i] = mojom::wtf::ShippingOption::From(i nput.shippingOptions()[i]);
esprehn 2016/03/25 23:40:11 if we're going to be writing a lot of these copy :
Marijn Kruisselbrink 2016/03/25 23:59:39 See also my earlier comment; mojo::Array has these
please use gerrit instead 2016/03/29 22:15:45 Created a helper.
76 }
77 }
78 return output;
79 }
80 };
81
82 template <>
83 struct TypeConverter<mojom::wtf::PaymentOptionsPtr, blink::PaymentOptions> {
84 static mojom::wtf::PaymentOptionsPtr Convert(const blink::PaymentOptions& in put)
85 {
86 mojom::wtf::PaymentOptionsPtr output = mojom::wtf::PaymentOptions::New() ;
87 output->request_shipping = input.requestShipping();
88 return output;
89 }
90 };
91
92 } // namespace mojo
14 93
15 namespace blink { 94 namespace blink {
95 namespace {
96
97 // Validates ShippingOption and PaymentItem dictionaries, which happen to have i dentical fields.
98 template <typename T>
99 bool areValidItems(HeapVector<T> items, ExceptionState& exceptionState)
esprehn 2016/03/25 23:40:11 hmm what two things have the exact same fields bel
please use gerrit instead 2016/03/29 22:15:45 See the comment for this function: "ShippingOption
100 {
101 String errorMessage;
102 for (const auto& item : items) {
103 if (!item.hasId()) {
104 exceptionState.throwTypeError("Item id required");
105 return false;
106 }
107
108 if (!item.hasLabel()) {
109 exceptionState.throwTypeError("Item label required");
110 return false;
111 }
112
113 if (!item.hasAmount()) {
114 exceptionState.throwTypeError("Currency amount required");
115 return false;
116 }
117
118 if (!item.amount().hasCurrencyCode()) {
119 exceptionState.throwTypeError("Currency code required");
120 return false;
121 }
122
123 if (!item.amount().hasValue()) {
124 exceptionState.throwTypeError("Currency value required");
125 return false;
126 }
127
128 if (!isValidCurrencyCodeFormat(item.amount().currencyCode(), &errorMessa ge)) {
129 exceptionState.throwTypeError(errorMessage);
130 return false;
131 }
132
133 if (!isValidAmountFormat(item.amount().value(), &errorMessage)) {
134 exceptionState.throwTypeError(errorMessage);
135 return false;
136 }
137 }
138
139 return true;
esprehn 2016/03/25 23:40:11 you actually don't need the boolean, you can just
Marijn Kruisselbrink 2016/03/25 23:59:39 of course you'd still need to return as soon as yo
please use gerrit instead 2016/03/29 22:15:45 Done. Note that "else if" statements are not suita
140 }
141
142 } // namespace
16 143
17 // static 144 // static
18 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, ExceptionState& exceptio nState) 145 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, ExceptionState& exceptio nState)
19 { 146 {
20 return new PaymentRequest(scriptState, supportedMethods, details, PaymentOpt ions(), ScriptValue(), exceptionState); 147 return new PaymentRequest(scriptState, supportedMethods, details, PaymentOpt ions(), ScriptValue(), exceptionState);
21 } 148 }
22 149
23 // static 150 // static
24 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, ExceptionState& exceptionState) 151 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, ExceptionState& exceptionState)
25 { 152 {
26 return new PaymentRequest(scriptState, supportedMethods, details, options, S criptValue(), exceptionState); 153 return new PaymentRequest(scriptState, supportedMethods, details, options, S criptValue(), exceptionState);
27 } 154 }
28 155
29 // static 156 // static
30 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, const ScriptValue& data, ExceptionState& exceptionState) 157 PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<St ring>& supportedMethods, const PaymentDetails& details, const PaymentOptions& op tions, const ScriptValue& data, ExceptionState& exceptionState)
31 { 158 {
32 return new PaymentRequest(scriptState, supportedMethods, details, options, d ata, exceptionState); 159 return new PaymentRequest(scriptState, supportedMethods, details, options, d ata, exceptionState);
33 } 160 }
34 161
35 PaymentRequest::~PaymentRequest() 162 PaymentRequest::~PaymentRequest()
36 { 163 {
37 } 164 }
38 165
39 ScriptPromise PaymentRequest::show(ScriptState* scriptState) 166 ScriptPromise PaymentRequest::show(ScriptState* scriptState)
40 { 167 {
41 return ScriptPromise::rejectWithDOMException(scriptState, DOMException::crea te(NotSupportedError, "Not implemented.")); 168 if (m_showResolver)
169 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(InvalidStateError, "Already called show() once"));
170
171 DCHECK(!m_paymentProvider.is_bound());
172 blink::Platform::current()->connectToRemoteService(mojo::GetProxy(&m_payment Provider));
173 // TODO(rouslan): Handle connection errors.
esprehn 2016/03/25 23:40:11 What's the correct way to handle this? Often todo'
please use gerrit instead 2016/03/29 22:15:45 Handling it now. It was not very well documented,
174 m_paymentProvider->SetClient(m_clientBinding.CreateInterfacePtrAndBind());
175 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);
176
177 m_showResolver = ScriptPromiseResolver::create(scriptState);
178 return m_showResolver->promise();
42 } 179 }
43 180
44 void PaymentRequest::abort() 181 void PaymentRequest::abort(ExceptionState& exceptionState)
45 { 182 {
183 if (!m_showResolver) {
184 exceptionState.throwDOMException(InvalidStateError, "Never called show() , so nothing to abort");
185 return;
186 }
187
188 m_paymentProvider->Abort();
46 } 189 }
47 190
48 const AtomicString& PaymentRequest::interfaceName() const 191 const AtomicString& PaymentRequest::interfaceName() const
49 { 192 {
50 return EventTargetNames::PaymentRequest; 193 return EventTargetNames::PaymentRequest;
51 } 194 }
52 195
53 ExecutionContext* PaymentRequest::getExecutionContext() const 196 ExecutionContext* PaymentRequest::getExecutionContext() const
54 { 197 {
55 return m_scriptState->getExecutionContext(); 198 return m_scriptState->getExecutionContext();
56 } 199 }
57 200
201 ScriptPromise PaymentRequest::complete(ScriptState* scriptState, bool success)
202 {
203 if (m_completeResolver)
204 return ScriptPromise::rejectWithDOMException(scriptState, DOMException:: create(InvalidStateError, "Already called complete() once"));
205
206 m_completeResolver = ScriptPromiseResolver::create(scriptState);
207 m_paymentProvider->Complete(success);
208
209 return m_completeResolver->promise();
210 }
211
58 DEFINE_TRACE(PaymentRequest) 212 DEFINE_TRACE(PaymentRequest)
59 { 213 {
60 visitor->trace(m_details); 214 visitor->trace(m_details);
61 visitor->trace(m_options); 215 visitor->trace(m_options);
62 visitor->trace(m_shippingAddress); 216 visitor->trace(m_shippingAddress);
217 visitor->trace(m_showResolver);
218 visitor->trace(m_completeResolver);
63 RefCountedGarbageCollectedEventTargetWithInlineData<PaymentRequest>::trace(v isitor); 219 RefCountedGarbageCollectedEventTargetWithInlineData<PaymentRequest>::trace(v isitor);
64 } 220 }
65 221
66 PaymentRequest::PaymentRequest(ScriptState* scriptState, const Vector<String>& s upportedMethods, const PaymentDetails& details, const PaymentOptions& options, c onst ScriptValue& data, ExceptionState& exceptionState) 222 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) 223 : m_scriptState(scriptState)
68 , m_supportedMethods(supportedMethods) 224 , m_supportedMethods(supportedMethods)
69 , m_details(details) 225 , m_details(details)
70 , m_options(options) 226 , m_options(options)
227 , m_clientBinding(this)
71 { 228 {
229 // TODO(rouslan): Also check for a top-level browsing context.
230 // https://github.com/w3c/browser-payment-api/issues/2
231 if (!scriptState->getExecutionContext()->isSecureContext()) {
232 exceptionState.throwSecurityError("Must be in a secure context");
233 return;
234 }
235
236 if (supportedMethods.isEmpty()) {
237 exceptionState.throwTypeError("Must specify at least one payment method identifier");
238 return;
239 }
240
241 if (!details.hasItems()) {
242 exceptionState.throwTypeError("Must specify items");
243 return;
244 }
245
246 if (details.items().isEmpty()) {
247 exceptionState.throwTypeError("Must specify at least one item");
248 return;
249 }
250
251 if (!areValidItems(details.items(), exceptionState))
252 return;
253
254 if (details.hasShippingOptions() && !areValidItems(details.shippingOptions() , exceptionState))
255 return;
256
72 if (!data.isEmpty()) { 257 if (!data.isEmpty()) {
73 RefPtr<JSONValue> value = toJSONValue(data.context(), data.v8Value()); 258 RefPtr<JSONValue> value = toJSONValue(data.context(), data.v8Value());
esprehn 2016/03/25 23:40:11 this only returns null if maxDepth is reached btw
rwlbuis 2016/03/29 18:26:43 And I think there is a problem with the way it is
please use gerrit instead 2016/03/29 22:15:45 Added a layout test and throwing on infinite json,
74 if (value && value->getType() == JSONValue::TypeObject) 259 if (value && !value->isNull()) {
75 m_stringifiedData = JSONObject::cast(value)->toJSONString(); 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");
esprehn 2016/03/25 23:40:11 hmm, if we have real requirements on the |data| wh
Marijn Kruisselbrink 2016/03/25 23:59:40 a WebIDL dictionary has a fixed, hardcoded set of
please use gerrit instead 2016/03/29 22:15:45 Correct.
269 return;
270 }
271 if (!paymentMethodSpecificKeyValue.value || paymentMethodSpecifi cKeyValue.value->isNull() || paymentMethodSpecificKeyValue.value->getType() != J SONValue::TypeObject) {
esprehn 2016/03/25 23:40:11 ditto, all these null checks are kind of suspect.
rwlbuis 2016/03/29 19:30:46 Independent of this, note that getType() != TypeOb
please use gerrit instead 2016/03/29 22:15:45 Done.
please use gerrit instead 2016/03/29 22:15:45 Removed the "!paymentMethodSpecificKeyValue.value"
272 exceptionState.throwTypeError("Data for '" + paymentMethodSp ecificKeyValue.key + "' should be a JSON-serializable object");
273 return;
274 }
275 }
276
277 m_stringifiedData = jsonData->toJSONString();
esprehn 2016/03/25 23:40:11 why can't we reuse the data? This code is doing st
Marijn Kruisselbrink 2016/03/25 23:59:40 Where is it doing that? It starts as ScriptValue,
please use gerrit instead 2016/03/29 22:15:45 Acknowledged.
278 }
76 } 279 }
280
281 if (details.hasShippingOptions() && details.shippingOptions().size() == 1)
esprehn 2016/03/25 23:40:11 what happens if you specified more than one? It se
Marijn Kruisselbrink 2016/03/25 23:59:40 I was confused here for a bit too, all the options
please use gerrit instead 2016/03/29 22:15:45 If multiple options are available, the user has to
282 m_shippingOption = details.shippingOptions()[0].id();
283 }
284
285 void PaymentRequest::OnShippingAddressChange(mojom::wtf::ShippingAddressPtr addr ess)
286 {
287 DCHECK(m_showResolver);
288 DCHECK(!m_completeResolver);
289
290 // TODO(rouslan): Should the merchant website be notified of invalid shippin g address
291 // from the browser or the payment app?
292 String errorMessage;
293 if (!isValidRegionCodeFormat(address->region_code, &errorMessage)
294 || !isValidLanguageCodeFormat(address->language_code, &errorMessage)
295 || !isValidScriptCodeFormat(address->script_code, &errorMessage)) {
296 m_showResolver->reject(DOMException::create(SyntaxError, errorMessage));
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 return;
303 }
304
305 m_shippingAddress = new ShippingAddress(std::move(address));
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 DCHECK(m_showResolver);
314 DCHECK(!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::wtf::PaymentResponsePtr response)
322 {
323 DCHECK(m_showResolver);
324 DCHECK(!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 DCHECK(m_showResolver);
335 m_showResolver->reject(DOMException::create(SyntaxError, "Request cancelled" ));
336 m_clientBinding.Close();
337 m_paymentProvider.reset();
338 }
339
340 void PaymentRequest::OnComplete()
341 {
342 DCHECK(m_completeResolver);
343 m_completeResolver->resolve();
344 m_clientBinding.Close();
345 m_paymentProvider.reset();
77 } 346 }
78 347
79 } // namespace blink 348 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698