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

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: platform depends on mojo/wtf_support Created 4 years, 8 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 "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());
Marijn Kruisselbrink 2016/03/31 18:21:15 Do you need a connection error handler for the cli
please use gerrit instead 2016/03/31 20:43:08 chromium-mojo@ says the second connection handler
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());
esprehn 2016/03/31 04:44:58 you're the only consumer of toJSONValue. I want to
please use gerrit instead 2016/03/31 20:43:08 Planning to do this in one of the following patche
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);
haraken 2016/03/31 02:19:34 To make this assert meaningful, do we need to clea
please use gerrit instead 2016/03/31 20:43:08 Done.
289 DCHECK(!m_completeResolver);
290
291 // TODO(rouslan): Should the merchant website be notified of invalid shippin g address
292 // from the browser or the payment app?
293 String errorMessage;
294 if (!PaymentsValidators::isValidRegionCodeFormat(address->region_code, &erro rMessage)
295 || !PaymentsValidators::isValidLanguageCodeFormat(address->language_code , &errorMessage)
296 || !PaymentsValidators::isValidScriptCodeFormat(address->script_code, &e rrorMessage)) {
297 m_showResolver->reject(DOMException::create(SyntaxError, errorMessage));
298 return;
299 }
300
301 if (address->language_code.isEmpty() && !address->script_code.isEmpty()) {
302 m_showResolver->reject(DOMException::create(SyntaxError, "If language co de is empty, then script code should also be empty"));
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)
haraken 2016/03/31 02:19:34 How is it guaranteed that PaymentRequest::OnPaymen
please use gerrit instead 2016/03/31 20:43:08 Closing the Mojo connection now.
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 else if (m_showResolver)
haraken 2016/03/31 02:19:34 Nit: I'd use 'if' rather than 'else if' just in ca
please use gerrit instead 2016/03/31 20:43:08 Done.
334 m_showResolver->reject(DOMException::create(SyntaxError, "Request cancel led"));
335 m_clientBinding.Close();
336 m_paymentProvider.reset();
337 }
338
339 void PaymentRequest::OnComplete()
340 {
341 DCHECK(m_completeResolver);
342 m_completeResolver->resolve();
343 m_clientBinding.Close();
344 m_paymentProvider.reset();
77 } 345 }
78 346
79 } // namespace blink 347 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698