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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
diff --git a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
index fe3ab4642012884480a3a1fe8edd66b0e6bdd892..95ca13d98f83fe2a9b9533a854ce5d4402c333bb 100644
--- a/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
+++ b/third_party/WebKit/Source/modules/payments/PaymentRequest.cpp
@@ -6,13 +6,113 @@
#include "bindings/core/v8/ExceptionState.h"
#include "bindings/core/v8/JSONValuesForV8.h"
+#include "bindings/core/v8/ScriptPromiseResolver.h"
#include "bindings/core/v8/ScriptState.h"
+#include "core/EventTypeNames.h"
#include "core/dom/DOMException.h"
#include "core/dom/ExceptionCode.h"
+#include "core/events/Event.h"
+#include "core/events/EventQueue.h"
#include "modules/EventTargetModulesNames.h"
+#include "modules/payments/PaymentItem.h"
+#include "modules/payments/PaymentResponse.h"
#include "modules/payments/ShippingAddress.h"
+#include "modules/payments/ShippingOption.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "public/platform/Platform.h"
+#include <utility>
namespace blink {
+namespace {
+
+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.
+{
+ if (code.length() != 3) {
+ exceptionState.throwTypeError("'" + code + "' is not a valid ISO 4217 currency 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
+ return false;
+ }
+
+ for (size_t i = 0; i < code.length(); ++i) {
+ if (code[i] < 'A' || code[i] > 'Z') {
+ exceptionState.throwTypeError("'" + code + "' is not a valid ISO 4217 currency code, should be upper case letters [A-Z]");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+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
+{
+ if (amount.isEmpty()) {
+ exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 200222 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.
+ return false;
+ }
+
+ if (amount.length() > 32) {
+ exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 200222 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.
+ return false;
+ }
+
+ int numberOfPeriods = 0;
+ int numberOfDigits = 0;
+ int numberOfFractionDigits = 0;
+ for (size_t i = 0; i < amount.length(); ++i) {
+ if (i == 0 && amount[i] == '-')
+ continue;
+
+ bool isPeriod = amount[i] == '.';
+ 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.
+ if (!isPeriod && !isDigit) {
+ exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 200222 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
+ return false;
+ }
+
+ if (isPeriod)
+ ++numberOfPeriods;
+
+ if (isDigit) {
+ ++numberOfDigits;
+ if (numberOfPeriods > 0)
+ ++numberOfFractionDigits;
+ }
+ }
+
+ if (numberOfPeriods > 1) {
+ exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 200222 CurrencyAnd30Amount currency amount, should contain only one '.'");
+ return false;
+ }
+
+ if (numberOfDigits > 30) {
+ exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 200222 CurrencyAnd30Amount currency amount, should contain at most 30 digits");
+ return false;
+ }
+
+ if (numberOfFractionDigits > 10) {
+ exceptionState.throwTypeError("'" + amount + "' is not a valid ISO 200222 CurrencyAnd30Amount currency amount, should contain at most 10 fraction digits");
+ return false;
+ }
+
+ return true;
+}
+
+template <typename T>
+bool areValidCurrencyAndAmounts(HeapVector<T> items, ExceptionState& exceptionState)
+{
+ for (const auto& item : items) {
+ if (item.hasAmount()) {
+ if (item.amount().hasCurrencyCode() && !isValidCurrencyCode(item.amount().currencyCode(), exceptionState))
+ return false;
+
+ 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.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
// static
PaymentRequest* PaymentRequest::create(ScriptState* scriptState, const Vector<String>& supportedMethods, const PaymentDetails& details, ExceptionState& exceptionState)
@@ -38,11 +138,29 @@ PaymentRequest::~PaymentRequest()
ScriptPromise PaymentRequest::show(ScriptState* scriptState)
{
- return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError, "Not implemented."));
+ if (m_showResolver)
+ return m_showResolver->promise().rejectWithDOMException(scriptState, DOMException::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
+
+ ASSERT(!m_paymentProvider.is_bound());
+ blink::Platform::current()->connectToRemoteService(mojo::GetProxy(&m_paymentProvider));
+ if (!m_paymentProvider)
+ return m_showResolver->promise().rejectWithDOMException(scriptState, DOMException::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
+
+ m_paymentProvider->SetClient(m_clientBinding.CreateInterfacePtrAndBind());
+ // TODO(rouslan): Call show() after WTF-Mojo serialization works.
+
+ m_showResolver = ScriptPromiseResolver::create(scriptState);
+ return m_showResolver->promise();
}
-void PaymentRequest::abort()
+void PaymentRequest::abort(ExceptionState& exceptionState)
{
+ if (!m_showResolver) {
+ exceptionState.throwDOMException(InvalidStateError, "Never called show(), so nothing to abort");
+ return;
+ }
+
+ m_paymentProvider->Abort();
}
const AtomicString& PaymentRequest::interfaceName() const
@@ -55,11 +173,24 @@ ExecutionContext* PaymentRequest::getExecutionContext() const
return m_scriptState->getExecutionContext();
}
+ScriptPromise PaymentRequest::complete(ScriptState* scriptState, bool success)
+{
+ if (m_completeResolver)
+ 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.
+
+ m_completeResolver = ScriptPromiseResolver::create(scriptState);
+ m_paymentProvider->Complete(success);
+
+ return m_completeResolver->promise();
+}
+
DEFINE_TRACE(PaymentRequest)
{
visitor->trace(m_details);
visitor->trace(m_options);
visitor->trace(m_shippingAddress);
+ visitor->trace(m_showResolver);
+ visitor->trace(m_completeResolver);
RefCountedGarbageCollectedEventTargetWithInlineData<PaymentRequest>::trace(visitor);
}
@@ -68,12 +199,109 @@ PaymentRequest::PaymentRequest(ScriptState* scriptState, const Vector<String>& s
, m_supportedMethods(supportedMethods)
, m_details(details)
, m_options(options)
+ , m_clientBinding(this)
{
+ // TODO(rouslan): Also check for a top-level browsing context.
+ // https://github.com/w3c/browser-payment-api/issues/2
+ if (!scriptState->getExecutionContext()->isSecureContext()) {
+ exceptionState.throwSecurityError("Must be in a secure context");
+ return;
+ }
+
+ if (supportedMethods.isEmpty()) {
+ exceptionState.throwTypeError("No payment methods identifiers provided");
+ return;
+ }
+
+ if (details.hasItems() && !areValidCurrencyAndAmounts(details.items(), exceptionState))
+ return;
+
+ if (details.hasShippingOptions() && !areValidCurrencyAndAmounts(details.shippingOptions(), exceptionState))
+ return;
+
if (!data.isEmpty()) {
RefPtr<JSONValue> value = toJSONValue(data.context(), data.v8Value());
- if (value && value->getType() == JSONValue::TypeObject)
- m_stringifiedData = JSONObject::cast(value)->toJSONString();
+ if (value && !value->isNull()) {
+ if (value->getType() != JSONValue::TypeObject) {
+ exceptionState.throwTypeError("Payment method specific data should be a JSON-serializable object");
+ return;
+ }
+
+ RefPtr<JSONObject> jsonData = JSONObject::cast(value);
+ for (const auto& paymentMethodSpecificKeyValue : *jsonData) {
+ if (!supportedMethods.contains(paymentMethodSpecificKeyValue.key)) {
+ exceptionState.throwTypeError("'" + paymentMethodSpecificKeyValue.key + "' does not match one of the payment method identifiers");
+ return;
+ }
+ if (!paymentMethodSpecificKeyValue.value || paymentMethodSpecificKeyValue.value->isNull() || paymentMethodSpecificKeyValue.value->getType() != JSONValue::TypeObject) {
+ exceptionState.throwTypeError("Data for '" + paymentMethodSpecificKeyValue.key + "' should be a JSON-serializable object");
+ return;
+ }
+ }
+
+ m_stringifiedData = jsonData->toJSONString();
+ }
}
+
+ if (details.hasShippingOptions() && details.shippingOptions().size() == 1 && details.shippingOptions()[0].hasId())
+ m_shippingOption = details.shippingOptions()[0].id();
+}
+
+void PaymentRequest::OnShippingAddressChange(mojom::blink::ShippingAddressPtr address)
+{
+ ASSERT(m_showResolver);
+ ASSERT(!m_completeResolver);
+ // TODO(rouslan): Should the merchant website be notified of invalid shipping address
+ // from the browser or the payment app?
+ m_shippingAddress = new ShippingAddress(std::move(address));
+ if (!m_shippingAddress->isValidRegionCode()) {
+ m_showResolver->reject(DOMException::create(SyntaxError, "Invalid ISO 3166 country code"));
+ return;
+ }
+ if (!m_shippingAddress->isValidLanguageCode()) {
+ m_showResolver->reject(DOMException::create(SyntaxError, "Invalid ISO 639 language code"));
+ return;
+ }
+ if (!m_shippingAddress->isValidScriptCode()) {
+ m_showResolver->reject(DOMException::create(SyntaxError, "Invalid ISO 1524 script code"));
+ return;
+ }
+ RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::shippingaddresschange);
+ event->setTarget(this);
+ getExecutionContext()->getEventQueue()->enqueueEvent(event);
+}
+
+void PaymentRequest::OnShippingOptionChange(const String& shippingOptionId)
+{
+ ASSERT(m_showResolver);
+ ASSERT(!m_completeResolver);
+ m_shippingOption = shippingOptionId;
+ RefPtrWillBeRawPtr<Event> event = Event::create(EventTypeNames::shippingoptionchange);
+ event->setTarget(this);
+ getExecutionContext()->getEventQueue()->enqueueEvent(event);
+}
+
+void PaymentRequest::OnPaymentResponse(mojom::blink::PaymentResponsePtr response)
+{
+ ASSERT(m_showResolver);
+ ASSERT(!m_completeResolver);
+ m_showResolver->resolve(new PaymentResponse(std::move(response), this));
+}
+
+void PaymentRequest::OnError()
+{
+ if (m_completeResolver) {
+ m_completeResolver->reject(DOMException::create(SyntaxError, "Request cancelled"));
+ return;
+ }
+ ASSERT(m_showResolver);
+ m_showResolver->reject(DOMException::create(SyntaxError, "Request cancelled"));
+}
+
+void PaymentRequest::OnComplete()
+{
+ ASSERT(m_completeResolver);
+ m_completeResolver->resolve();
}
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698