Commit f20e3bf7 authored by Glen Robertson's avatar Glen Robertson Committed by Commit Bot

digital-goods: Add mojo interface to get a DigitalGoods impl object.

DigitalGoodsFactory was split up to DigitalGoodsFactory{Factory,Impl}.
Allows the impl on the other side of the mojo interface to determine
whether the DigitalGoods API should be available. Previously the caller
could not distinguish a successful vs failed attempt to get a
DigitalGoods impl over mojo.

Bug: 1131747
Change-Id: Ib21b72d27522a101ceaf2430e7130069a149d576
Fixed: 1131747
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2426150Reviewed-by: default avatarYaron Friedman <yfriedman@chromium.org>
Reviewed-by: default avatarGlen Robertson <glenrob@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Commit-Queue: Glen Robertson <glenrob@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812599}
parent 07271aea
...@@ -183,7 +183,8 @@ chrome_java_sources = [ ...@@ -183,7 +183,8 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/browserservices/VerificationResultStore.java", "java/src/org/chromium/chrome/browser/browserservices/VerificationResultStore.java",
"java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsAdapter.java", "java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsAdapter.java",
"java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsConverter.java", "java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsConverter.java",
"java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactory.java", "java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryFactory.java",
"java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryImpl.java",
"java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsImpl.java", "java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsImpl.java",
"java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappBridge.java", "java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappBridge.java",
"java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappGeolocationBridge.java", "java/src/org/chromium/chrome/browser/browserservices/permissiondelegation/InstalledWebappGeolocationBridge.java",
......
...@@ -77,7 +77,7 @@ specific_include_rules = { ...@@ -77,7 +77,7 @@ specific_include_rules = {
"QualityEnforcer\.java": [ "QualityEnforcer\.java": [
"+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java", "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
], ],
"DigitalGoodsFactory\.java": [ "DigitalGoodsFactoryImpl\.java": [
"+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java", "+chrome/android/java/src/org/chromium/chrome/browser/app/ChromeActivity.java",
], ],
"TwaFinishHandler\.java": [ "TwaFinishHandler\.java": [
......
...@@ -151,6 +151,7 @@ public class DigitalGoodsConverter { ...@@ -151,6 +151,7 @@ public class DigitalGoodsConverter {
case PLAY_BILLING_ITEM_UNAVAILABLE: case PLAY_BILLING_ITEM_UNAVAILABLE:
return BillingResponseCode.ITEM_UNAVAILABLE; return BillingResponseCode.ITEM_UNAVAILABLE;
default: default:
Log.w(TAG, "Unexpected response code: " + responseCode);
return BillingResponseCode.ERROR; return BillingResponseCode.ERROR;
} }
} }
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.browserservices.digitalgoods;
import org.chromium.content_public.browser.RenderFrameHost;
import org.chromium.payments.mojom.DigitalGoodsFactory;
import org.chromium.services.service_manager.InterfaceFactory;
/**
* A factory to produce instances of the mojo {@link DigitalGoodsFactory} interface.
*/
public class DigitalGoodsFactoryFactory implements InterfaceFactory<DigitalGoodsFactory> {
private final RenderFrameHost mRenderFrameHost;
public DigitalGoodsFactoryFactory(RenderFrameHost renderFrameHost) {
mRenderFrameHost = renderFrameHost;
}
@Override
public DigitalGoodsFactory createImpl() {
return new DigitalGoodsFactoryImpl(mRenderFrameHost);
}
}
...@@ -9,17 +9,21 @@ import androidx.annotation.VisibleForTesting; ...@@ -9,17 +9,21 @@ import androidx.annotation.VisibleForTesting;
import org.chromium.chrome.browser.ChromeApplication; import org.chromium.chrome.browser.ChromeApplication;
import org.chromium.chrome.browser.app.ChromeActivity; import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.customtabs.CustomTabActivity;
import org.chromium.components.payments.MethodStrings;
import org.chromium.components.payments.PaymentFeatureList; import org.chromium.components.payments.PaymentFeatureList;
import org.chromium.content_public.browser.RenderFrameHost; import org.chromium.content_public.browser.RenderFrameHost;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsStatics; import org.chromium.content_public.browser.WebContentsStatics;
import org.chromium.mojo.system.MojoException;
import org.chromium.payments.mojom.CreateDigitalGoodsResponseCode;
import org.chromium.payments.mojom.DigitalGoods; import org.chromium.payments.mojom.DigitalGoods;
import org.chromium.services.service_manager.InterfaceFactory; import org.chromium.payments.mojom.DigitalGoodsFactory;
import org.chromium.payments.mojom.DigitalGoodsFactory.CreateDigitalGoodsResponse;
/** /**
* A factory to create instances of the {@link DigitalGoods} mojo interface. * An implementation of the mojo {@link DigitalGoodsFactory} interface.
*/ */
public class DigitalGoodsFactory implements InterfaceFactory<DigitalGoods> { public class DigitalGoodsFactoryImpl implements DigitalGoodsFactory {
private static DigitalGoods sImplForTesting; private static DigitalGoods sImplForTesting;
private final RenderFrameHost mRenderFrameHost; private final RenderFrameHost mRenderFrameHost;
...@@ -31,30 +35,58 @@ public class DigitalGoodsFactory implements InterfaceFactory<DigitalGoods> { ...@@ -31,30 +35,58 @@ public class DigitalGoodsFactory implements InterfaceFactory<DigitalGoods> {
sImplForTesting = impl; sImplForTesting = impl;
} }
public DigitalGoodsFactory(RenderFrameHost renderFrameHost) { public DigitalGoodsFactoryImpl(RenderFrameHost renderFrameHost) {
mRenderFrameHost = renderFrameHost; mRenderFrameHost = renderFrameHost;
mDigitalGoodsDelegate = mRenderFrameHost::getLastCommittedURL; mDigitalGoodsDelegate = mRenderFrameHost::getLastCommittedURL;
mAdapter = new DigitalGoodsAdapter( mAdapter = new DigitalGoodsAdapter(
ChromeApplication.getComponent().resolveTrustedWebActivityClient()); ChromeApplication.getComponent().resolveTrustedWebActivityClient());
} }
@Override private int getResponseCode(String paymentMethod) {
public DigitalGoods createImpl() {
if (sImplForTesting != null) return sImplForTesting;
if (!PaymentFeatureList.isEnabled(PaymentFeatureList.WEB_PAYMENTS_APP_STORE_BILLING)) { if (!PaymentFeatureList.isEnabled(PaymentFeatureList.WEB_PAYMENTS_APP_STORE_BILLING)) {
return null; return CreateDigitalGoodsResponseCode.UNSUPPORTED_CONTEXT;
} }
// Ensure that the DigitalGoodsImpl is only created if we're in a TWA and on its verified // Ensure that the DigitalGoodsImpl is only created if we're in a TWA and on its verified
// origin. // origin.
WebContents wc = WebContentsStatics.fromRenderFrameHost(mRenderFrameHost); WebContents wc = WebContentsStatics.fromRenderFrameHost(mRenderFrameHost);
ChromeActivity<?> activity = ChromeActivity.fromWebContents(wc); ChromeActivity<?> activity = ChromeActivity.fromWebContents(wc);
if (!(activity instanceof CustomTabActivity)) return null; if (!(activity instanceof CustomTabActivity)) {
return CreateDigitalGoodsResponseCode.UNSUPPORTED_CONTEXT;
}
CustomTabActivity cta = (CustomTabActivity) activity; CustomTabActivity cta = (CustomTabActivity) activity;
if (!cta.isInTwaMode()) return null; if (!cta.isInTwaMode()) {
return CreateDigitalGoodsResponseCode.UNSUPPORTED_CONTEXT;
}
if (!MethodStrings.GOOGLE_PLAY_BILLING.equals(paymentMethod)) {
return CreateDigitalGoodsResponseCode.UNSUPPORTED_PAYMENT_METHOD;
}
// TODO(peconn): Add a test for this. // TODO(peconn): Add a test for this.
return new DigitalGoodsImpl(mAdapter, mDigitalGoodsDelegate); return CreateDigitalGoodsResponseCode.OK;
} }
@Override
public void createDigitalGoods(String paymentMethod, CreateDigitalGoodsResponse callback) {
if (sImplForTesting != null) {
callback.call(CreateDigitalGoodsResponseCode.OK, sImplForTesting);
return;
}
int code = getResponseCode(paymentMethod);
CreateDigitalGoodsResponseCode.validate(code);
if (code == CreateDigitalGoodsResponseCode.OK) {
callback.call(code, new DigitalGoodsImpl(mAdapter, mDigitalGoodsDelegate));
} else {
callback.call(code, null);
}
}
@Override
public void close() {}
@Override
public void onConnectionError(MojoException e) {}
} }
...@@ -9,8 +9,13 @@ Android APIs are the source of truth for information such as price. ...@@ -9,8 +9,13 @@ Android APIs are the source of truth for information such as price.
## Code ## Code
* `DigitalGoodsImpl` implements the `DigitalGoods` mojo API and is where * `DigitalGoodsImpl` implements the `DigitalGoods` mojo API, which handles
requests come from. It is created by the `DigitalGoodsFactory`. requests from JavaScript. It is created by the `DigitalGoodsFactoryImpl`.
* `DigitalGoodsFactoryImpl` implements the `DigitalGoodsFactory` mojo API, which
handles requests for new `DigitalGoods` instances. It is created by the
`DigitalGoodsFactoryFactory`. This extra indirection allows the
`DigitalGoodsFactory` to report success/failure when creating a `DigitalGoods`
instance, which would not be possible if instantiating it directly.
* `TrustedWebActivityClient` is the class that talks to Trusted Web Activities. * `TrustedWebActivityClient` is the class that talks to Trusted Web Activities.
* `DigitalGoodsAdapter` sits between `DigitalGoodsImpl` and * `DigitalGoodsAdapter` sits between `DigitalGoodsImpl` and
`TrustedWebActivityClient`, transforming between appropriate data types. `TrustedWebActivityClient`, transforming between appropriate data types.
......
...@@ -6,7 +6,7 @@ package org.chromium.chrome.browser.mojo; ...@@ -6,7 +6,7 @@ package org.chromium.chrome.browser.mojo;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.blink.mojom.Authenticator; import org.chromium.blink.mojom.Authenticator;
import org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsFactory; import org.chromium.chrome.browser.browserservices.digitalgoods.DigitalGoodsFactoryFactory;
import org.chromium.chrome.browser.installedapp.InstalledAppProviderFactory; import org.chromium.chrome.browser.installedapp.InstalledAppProviderFactory;
import org.chromium.chrome.browser.payments.PaymentRequestFactory; import org.chromium.chrome.browser.payments.PaymentRequestFactory;
import org.chromium.chrome.browser.webauth.AuthenticatorFactory; import org.chromium.chrome.browser.webauth.AuthenticatorFactory;
...@@ -15,7 +15,7 @@ import org.chromium.content_public.browser.InterfaceRegistrar; ...@@ -15,7 +15,7 @@ import org.chromium.content_public.browser.InterfaceRegistrar;
import org.chromium.content_public.browser.RenderFrameHost; import org.chromium.content_public.browser.RenderFrameHost;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.installedapp.mojom.InstalledAppProvider; import org.chromium.installedapp.mojom.InstalledAppProvider;
import org.chromium.payments.mojom.DigitalGoods; import org.chromium.payments.mojom.DigitalGoodsFactory;
import org.chromium.payments.mojom.PaymentRequest; import org.chromium.payments.mojom.PaymentRequest;
import org.chromium.services.service_manager.InterfaceRegistry; import org.chromium.services.service_manager.InterfaceRegistry;
import org.chromium.webshare.mojom.ShareService; import org.chromium.webshare.mojom.ShareService;
...@@ -49,7 +49,8 @@ class ChromeInterfaceRegistrar { ...@@ -49,7 +49,8 @@ class ChromeInterfaceRegistrar {
registry.addInterface( registry.addInterface(
InstalledAppProvider.MANAGER, new InstalledAppProviderFactory(renderFrameHost)); InstalledAppProvider.MANAGER, new InstalledAppProviderFactory(renderFrameHost));
registry.addInterface(Authenticator.MANAGER, new AuthenticatorFactory(renderFrameHost)); registry.addInterface(Authenticator.MANAGER, new AuthenticatorFactory(renderFrameHost));
registry.addInterface(DigitalGoods.MANAGER, new DigitalGoodsFactory(renderFrameHost)); registry.addInterface(
DigitalGoodsFactory.MANAGER, new DigitalGoodsFactoryFactory(renderFrameHost));
} }
} }
} }
...@@ -91,13 +91,13 @@ public class DigitalGoodsTest { ...@@ -91,13 +91,13 @@ public class DigitalGoodsTest {
/** /**
* Test that calling methods in JavaScript in the page gets correctly plumbed through to * Test that calling methods in JavaScript in the page gets correctly plumbed through to
* DigitalGoodsFactory. * DigitalGoodsFactoryImpl.
*/ */
@Test @Test
@MediumTest @MediumTest
public void javaImplConnected() throws TimeoutException { public void javaImplConnected() throws TimeoutException {
FakeDigitalGoods fake = new FakeDigitalGoods(); FakeDigitalGoods fake = new FakeDigitalGoods();
DigitalGoodsFactory.setDigitalGoodsForTesting(fake); DigitalGoodsFactoryImpl.setDigitalGoodsForTesting(fake);
fake.addItem("id1", "Item 1", "desc", "GBP", "10"); fake.addItem("id1", "Item 1", "desc", "GBP", "10");
...@@ -144,7 +144,7 @@ public class DigitalGoodsTest { ...@@ -144,7 +144,7 @@ public class DigitalGoodsTest {
@Test @Test
@MediumTest @MediumTest
public void jsToTwaConnected() throws TimeoutException { public void jsToTwaConnected() throws TimeoutException {
DigitalGoodsFactory.setDigitalGoodsForTesting(createFixedDigitalGoods()); DigitalGoodsFactoryImpl.setDigitalGoodsForTesting(createFixedDigitalGoods());
// Note: The response code much be 0 for success otherwise it doesn't propagate through to // Note: The response code much be 0 for success otherwise it doesn't propagate through to
// JS. // JS.
...@@ -166,7 +166,7 @@ public class DigitalGoodsTest { ...@@ -166,7 +166,7 @@ public class DigitalGoodsTest {
@Test @Test
@MediumTest @MediumTest
public void acknowledge() throws TimeoutException { public void acknowledge() throws TimeoutException {
DigitalGoodsFactory.setDigitalGoodsForTesting(createFixedDigitalGoods()); DigitalGoodsFactoryImpl.setDigitalGoodsForTesting(createFixedDigitalGoods());
setTwaServiceResponse(RESPONSE_ACKNOWLEDGE, createAcknowledgeResponseBundle(0)); setTwaServiceResponse(RESPONSE_ACKNOWLEDGE, createAcknowledgeResponseBundle(0));
...@@ -182,7 +182,7 @@ public class DigitalGoodsTest { ...@@ -182,7 +182,7 @@ public class DigitalGoodsTest {
@Test @Test
@MediumTest @MediumTest
public void acknowledge_failsOnNonZeroResponse() throws TimeoutException { public void acknowledge_failsOnNonZeroResponse() throws TimeoutException {
DigitalGoodsFactory.setDigitalGoodsForTesting(createFixedDigitalGoods()); DigitalGoodsFactoryImpl.setDigitalGoodsForTesting(createFixedDigitalGoods());
setTwaServiceResponse(RESPONSE_ACKNOWLEDGE, createAcknowledgeResponseBundle(1)); setTwaServiceResponse(RESPONSE_ACKNOWLEDGE, createAcknowledgeResponseBundle(1));
......
...@@ -498,8 +498,8 @@ void PopulateChromeFrameBinders( ...@@ -498,8 +498,8 @@ void PopulateChromeFrameBinders(
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
map->Add<blink::mojom::InstalledAppProvider>(base::BindRepeating( map->Add<blink::mojom::InstalledAppProvider>(base::BindRepeating(
&ForwardToJavaFrame<blink::mojom::InstalledAppProvider>)); &ForwardToJavaFrame<blink::mojom::InstalledAppProvider>));
map->Add<payments::mojom::DigitalGoods>(base::BindRepeating( map->Add<payments::mojom::DigitalGoodsFactory>(base::BindRepeating(
&ForwardToJavaFrame<payments::mojom::DigitalGoods>)); &ForwardToJavaFrame<payments::mojom::DigitalGoodsFactory>));
#if defined(BROWSER_MEDIA_CONTROLS_MENU) #if defined(BROWSER_MEDIA_CONTROLS_MENU)
map->Add<blink::mojom::MediaControlsMenuHost>(base::BindRepeating( map->Add<blink::mojom::MediaControlsMenuHost>(base::BindRepeating(
&ForwardToJavaFrame<blink::mojom::MediaControlsMenuHost>)); &ForwardToJavaFrame<blink::mojom::MediaControlsMenuHost>));
......
...@@ -23,8 +23,17 @@ interface DigitalGoods { ...@@ -23,8 +23,17 @@ interface DigitalGoods {
=> (BillingResponseCode code); => (BillingResponseCode code);
}; };
// TODO(crbug.com/1061503): Narrow down this list as discussions settle on // Allow the renderer to request a |DigitalGoods| instance. DigitalGoods
// https://github.com/WICG/digital-goods/blob/master/explainer.md // instances must be created via this factory to allow the backend to validate
// the current context.
interface DigitalGoodsFactory {
// Creates a DigitalGoods instance. The |digital_goods| is non-null iff
// |code| == kOk.
CreateDigitalGoods(string payment_method)
=> (CreateDigitalGoodsResponseCode code,
pending_remote<DigitalGoods>? digital_goods);
};
enum BillingResponseCode { enum BillingResponseCode {
kOk, kOk,
kError, kError,
...@@ -41,3 +50,10 @@ struct ItemDetails { ...@@ -41,3 +50,10 @@ struct ItemDetails {
string description; string description;
PaymentCurrencyAmount price; PaymentCurrencyAmount price;
}; };
enum CreateDigitalGoodsResponseCode {
kOk,
kError,
kUnsupportedPaymentMethod,
kUnsupportedContext,
};
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <utility>
#include "third_party/blink/renderer/modules/payments/goods/digital_goods_service.h" #include "third_party/blink/renderer/modules/payments/goods/digital_goods_service.h"
#include "third_party/blink/public/common/browser_interface_broker_proxy.h" #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
...@@ -43,9 +45,10 @@ void OnAcknowledgeResponse(ScriptPromiseResolver* resolver, ...@@ -43,9 +45,10 @@ void OnAcknowledgeResponse(ScriptPromiseResolver* resolver,
} // namespace } // namespace
DigitalGoodsService::DigitalGoodsService(ExecutionContext* context) { DigitalGoodsService::DigitalGoodsService(
context->GetBrowserInterfaceBroker().GetInterface( mojo::PendingRemote<payments::mojom::blink::DigitalGoods> pending_remote) {
mojo_service_.BindNewPipeAndPassReceiver()); DCHECK(pending_remote.is_valid());
mojo_service_.Bind(std::move(pending_remote));
DCHECK(mojo_service_); DCHECK(mojo_service_);
} }
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
namespace blink { namespace blink {
class ExecutionContext;
class ScriptState; class ScriptState;
class Visitor; class Visitor;
...@@ -19,7 +18,8 @@ class DigitalGoodsService final : public ScriptWrappable { ...@@ -19,7 +18,8 @@ class DigitalGoodsService final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO(); DEFINE_WRAPPERTYPEINFO();
public: public:
explicit DigitalGoodsService(ExecutionContext* context); explicit DigitalGoodsService(
mojo::PendingRemote<payments::mojom::blink::DigitalGoods> pending_remote);
~DigitalGoodsService() override; ~DigitalGoodsService() override;
// IDL Interface: // IDL Interface:
......
...@@ -9,9 +9,11 @@ ...@@ -9,9 +9,11 @@
namespace mojo { namespace mojo {
blink::ItemDetails* using payments::mojom::blink::BillingResponseCode;
TypeConverter<blink::ItemDetails*, payments::mojom::blink::ItemDetailsPtr>:: using payments::mojom::blink::ItemDetailsPtr;
Convert(const payments::mojom::blink::ItemDetailsPtr& input) {
blink::ItemDetails* TypeConverter<blink::ItemDetails*, ItemDetailsPtr>::Convert(
const ItemDetailsPtr& input) {
if (!input) if (!input)
return nullptr; return nullptr;
blink::ItemDetails* output = blink::ItemDetails::Create(); blink::ItemDetails* output = blink::ItemDetails::Create();
...@@ -23,26 +25,24 @@ TypeConverter<blink::ItemDetails*, payments::mojom::blink::ItemDetailsPtr>:: ...@@ -23,26 +25,24 @@ TypeConverter<blink::ItemDetails*, payments::mojom::blink::ItemDetailsPtr>::
return output; return output;
} }
WTF::String WTF::String TypeConverter<WTF::String, BillingResponseCode>::Convert(
TypeConverter<WTF::String, payments::mojom::blink::BillingResponseCode>:: const BillingResponseCode& input) {
Convert(const payments::mojom::blink::BillingResponseCode& input) {
switch (input) { switch (input) {
case payments::mojom::blink::BillingResponseCode::kOk: case BillingResponseCode::kOk:
return "ok"; return "ok";
case payments::mojom::blink::BillingResponseCode::kError: case BillingResponseCode::kError:
return "error"; return "error";
case payments::mojom::blink::BillingResponseCode::kItemAlreadyOwned: case BillingResponseCode::kItemAlreadyOwned:
return "itemAlreadyOwned"; return "itemAlreadyOwned";
case payments::mojom::blink::BillingResponseCode::kItemNotOwned: case BillingResponseCode::kItemNotOwned:
return "itemNotOwned"; return "itemNotOwned";
case payments::mojom::blink::BillingResponseCode::kItemUnavailable: case BillingResponseCode::kItemUnavailable:
return "itemUnavailable"; return "itemUnavailable";
case payments::mojom::blink::BillingResponseCode::kClientAppUnavailable: case BillingResponseCode::kClientAppUnavailable:
return "clientAppUnavailable"; return "clientAppUnavailable";
case payments::mojom::blink::BillingResponseCode::kClientAppError: case BillingResponseCode::kClientAppError:
return "clientAppError"; return "clientAppError";
} }
NOTREACHED(); NOTREACHED();
} }
......
...@@ -4,17 +4,39 @@ ...@@ -4,17 +4,39 @@
#include "third_party/blink/renderer/modules/payments/goods/dom_window_digital_goods.h" #include "third_party/blink/renderer/modules/payments/goods/dom_window_digital_goods.h"
#include <utility>
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h" #include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/modules/payments/goods/digital_goods_service.h" #include "third_party/blink/renderer/modules/payments/goods/digital_goods_service.h"
namespace blink {
namespace { namespace {
using payments::mojom::blink::CreateDigitalGoodsResponseCode;
const char known_payment_method_[] = "https://play.google.com/billing"; const char known_payment_method_[] = "https://play.google.com/billing";
} // namespace void OnCreateDigitalGoodsResponse(
ScriptPromiseResolver* resolver,
CreateDigitalGoodsResponseCode code,
mojo::PendingRemote<payments::mojom::blink::DigitalGoods> pending_remote) {
if (code != CreateDigitalGoodsResponseCode::kOk) {
DCHECK(!pending_remote);
DVLOG(1) << "CreateDigitalGoodsResponseCode " << code;
resolver->Resolve();
return;
}
DCHECK(pending_remote);
namespace blink { auto* digital_goods_service_ =
MakeGarbageCollected<DigitalGoodsService>(std::move(pending_remote));
resolver->Resolve(digital_goods_service_);
}
} // namespace
const char DOMWindowDigitalGoods::kSupplementName[] = "DOMWindowDigitalGoods"; const char DOMWindowDigitalGoods::kSupplementName[] = "DOMWindowDigitalGoods";
...@@ -32,26 +54,31 @@ ScriptPromise DOMWindowDigitalGoods::GetDigitalGoodsService( ...@@ -32,26 +54,31 @@ ScriptPromise DOMWindowDigitalGoods::GetDigitalGoodsService(
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
auto promise = resolver->Promise(); auto promise = resolver->Promise();
// TODO (crbug.com/1061503): Enable JS to connect to various payment method if (payment_method.IsEmpty()) {
// backends. For now, just connect to one known backend and check the URL is resolver->Resolve();
// correct for that payment method. return promise;
}
if (payment_method != known_payment_method_) { if (payment_method != known_payment_method_) {
resolver->Resolve(); resolver->Resolve();
return promise; return promise;
} }
if (!digital_goods_service_) { // TODO: Bind only on platforms where an implementation exists.
digital_goods_service_ = MakeGarbageCollected<DigitalGoodsService>( if (!mojo_service_) {
ExecutionContext::From(script_state)); ExecutionContext::From(script_state)
->GetBrowserInterfaceBroker()
.GetInterface(mojo_service_.BindNewPipeAndPassReceiver());
} }
resolver->Resolve(digital_goods_service_); mojo_service_->CreateDigitalGoods(
payment_method,
WTF::Bind(&OnCreateDigitalGoodsResponse, WrapPersistent(resolver)));
return promise; return promise;
} }
void DOMWindowDigitalGoods::Trace(Visitor* visitor) const { void DOMWindowDigitalGoods::Trace(Visitor* visitor) const {
Supplement<LocalDOMWindow>::Trace(visitor); Supplement<LocalDOMWindow>::Trace(visitor);
visitor->Trace(digital_goods_service_);
} }
// static // static
......
...@@ -5,12 +5,13 @@ ...@@ -5,12 +5,13 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_GOODS_DOM_WINDOW_DIGITAL_GOODS_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_GOODS_DOM_WINDOW_DIGITAL_GOODS_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_GOODS_DOM_WINDOW_DIGITAL_GOODS_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_GOODS_DOM_WINDOW_DIGITAL_GOODS_H_
#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/blink/public/mojom/digital_goods/digital_goods.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/platform/supplementable.h" #include "third_party/blink/renderer/platform/supplementable.h"
namespace blink { namespace blink {
class DigitalGoodsService;
class LocalDOMWindow; class LocalDOMWindow;
class ScriptState; class ScriptState;
class Visitor; class Visitor;
...@@ -31,7 +32,7 @@ class DOMWindowDigitalGoods final ...@@ -31,7 +32,7 @@ class DOMWindowDigitalGoods final
void Trace(Visitor* visitor) const override; void Trace(Visitor* visitor) const override;
private: private:
Member<DigitalGoodsService> digital_goods_service_; mojo::Remote<payments::mojom::blink::DigitalGoodsFactory> mojo_service_;
static DOMWindowDigitalGoods* FromState(LocalDOMWindow*); static DOMWindowDigitalGoods* FromState(LocalDOMWindow*);
}; };
......
...@@ -2,9 +2,7 @@ ...@@ -2,9 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// TODO(crbug.com/1061503): Add more fields from
// https://github.com/WICG/digital-goods/blob/master/explainer.md // https://github.com/WICG/digital-goods/blob/master/explainer.md
// as the discussions settle.
dictionary ItemDetails { dictionary ItemDetails {
DOMString itemId; DOMString itemId;
DOMString title; DOMString title;
......
...@@ -2,11 +2,12 @@ ...@@ -2,11 +2,12 @@
<html> <html>
<head> <head>
<title>Digital Goods API: Test calls from WebIDL to mojo and back.</title> <title>Digital Goods API: Test calls from WebIDL to mojo and back.</title>
<script src="../resources/js-test.js"></script>
<script src="../resources/testharness.js"></script> <script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script> <script src="../resources/testharnessreport.js"></script>
<script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings_lite.js"></script> <script src="file:///gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
<script src="file:///gen/components/payments/mojom/payment_request_data.mojom-lite.js"></script> <script src="file:///gen/components/payments/mojom/payment_request_data.mojom.js"></script>
<script src="file:///gen/third_party/blink/public/mojom/digital_goods/digital_goods.mojom-lite.js"></script> <script src="file:///gen/third_party/blink/public/mojom/digital_goods/digital_goods.mojom.js"></script>
<script src="resources/mock-digital-goods-service.js"></script> <script src="resources/mock-digital-goods-service.js"></script>
</head> </head>
<body> <body>
...@@ -65,7 +66,7 @@ digital_goods_test(async service => { ...@@ -65,7 +66,7 @@ digital_goods_test(async service => {
await service.getDetails(['fail']); await service.getDetails(['fail']);
assert_unreached(); assert_unreached();
} catch (error) { } catch (error) {
assert_equals(error, 'error'); // TODO: throw an error instead of rejecting with a string? assert_equals(error, 'error');
} }
}, {title: 'GetDetails internal error.'}); }, {title: 'GetDetails internal error.'});
...@@ -90,7 +91,7 @@ digital_goods_test(async service => { ...@@ -90,7 +91,7 @@ digital_goods_test(async service => {
await service.acknowledge('fail', 'repeatable'); await service.acknowledge('fail', 'repeatable');
assert_unreached(); assert_unreached();
} catch (error) { } catch (error) {
assert_equals(error, 'error'); // TODO: throw an error instead of rejecting with a string? assert_equals(error, 'error');
} }
}, {title: 'Acknowledge bad ID should error.'}); }, {title: 'Acknowledge bad ID should error.'});
...@@ -103,6 +104,17 @@ digital_goods_test(async service => { ...@@ -103,6 +104,17 @@ digital_goods_test(async service => {
} }
}, {title: 'Acknowledge bad purchase type should error.'}); }, {title: 'Acknowledge bad purchase type should error.'});
digital_goods_test(async service => {
const response1 = await service.getDetails(['id1']);
assert_equals(response1.length, 1);
gc();
const response2 = await service.getDetails(['id2']);
assert_equals(response2.length, 1);
gc();
const response3 = await service.getDetails(['id3']);
assert_equals(response3.length, 1);
}, {title: 'DigitalGoods referenced by scripts should survive garbage collector.'});
</script> </script>
</body> </body>
</html> </html>
...@@ -2,19 +2,11 @@ ...@@ -2,19 +2,11 @@
class MockDigitalGoods { class MockDigitalGoods {
constructor() { constructor() {
this.interceptor_ =
new MojoInterfaceInterceptor(
payments.mojom.DigitalGoods.$interfaceName);
this.interceptor_.oninterfacerequest =
e => this.bindHandleToReceiver_(e.handle);
this.interceptor_.start();
this.resetRecordedAction_(); this.resetRecordedAction_();
} }
bindHandleToReceiver_(handle) { bind(request) {
this.receiver_ = new payments.mojom.DigitalGoodsReceiver(this); this.binding = new mojo.Binding(payments.mojom.DigitalGoods, this, request);
this.receiver_.$.bindHandle(handle);
} }
getRecordedAction_() { getRecordedAction_() {
...@@ -80,6 +72,42 @@ class MockDigitalGoods { ...@@ -80,6 +72,42 @@ class MockDigitalGoods {
let mockDigitalGoods = new MockDigitalGoods(); let mockDigitalGoods = new MockDigitalGoods();
class MockDigitalGoodsFactory {
constructor() {
this.interceptor_ =
new MojoInterfaceInterceptor(
payments.mojom.DigitalGoodsFactory.name);
this.interceptor_.oninterfacerequest = e => this.bind(e.handle);
this.bindingSet_ = new mojo.BindingSet(payments.mojom.DigitalGoodsFactory);
this.interceptor_.start();
}
bind(handle) {
this.bindingSet_.addBinding(this, handle);
}
async createDigitalGoods(paymentMethod) {
if (paymentMethod !== 'https://play.google.com/billing') {
return {
code: /*CreateDigitalGoodsResponseCode.kUnsupportedPaymentMethod=*/2,
digitalGoods: null
};
}
let digitalGoodsPtr = new payments.mojom.DigitalGoodsPtr();
mockDigitalGoods.bind(mojo.makeRequest(digitalGoodsPtr));
return {
code: /*CreateDigitalGoodsResponseCode.kOk=*/0,
digitalGoods: digitalGoodsPtr
};
}
}
let mockDigitalGoodsFactory = new MockDigitalGoodsFactory();
function digital_goods_test(func, { function digital_goods_test(func, {
title, title,
expectedAction, expectedAction,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment