chromium/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryImpl.java

// Copyright 2020 The Chromium Authors
// 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 android.app.Activity;

import org.chromium.base.ResettersForTesting;
import org.chromium.chrome.browser.ActivityUtils;
import org.chromium.chrome.browser.ChromeApplicationImpl;
import org.chromium.chrome.browser.customtabs.CustomTabActivity;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.components.payments.MethodStrings;
import org.chromium.components.payments.PaymentFeatureList;
import org.chromium.content_public.browser.RenderFrameHost;
import org.chromium.content_public.browser.WebContents;
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.DigitalGoodsFactory;
import org.chromium.payments.mojom.DigitalGoodsFactory.CreateDigitalGoods_Response;

/** An implementation of the mojo {@link DigitalGoodsFactory} interface. */
public class DigitalGoodsFactoryImpl implements DigitalGoodsFactory {
    private static DigitalGoods sImplForTesting;

    private final RenderFrameHost mRenderFrameHost;
    private final DigitalGoodsImpl.Delegate mDigitalGoodsDelegate;
    private final DigitalGoodsAdapter mAdapter;

    public static void setDigitalGoodsForTesting(DigitalGoods impl) {
        sImplForTesting = impl;
        ResettersForTesting.register(() -> sImplForTesting = null);
    }

    public DigitalGoodsFactoryImpl(RenderFrameHost renderFrameHost) {
        mRenderFrameHost = renderFrameHost;
        mDigitalGoodsDelegate = mRenderFrameHost::getLastCommittedURL;
        mAdapter =
                new DigitalGoodsAdapter(
                        ChromeApplicationImpl.getComponent().resolveTrustedWebActivityClient());
    }

    private int getResponseCode(String paymentMethod) {
        if (!PaymentFeatureList.isEnabled(PaymentFeatureList.WEB_PAYMENTS_APP_STORE_BILLING)) {
            return CreateDigitalGoodsResponseCode.UNSUPPORTED_CONTEXT;
        }

        // Ensure that the DigitalGoodsImpl is only created if we're in a TWA and on its verified
        // origin.
        WebContents wc = WebContentsStatics.fromRenderFrameHost(mRenderFrameHost);
        Activity activity = ActivityUtils.getActivityFromWebContents(wc);
        if (!(activity instanceof CustomTabActivity)) {
            return CreateDigitalGoodsResponseCode.UNSUPPORTED_CONTEXT;
        }
        CustomTabActivity cta = (CustomTabActivity) activity;
        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.

        return CreateDigitalGoodsResponseCode.OK;
    }

    @Override
    public void createDigitalGoods(String paymentMethod, CreateDigitalGoods_Response callback) {
        if (sImplForTesting != null) {
            callback.call(CreateDigitalGoodsResponseCode.OK, sImplForTesting);
            return;
        }

        // If the user is making Digital Goods payments, this is a good hint that we should enable
        // site isolation for the site.
        WebContents wc = WebContentsStatics.fromRenderFrameHost(mRenderFrameHost);
        SiteIsolator.startIsolatingSite(
                Profile.fromWebContents(wc), mDigitalGoodsDelegate.getUrl());

        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) {}
}