Commit 65b4d26b authored by Liquan (Max) Gu's avatar Liquan (Max) Gu Committed by Commit Bot

[PRImpl] Move url checks and Delegate of initAndValidate() into CPRImpl

Change:
* Move part of PRImpl#initAndValidate() into CPRImpl#initAndValidate()
* Since the move of initAndValidate() involves
Delegate.getInvalidSslCertificateErrorMessage(), PRImpl.Delegate is
moved into CPRImpl becoming CPRImpl.Delegate.
* Since the move of initAndValidate() involves mPaymentOptions and
mRequestShipping, etc., they are duplicated in
ComponentPaymentRequestImpl. This duplication is necessary because
PRImpl and CPRImpl may lose access to each other, and is without the
out-of-sync issue because mPaymentOptions is final.
* Since the Delegate depends on ChromeActivity, the ChromeActivity
dependencies is refactored to WebContents dependencies.
* Simplifies Delegate#isOffTheRecord(), Delegate#isWebContentsActive(),
Delegate#getTwaPackageName() because they don't need to rely on a
WebContents/ChromeActivity parameter.

Bug: 1102522

Change-Id: I62bb66ff7c799020c973394102f57c33c3d0392a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2401783
Commit-Queue: Liquan (Max) Gu <maxlg@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Reviewed-by: default avatarEvan Stade <estade@chromium.org>
Cr-Commit-Position: refs/heads/master@{#807302}
parent 55e0406b
...@@ -4,14 +4,11 @@ ...@@ -4,14 +4,11 @@
package org.chromium.chrome.browser.payments; package org.chromium.chrome.browser.payments;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.chromium.chrome.browser.app.ChromeActivity; import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.preferences.Pref; import org.chromium.chrome.browser.preferences.Pref;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelUtils;
import org.chromium.components.payments.ComponentPaymentRequestImpl; import org.chromium.components.payments.ComponentPaymentRequestImpl;
import org.chromium.components.payments.ErrorStrings; import org.chromium.components.payments.ErrorStrings;
import org.chromium.components.payments.OriginSecurityChecker; import org.chromium.components.payments.OriginSecurityChecker;
...@@ -20,6 +17,7 @@ import org.chromium.components.payments.SslValidityChecker; ...@@ -20,6 +17,7 @@ import org.chromium.components.payments.SslValidityChecker;
import org.chromium.components.user_prefs.UserPrefs; import org.chromium.components.user_prefs.UserPrefs;
import org.chromium.content_public.browser.FeaturePolicyFeature; import org.chromium.content_public.browser.FeaturePolicyFeature;
import org.chromium.content_public.browser.RenderFrameHost; import org.chromium.content_public.browser.RenderFrameHost;
import org.chromium.content_public.browser.Visibility;
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.mojo.system.MojoException;
...@@ -40,7 +38,7 @@ import org.chromium.services.service_manager.InterfaceFactory; ...@@ -40,7 +38,7 @@ import org.chromium.services.service_manager.InterfaceFactory;
*/ */
public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> { public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> {
// Tests can inject behaviour on future PaymentRequests via these objects. // Tests can inject behaviour on future PaymentRequests via these objects.
public static PaymentRequestImpl.Delegate sDelegateForTest; public static ComponentPaymentRequestImpl.Delegate sDelegateForTest;
private final RenderFrameHost mRenderFrameHost; private final RenderFrameHost mRenderFrameHost;
...@@ -108,8 +106,9 @@ public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> { ...@@ -108,8 +106,9 @@ public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> {
* Production implementation of the PaymentRequestImpl's Delegate. Gives true answers * Production implementation of the PaymentRequestImpl's Delegate. Gives true answers
* about the system. * about the system.
*/ */
public static class PaymentRequestDelegateImpl implements PaymentRequestImpl.Delegate { public static class PaymentRequestDelegateImpl implements ComponentPaymentRequestImpl.Delegate {
private final TwaPackageManagerDelegate mPackageManager = new TwaPackageManagerDelegate(); private final TwaPackageManagerDelegate mPackageManagerDelegate =
new TwaPackageManagerDelegate();
private final RenderFrameHost mRenderFrameHost; private final RenderFrameHost mRenderFrameHost;
/* package */ PaymentRequestDelegateImpl(RenderFrameHost renderFrameHost) { /* package */ PaymentRequestDelegateImpl(RenderFrameHost renderFrameHost) {
...@@ -117,39 +116,42 @@ public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> { ...@@ -117,39 +116,42 @@ public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> {
} }
@Override @Override
public boolean isOffTheRecord(WebContents webContents) { public boolean isOffTheRecord() {
// To be conservative, a request which we don't know its profile is considered // TODO(crbug.com/1128658): Try getting around the Profile dependency, as in C++ where
// off-the-record, and thus user data would not be recorded in this case. // we can do web_contents->GetBrowserContext()->IsOffTheRecord().
ChromeActivity activity = ChromeActivity.fromWebContents(webContents); WebContents liveWebContents = getLiveWebContents();
if (activity == null) return true; if (liveWebContents == null) return true;
TabModel tabModel = activity.getCurrentTabModel(); Profile profile = Profile.fromWebContents(liveWebContents);
assert tabModel != null;
Profile profile = tabModel.getProfile();
if (profile == null) return true; if (profile == null) return true;
return profile.isOffTheRecord(); return profile.isOffTheRecord();
} }
@Override @Override
public String getInvalidSslCertificateErrorMessage() { public String getInvalidSslCertificateErrorMessage() {
WebContents webContents = getWebContents(); WebContents liveWebContents = getLiveWebContents();
if (webContents == null || webContents.isDestroyed()) return null; if (liveWebContents == null) return null;
if (!OriginSecurityChecker.isSchemeCryptographic(webContents.getLastCommittedUrl())) { if (!OriginSecurityChecker.isSchemeCryptographic(
liveWebContents.getLastCommittedUrl())) {
return null; return null;
} }
return SslValidityChecker.getInvalidSslCertificateErrorMessage(webContents); return SslValidityChecker.getInvalidSslCertificateErrorMessage(liveWebContents);
} }
@Override @Override
public boolean isWebContentsActive(@NonNull ChromeActivity activity) { public boolean isWebContentsActive() {
return TabModelUtils.getCurrentWebContents(activity.getCurrentTabModel()) // TODO(crbug.com/1128658): Try making the WebContents inactive for instrumentation
== getWebContents(); // tests rather than mocking it with this method.
WebContents liveWebContents = getLiveWebContents();
return liveWebContents != null && liveWebContents.getVisibility() == Visibility.VISIBLE;
} }
@Override @Override
public boolean prefsCanMakePayment() { public boolean prefsCanMakePayment() {
WebContents webContents = getWebContents(); // TODO(crbug.com/1128658): Try replacing Profile with BrowserContextHandle, which
return webContents != null && !webContents.isDestroyed() // represents a Chrome Profile or WebLayer ProfileImpl, and which UserPrefs operates on.
&& UserPrefs.get(Profile.fromWebContents(webContents)) WebContents liveWebContents = getLiveWebContents();
return liveWebContents != null
&& UserPrefs.get(Profile.fromWebContents(liveWebContents))
.getBoolean(Pref.CAN_MAKE_PAYMENT_ENABLED); .getBoolean(Pref.CAN_MAKE_PAYMENT_ENABLED);
} }
...@@ -160,13 +162,17 @@ public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> { ...@@ -160,13 +162,17 @@ public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> {
@Override @Override
@Nullable @Nullable
public String getTwaPackageName(@Nullable ChromeActivity activity) { public String getTwaPackageName() {
return activity != null ? mPackageManager.getTwaPackageName(activity) : null; WebContents liveWebContents = getLiveWebContents();
if (liveWebContents == null) return null;
ChromeActivity activity = ChromeActivity.fromWebContents(liveWebContents);
return activity != null ? mPackageManagerDelegate.getTwaPackageName(activity) : null;
} }
@Nullable @Nullable
private WebContents getWebContents() { private WebContents getLiveWebContents() {
return WebContentsStatics.fromRenderFrameHost(mRenderFrameHost); WebContents webContents = WebContentsStatics.fromRenderFrameHost(mRenderFrameHost);
return webContents != null && !webContents.isDestroyed() ? webContents : null;
} }
} }
...@@ -192,7 +198,7 @@ public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> { ...@@ -192,7 +198,7 @@ public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> {
return new InvalidPaymentRequest(); return new InvalidPaymentRequest();
} }
PaymentRequestImpl.Delegate delegate; ComponentPaymentRequestImpl.Delegate delegate;
if (sDelegateForTest != null) { if (sDelegateForTest != null) {
delegate = sDelegateForTest; delegate = sDelegateForTest;
} else { } else {
...@@ -203,8 +209,8 @@ public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> { ...@@ -203,8 +209,8 @@ public class PaymentRequestFactory implements InterfaceFactory<PaymentRequest> {
if (webContents == null || webContents.isDestroyed()) return new InvalidPaymentRequest(); if (webContents == null || webContents.isDestroyed()) return new InvalidPaymentRequest();
return ComponentPaymentRequestImpl.createPaymentRequest(mRenderFrameHost, return ComponentPaymentRequestImpl.createPaymentRequest(mRenderFrameHost,
/*isOffTheRecord=*/delegate.isOffTheRecord(webContents), /*isOffTheRecord=*/delegate.isOffTheRecord(),
/*skipUiForBasicCard=*/delegate.skipUiForBasicCard(), /*skipUiForBasicCard=*/delegate.skipUiForBasicCard(), delegate,
(componentPaymentRequest) (componentPaymentRequest)
-> new PaymentRequestImpl(componentPaymentRequest, delegate)); -> new PaymentRequestImpl(componentPaymentRequest, delegate));
} }
......
...@@ -8,7 +8,6 @@ import android.content.Context; ...@@ -8,7 +8,6 @@ import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.collection.ArrayMap; import androidx.collection.ArrayMap;
...@@ -36,6 +35,7 @@ import org.chromium.components.payments.BrowserPaymentRequest; ...@@ -36,6 +35,7 @@ import org.chromium.components.payments.BrowserPaymentRequest;
import org.chromium.components.payments.CanMakePaymentQuery; import org.chromium.components.payments.CanMakePaymentQuery;
import org.chromium.components.payments.CheckoutFunnelStep; import org.chromium.components.payments.CheckoutFunnelStep;
import org.chromium.components.payments.ComponentPaymentRequestImpl; import org.chromium.components.payments.ComponentPaymentRequestImpl;
import org.chromium.components.payments.ComponentPaymentRequestImpl.Delegate;
import org.chromium.components.payments.ErrorMessageUtil; import org.chromium.components.payments.ErrorMessageUtil;
import org.chromium.components.payments.ErrorStrings; import org.chromium.components.payments.ErrorStrings;
import org.chromium.components.payments.Event; import org.chromium.components.payments.Event;
...@@ -98,43 +98,6 @@ public class PaymentRequestImpl ...@@ -98,43 +98,6 @@ public class PaymentRequestImpl
PaymentResponseHelper.PaymentResponseRequesterDelegate, PaymentResponseHelper.PaymentResponseRequesterDelegate,
PaymentDetailsConverter.MethodChecker, PaymentUIsManager.Delegate, PaymentDetailsConverter.MethodChecker, PaymentUIsManager.Delegate,
PaymentUIsObserver { PaymentUIsObserver {
/**
* A delegate to ask questions about the system, that allows tests to inject behaviour without
* having to modify the entire system. This partially mirrors a similar C++
* (Content)PaymentRequestDelegate for the C++ implementation, allowing the test harness to
* override behaviour in both in a similar fashion.
*/
public interface Delegate {
/**
* Returns whether the WebContents is currently showing an off-the-record tab. Return true
* if the tab profile is not accessible from the WebContents.
*/
boolean isOffTheRecord(WebContents webContents);
/**
* Returns a non-null string if there is an invalid SSL certificate on the currently
* loaded page.
*/
String getInvalidSslCertificateErrorMessage();
/**
* Returns true if the web contents that initiated the payment request is active.
*/
boolean isWebContentsActive(@NonNull ChromeActivity activity);
/**
* Returns whether the preferences allow CAN_MAKE_PAYMENT.
*/
boolean prefsCanMakePayment();
/**
* Returns true if the UI can be skipped for "basic-card" scenarios. This will only ever
* be true in tests.
*/
boolean skipUiForBasicCard();
/**
* If running inside of a Trusted Web Activity, returns the package name for Trusted Web
* Activity. Otherwise returns an empty string or null.
*/
@Nullable
String getTwaPackageName(@Nullable ChromeActivity activity);
}
private static final String TAG = "PaymentRequest"; private static final String TAG = "PaymentRequest";
private static boolean sIsLocalCanMakePaymentQueryQuotaEnforcedForTest; private static boolean sIsLocalCanMakePaymentQueryQuotaEnforcedForTest;
...@@ -164,11 +127,13 @@ public class PaymentRequestImpl ...@@ -164,11 +127,13 @@ public class PaymentRequestImpl
private final PaymentUIsManager mPaymentUIsManager; private final PaymentUIsManager mPaymentUIsManager;
private PaymentOptions mPaymentOptions; @Nullable
private boolean mRequestShipping; private final PaymentOptions mPaymentOptions;
private boolean mRequestPayerName; private final boolean mRequestShipping;
private boolean mRequestPayerPhone; private final boolean mRequestPayerName;
private boolean mRequestPayerEmail; private final boolean mRequestPayerPhone;
private final boolean mRequestPayerEmail;
private final int mShippingType;
private boolean mIsCanMakePaymentResponsePending; private boolean mIsCanMakePaymentResponsePending;
private boolean mIsHasEnrolledInstrumentResponsePending; private boolean mIsHasEnrolledInstrumentResponsePending;
...@@ -178,7 +143,6 @@ public class PaymentRequestImpl ...@@ -178,7 +143,6 @@ public class PaymentRequestImpl
private boolean mHasClosed; private boolean mHasClosed;
private PaymentRequestSpec mSpec; private PaymentRequestSpec mSpec;
private int mShippingType;
private boolean mIsFinishedQueryingPaymentApps; private boolean mIsFinishedQueryingPaymentApps;
private List<PaymentApp> mPendingApps = new ArrayList<>(); private List<PaymentApp> mPendingApps = new ArrayList<>();
private PaymentApp mInvokedPaymentApp; private PaymentApp mInvokedPaymentApp;
...@@ -263,6 +227,14 @@ public class PaymentRequestImpl ...@@ -263,6 +227,14 @@ public class PaymentRequestImpl
mWebContents = componentPaymentRequestImpl.getWebContents(); mWebContents = componentPaymentRequestImpl.getWebContents();
mMerchantName = mWebContents.getTitle(); mMerchantName = mWebContents.getTitle();
mJourneyLogger = componentPaymentRequestImpl.getJourneyLogger(); mJourneyLogger = componentPaymentRequestImpl.getJourneyLogger();
mPaymentOptions = componentPaymentRequestImpl.getPaymentOptions();
mRequestShipping = PaymentOptionsUtils.requestShipping(mPaymentOptions);
mRequestPayerName = PaymentOptionsUtils.requestPayerName(mPaymentOptions);
mRequestPayerPhone = PaymentOptionsUtils.requestPayerPhone(mPaymentOptions);
mRequestPayerEmail = PaymentOptionsUtils.requestPayerEmail(mPaymentOptions);
mShippingType = PaymentOptionsUtils.getShippingType(mPaymentOptions);
mComponentPaymentRequestImpl = componentPaymentRequestImpl; mComponentPaymentRequestImpl = componentPaymentRequestImpl;
mPaymentUIsManager = new PaymentUIsManager(/*delegate=*/this, mPaymentUIsManager = new PaymentUIsManager(/*delegate=*/this,
/*params=*/this, mWebContents, mIsOffTheRecord, mJourneyLogger, mTopLevelOrigin, /*params=*/this, mWebContents, mIsOffTheRecord, mJourneyLogger, mTopLevelOrigin,
...@@ -275,36 +247,6 @@ public class PaymentRequestImpl ...@@ -275,36 +247,6 @@ public class PaymentRequestImpl
public boolean initAndValidate(PaymentMethodData[] rawMethodData, PaymentDetails details, public boolean initAndValidate(PaymentMethodData[] rawMethodData, PaymentDetails details,
@Nullable PaymentOptions options, boolean googlePayBridgeEligible) { @Nullable PaymentOptions options, boolean googlePayBridgeEligible) {
assert mComponentPaymentRequestImpl != null; assert mComponentPaymentRequestImpl != null;
mJourneyLogger.recordCheckoutStep(CheckoutFunnelStep.INITIATED);
mPaymentOptions = options;
mRequestShipping = options != null && options.requestShipping;
mRequestPayerName = options != null && options.requestPayerName;
mRequestPayerPhone = options != null && options.requestPayerPhone;
mRequestPayerEmail = options != null && options.requestPayerEmail;
mShippingType = PaymentOptionsUtils.getShippingType(options);
if (!UrlUtil.isOriginAllowedToUseWebPaymentApis(mWebContents.getLastCommittedUrl())) {
Log.d(TAG, ErrorStrings.PROHIBITED_ORIGIN);
Log.d(TAG, ErrorStrings.PROHIBITED_ORIGIN_OR_INVALID_SSL_EXPLANATION);
mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER);
disconnectFromClientWithDebugMessage(ErrorStrings.PROHIBITED_ORIGIN,
PaymentErrorReason.NOT_SUPPORTED_FOR_INVALID_ORIGIN_OR_SSL);
return false;
}
mJourneyLogger.setRequestedInformation(
mRequestShipping, mRequestPayerEmail, mRequestPayerPhone, mRequestPayerName);
String rejectShowErrorMessage = mDelegate.getInvalidSslCertificateErrorMessage();
if (!TextUtils.isEmpty(rejectShowErrorMessage)) {
Log.d(TAG, rejectShowErrorMessage);
Log.d(TAG, ErrorStrings.PROHIBITED_ORIGIN_OR_INVALID_SSL_EXPLANATION);
mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER);
disconnectFromClientWithDebugMessage(rejectShowErrorMessage,
PaymentErrorReason.NOT_SUPPORTED_FOR_INVALID_ORIGIN_OR_SSL);
return false;
}
boolean googlePayBridgeActivated = googlePayBridgeEligible boolean googlePayBridgeActivated = googlePayBridgeEligible
&& SkipToGPayHelper.canActivateExperiment(mWebContents, rawMethodData); && SkipToGPayHelper.canActivateExperiment(mWebContents, rawMethodData);
...@@ -396,7 +338,7 @@ public class PaymentRequestImpl ...@@ -396,7 +338,7 @@ public class PaymentRequestImpl
/** @return Whether the UI was built. */ /** @return Whether the UI was built. */
private boolean buildUI(ChromeActivity activity) { private boolean buildUI(ChromeActivity activity) {
String error = mPaymentUIsManager.buildPaymentRequestUI(activity, String error = mPaymentUIsManager.buildPaymentRequestUI(activity,
/*isWebContentsActive=*/mDelegate.isWebContentsActive(activity), /*isWebContentsActive=*/mDelegate.isWebContentsActive(),
/*waitForUpdatedDetails=*/mWaitForUpdatedDetails); /*waitForUpdatedDetails=*/mWaitForUpdatedDetails);
if (error != null) { if (error != null) {
mJourneyLogger.setNotShown(NotShownReason.OTHER); mJourneyLogger.setNotShown(NotShownReason.OTHER);
...@@ -1129,15 +1071,15 @@ public class PaymentRequestImpl ...@@ -1129,15 +1071,15 @@ public class PaymentRequestImpl
disconnectFromClientWithDebugMessage(ErrorStrings.USER_CANCELLED); disconnectFromClientWithDebugMessage(ErrorStrings.USER_CANCELLED);
} }
private void disconnectFromClientWithDebugMessage(String debugMessage) {
disconnectFromClientWithDebugMessage(debugMessage, PaymentErrorReason.USER_CANCEL);
}
// Implement BrowserPaymentRequest: // Implement BrowserPaymentRequest:
// This method is not supposed to be used outside this class and // This method is not supposed to be used outside this class and
// ComponentPaymentRequestImpl. // ComponentPaymentRequestImpl.
@Override @Override
public void disconnectFromClientWithDebugMessage(String debugMessage) { public void disconnectFromClientWithDebugMessage(String debugMessage, int reason) {
disconnectFromClientWithDebugMessage(debugMessage, PaymentErrorReason.USER_CANCEL);
}
private void disconnectFromClientWithDebugMessage(String debugMessage, int reason) {
Log.d(TAG, debugMessage); Log.d(TAG, debugMessage);
if (mComponentPaymentRequestImpl != null) { if (mComponentPaymentRequestImpl != null) {
mComponentPaymentRequestImpl.onError(reason, debugMessage); mComponentPaymentRequestImpl.onError(reason, debugMessage);
...@@ -1441,7 +1383,7 @@ public class PaymentRequestImpl ...@@ -1441,7 +1383,7 @@ public class PaymentRequestImpl
@Override @Override
@Nullable @Nullable
public String getTwaPackageName() { public String getTwaPackageName() {
return mDelegate.getTwaPackageName(ChromeActivity.fromWebContents(mWebContents)); return mDelegate.getTwaPackageName();
} }
// PaymentAppFactoryDelegate implementation. // PaymentAppFactoryDelegate implementation.
......
...@@ -7,12 +7,10 @@ package org.chromium.chrome.test_support; ...@@ -7,12 +7,10 @@ package org.chromium.chrome.test_support;
import android.os.Build; import android.os.Build;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.payments.PaymentRequestFactory; import org.chromium.chrome.browser.payments.PaymentRequestFactory;
import org.chromium.chrome.browser.payments.PaymentRequestImpl; import org.chromium.chrome.browser.payments.PaymentRequestImpl;
import org.chromium.components.autofill.EditableOption; import org.chromium.components.autofill.EditableOption;
...@@ -33,7 +31,8 @@ public class PaymentRequestTestBridge { ...@@ -33,7 +31,8 @@ public class PaymentRequestTestBridge {
* about the state of the system, in order to control which paths should be tested in the * about the state of the system, in order to control which paths should be tested in the
* PaymentRequestImpl. * PaymentRequestImpl.
*/ */
private static class PaymentRequestDelegateForTest implements PaymentRequestImpl.Delegate { private static class PaymentRequestDelegateForTest
implements ComponentPaymentRequestImpl.Delegate {
private final boolean mIsOffTheRecord; private final boolean mIsOffTheRecord;
private final boolean mIsValidSsl; private final boolean mIsValidSsl;
private final boolean mIsWebContentsActive; private final boolean mIsWebContentsActive;
...@@ -50,7 +49,7 @@ public class PaymentRequestTestBridge { ...@@ -50,7 +49,7 @@ public class PaymentRequestTestBridge {
} }
@Override @Override
public boolean isOffTheRecord(WebContents webContents) { public boolean isOffTheRecord() {
return mIsOffTheRecord; return mIsOffTheRecord;
} }
...@@ -61,7 +60,7 @@ public class PaymentRequestTestBridge { ...@@ -61,7 +60,7 @@ public class PaymentRequestTestBridge {
} }
@Override @Override
public boolean isWebContentsActive(@NonNull ChromeActivity activity) { public boolean isWebContentsActive() {
return mIsWebContentsActive; return mIsWebContentsActive;
} }
...@@ -77,7 +76,7 @@ public class PaymentRequestTestBridge { ...@@ -77,7 +76,7 @@ public class PaymentRequestTestBridge {
@Override @Override
@Nullable @Nullable
public String getTwaPackageName(@Nullable ChromeActivity activity) { public String getTwaPackageName() {
return mTwaPackageName; return mTwaPackageName;
} }
} }
......
...@@ -7,6 +7,7 @@ package org.chromium.components.payments; ...@@ -7,6 +7,7 @@ package org.chromium.components.payments;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.chromium.payments.mojom.PaymentDetails; import org.chromium.payments.mojom.PaymentDetails;
import org.chromium.payments.mojom.PaymentErrorReason;
import org.chromium.payments.mojom.PaymentMethodData; import org.chromium.payments.mojom.PaymentMethodData;
import org.chromium.payments.mojom.PaymentOptions; import org.chromium.payments.mojom.PaymentOptions;
import org.chromium.payments.mojom.PaymentRequest; import org.chromium.payments.mojom.PaymentRequest;
...@@ -83,8 +84,12 @@ public interface BrowserPaymentRequest { ...@@ -83,8 +84,12 @@ public interface BrowserPaymentRequest {
/** The browser part of the {@link PaymentRequest#canMakePayment} implementation. */ /** The browser part of the {@link PaymentRequest#canMakePayment} implementation. */
void canMakePayment(); void canMakePayment();
/** Delegate to the same method of PaymentRequestImpl. */ /**
void disconnectFromClientWithDebugMessage(String debugMessage); * Delegate to the same method of PaymentRequestImpl.
* @param debugMessage The debug message shown for web developers.
* @param reason The reason of the disconnection defined in {@link PaymentErrorReason}.
*/
void disconnectFromClientWithDebugMessage(String debugMessage, int reason);
/** /**
* Close this instance. The callers of this method should stop referencing this instance upon * Close this instance. The callers of this method should stop referencing this instance upon
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package org.chromium.components.payments; package org.chromium.components.payments;
import android.text.TextUtils;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
...@@ -18,6 +20,7 @@ import org.chromium.mojo.system.MojoException; ...@@ -18,6 +20,7 @@ import org.chromium.mojo.system.MojoException;
import org.chromium.payments.mojom.PayerDetail; import org.chromium.payments.mojom.PayerDetail;
import org.chromium.payments.mojom.PaymentAddress; import org.chromium.payments.mojom.PaymentAddress;
import org.chromium.payments.mojom.PaymentDetails; import org.chromium.payments.mojom.PaymentDetails;
import org.chromium.payments.mojom.PaymentErrorReason;
import org.chromium.payments.mojom.PaymentItem; import org.chromium.payments.mojom.PaymentItem;
import org.chromium.payments.mojom.PaymentMethodData; import org.chromium.payments.mojom.PaymentMethodData;
import org.chromium.payments.mojom.PaymentOptions; import org.chromium.payments.mojom.PaymentOptions;
...@@ -55,6 +58,13 @@ public class ComponentPaymentRequestImpl { ...@@ -55,6 +58,13 @@ public class ComponentPaymentRequestImpl {
@Nullable @Nullable
private final byte[][] mCertificateChain; private final byte[][] mCertificateChain;
private final boolean mIsOffTheRecord; private final boolean mIsOffTheRecord;
@Nullable
private final PaymentOptions mPaymentOptions;
private final boolean mRequestShipping;
private final boolean mRequestPayerName;
private final boolean mRequestPayerPhone;
private final boolean mRequestPayerEmail;
private final Delegate mDelegate;
private boolean mSkipUiForNonUrlPaymentMethodIdentifiers; private boolean mSkipUiForNonUrlPaymentMethodIdentifiers;
private PaymentRequestLifecycleObserver mPaymentRequestLifecycleObserver; private PaymentRequestLifecycleObserver mPaymentRequestLifecycleObserver;
private boolean mHasClosed; private boolean mHasClosed;
...@@ -85,6 +95,50 @@ public class ComponentPaymentRequestImpl { ...@@ -85,6 +95,50 @@ public class ComponentPaymentRequestImpl {
void onMinimalUIReady(); void onMinimalUIReady();
} }
/**
* A delegate to ask questions about the system, that allows tests to inject behaviour without
* having to modify the entire system. This partially mirrors a similar C++
* (Content)PaymentRequestDelegate for the C++ implementation, allowing the test harness to
* override behaviour in both in a similar fashion.
*/
public interface Delegate {
/**
* @return Whether the merchant's WebContents is currently showing an off-the-record tab.
* Return true if the tab profile is not accessible from the WebContents.
*/
boolean isOffTheRecord();
/**
* @return A non-null string if there is an invalid SSL certificate on the currently loaded
* page.
*/
String getInvalidSslCertificateErrorMessage();
/**
* @return True if the merchant's web contents that initiated the payment request is active.
*/
boolean isWebContentsActive();
/**
* @return Whether the preferences allow CAN_MAKE_PAYMENT.
*/
boolean prefsCanMakePayment();
/**
* @return True if the UI can be skipped for "basic-card" scenarios. This will only ever be
* true in tests.
*/
boolean skipUiForBasicCard();
/**
* @return If the merchant's WebContents is running inside of a Trusted Web Activity,
* returns the package name for Trusted Web Activity. Otherwise returns an empty
* string or null.
*/
@Nullable
String getTwaPackageName();
}
/** /**
* A test-only observer for the PaymentRequest service implementation. * A test-only observer for the PaymentRequest service implementation.
*/ */
...@@ -156,20 +210,21 @@ public class ComponentPaymentRequestImpl { ...@@ -156,20 +210,21 @@ public class ComponentPaymentRequestImpl {
* @param renderFrameHost The RenderFrameHost of the merchant page. * @param renderFrameHost The RenderFrameHost of the merchant page.
* @param isOffTheRecord Whether the merchant page is in a off-the-record (e.g., incognito, * @param isOffTheRecord Whether the merchant page is in a off-the-record (e.g., incognito,
* guest mode) Tab. * guest mode) Tab.
* @param delegate The delegate of this class.
* @param skipUiForBasicCard True if the PaymentRequest UI should be skipped when the request * @param skipUiForBasicCard True if the PaymentRequest UI should be skipped when the request
* only supports basic-card methods. * only supports basic-card methods.
* @param browserPaymentRequestFactory The factory that generates BrowserPaymentRequest. * @param browserPaymentRequestFactory The factory that generates BrowserPaymentRequest.
* @return The created instance. * @return The created instance.
*/ */
public static PaymentRequest createPaymentRequest(RenderFrameHost renderFrameHost, public static PaymentRequest createPaymentRequest(RenderFrameHost renderFrameHost,
boolean isOffTheRecord, boolean skipUiForBasicCard, boolean isOffTheRecord, boolean skipUiForBasicCard, Delegate delegate,
BrowserPaymentRequest.Factory browserPaymentRequestFactory) { BrowserPaymentRequest.Factory browserPaymentRequestFactory) {
return new MojoPaymentRequestGateKeeper( return new MojoPaymentRequestGateKeeper(
(client, methodData, details, options, googlePayBridgeEligible, onClosedListener) (client, methodData, details, options, googlePayBridgeEligible, onClosedListener)
-> ComponentPaymentRequestImpl.createIfParamsValid(renderFrameHost, -> ComponentPaymentRequestImpl.createIfParamsValid(renderFrameHost,
isOffTheRecord, skipUiForBasicCard, browserPaymentRequestFactory, isOffTheRecord, skipUiForBasicCard, browserPaymentRequestFactory,
client, methodData, details, options, googlePayBridgeEligible, client, methodData, details, options, googlePayBridgeEligible,
onClosedListener)); onClosedListener, delegate));
} }
/** /**
...@@ -182,7 +237,7 @@ public class ComponentPaymentRequestImpl { ...@@ -182,7 +237,7 @@ public class ComponentPaymentRequestImpl {
BrowserPaymentRequest.Factory browserPaymentRequestFactory, BrowserPaymentRequest.Factory browserPaymentRequestFactory,
@Nullable PaymentRequestClient client, @Nullable PaymentMethodData[] methodData, @Nullable PaymentRequestClient client, @Nullable PaymentMethodData[] methodData,
@Nullable PaymentDetails details, @Nullable PaymentOptions options, @Nullable PaymentDetails details, @Nullable PaymentOptions options,
boolean googlePayBridgeEligible, Runnable onClosedListener) { boolean googlePayBridgeEligible, Runnable onClosedListener, Delegate delegate) {
assert renderFrameHost != null; assert renderFrameHost != null;
assert browserPaymentRequestFactory != null; assert browserPaymentRequestFactory != null;
assert onClosedListener != null; assert onClosedListener != null;
...@@ -223,7 +278,7 @@ public class ComponentPaymentRequestImpl { ...@@ -223,7 +278,7 @@ public class ComponentPaymentRequestImpl {
ComponentPaymentRequestImpl instance = ComponentPaymentRequestImpl instance =
new ComponentPaymentRequestImpl(client, renderFrameHost, webContents, journeyLogger, new ComponentPaymentRequestImpl(client, renderFrameHost, webContents, journeyLogger,
skipUiForBasicCard, isOffTheRecord, onClosedListener); options, skipUiForBasicCard, isOffTheRecord, onClosedListener, delegate);
instance.onCreated(); instance.onCreated();
boolean valid = instance.initAndValidate(renderFrameHost, browserPaymentRequestFactory, boolean valid = instance.initAndValidate(renderFrameHost, browserPaymentRequestFactory,
methodData, details, options, googlePayBridgeEligible, isOffTheRecord); methodData, details, options, googlePayBridgeEligible, isOffTheRecord);
...@@ -250,11 +305,13 @@ public class ComponentPaymentRequestImpl { ...@@ -250,11 +305,13 @@ public class ComponentPaymentRequestImpl {
private ComponentPaymentRequestImpl(PaymentRequestClient client, private ComponentPaymentRequestImpl(PaymentRequestClient client,
RenderFrameHost renderFrameHost, WebContents webContents, JourneyLogger journeyLogger, RenderFrameHost renderFrameHost, WebContents webContents, JourneyLogger journeyLogger,
boolean skipUiForBasicCard, boolean isOffTheRecord, Runnable onClosedListener) { @Nullable PaymentOptions options, boolean skipUiForBasicCard, boolean isOffTheRecord,
Runnable onClosedListener, Delegate delegate) {
assert client != null; assert client != null;
assert renderFrameHost != null; assert renderFrameHost != null;
assert webContents != null; assert webContents != null;
assert journeyLogger != null; assert journeyLogger != null;
assert delegate != null;
mRenderFrameHost = renderFrameHost; mRenderFrameHost = renderFrameHost;
mPaymentRequestSecurityOrigin = mRenderFrameHost.getLastCommittedOrigin(); mPaymentRequestSecurityOrigin = mRenderFrameHost.getLastCommittedOrigin();
...@@ -266,6 +323,12 @@ public class ComponentPaymentRequestImpl { ...@@ -266,6 +323,12 @@ public class ComponentPaymentRequestImpl {
mTopLevelOrigin = mTopLevelOrigin =
UrlFormatter.formatUrlForSecurityDisplay(mWebContents.getLastCommittedUrl()); UrlFormatter.formatUrlForSecurityDisplay(mWebContents.getLastCommittedUrl());
mPaymentOptions = options;
mRequestShipping = options != null && options.requestShipping;
mRequestPayerName = options != null && options.requestPayerName;
mRequestPayerPhone = options != null && options.requestPayerPhone;
mRequestPayerEmail = options != null && options.requestPayerEmail;
mMerchantName = mWebContents.getTitle(); mMerchantName = mWebContents.getTitle();
mCertificateChain = CertificateChainHelper.getCertificateChain(mWebContents); mCertificateChain = CertificateChainHelper.getCertificateChain(mWebContents);
mIsOffTheRecord = isOffTheRecord; mIsOffTheRecord = isOffTheRecord;
...@@ -273,6 +336,7 @@ public class ComponentPaymentRequestImpl { ...@@ -273,6 +336,7 @@ public class ComponentPaymentRequestImpl {
mClient = client; mClient = client;
mJourneyLogger = journeyLogger; mJourneyLogger = journeyLogger;
mOnClosedListener = onClosedListener; mOnClosedListener = onClosedListener;
mDelegate = delegate;
mHasClosed = false; mHasClosed = false;
} }
...@@ -297,6 +361,38 @@ public class ComponentPaymentRequestImpl { ...@@ -297,6 +361,38 @@ public class ComponentPaymentRequestImpl {
@Nullable PaymentDetails details, @Nullable PaymentOptions options, @Nullable PaymentDetails details, @Nullable PaymentOptions options,
boolean googlePayBridgeEligible, boolean isOffTheRecord) { boolean googlePayBridgeEligible, boolean isOffTheRecord) {
mBrowserPaymentRequest = factory.createBrowserPaymentRequest(this); mBrowserPaymentRequest = factory.createBrowserPaymentRequest(this);
mJourneyLogger.recordCheckoutStep(
org.chromium.components.payments.CheckoutFunnelStep.INITIATED);
if (!UrlUtil.isOriginAllowedToUseWebPaymentApis(mWebContents.getLastCommittedUrl())) {
Log.d(TAG, org.chromium.components.payments.ErrorStrings.PROHIBITED_ORIGIN);
Log.d(TAG,
org.chromium.components.payments.ErrorStrings
.PROHIBITED_ORIGIN_OR_INVALID_SSL_EXPLANATION);
mJourneyLogger.setAborted(
org.chromium.components.payments.AbortReason.INVALID_DATA_FROM_RENDERER);
mBrowserPaymentRequest.disconnectFromClientWithDebugMessage(
ErrorStrings.PROHIBITED_ORIGIN,
PaymentErrorReason.NOT_SUPPORTED_FOR_INVALID_ORIGIN_OR_SSL);
return false;
}
mJourneyLogger.setRequestedInformation(
mRequestShipping, mRequestPayerEmail, mRequestPayerPhone, mRequestPayerName);
String rejectShowErrorMessage = mDelegate.getInvalidSslCertificateErrorMessage();
if (!TextUtils.isEmpty(rejectShowErrorMessage)) {
Log.d(TAG, rejectShowErrorMessage);
Log.d(TAG,
org.chromium.components.payments.ErrorStrings
.PROHIBITED_ORIGIN_OR_INVALID_SSL_EXPLANATION);
mJourneyLogger.setAborted(
org.chromium.components.payments.AbortReason.INVALID_DATA_FROM_RENDERER);
mBrowserPaymentRequest.disconnectFromClientWithDebugMessage(rejectShowErrorMessage,
PaymentErrorReason.NOT_SUPPORTED_FOR_INVALID_ORIGIN_OR_SSL);
return false;
}
return mBrowserPaymentRequest.initAndValidate( return mBrowserPaymentRequest.initAndValidate(
methodData, details, options, googlePayBridgeEligible); methodData, details, options, googlePayBridgeEligible);
} }
...@@ -421,7 +517,8 @@ public class ComponentPaymentRequestImpl { ...@@ -421,7 +517,8 @@ public class ComponentPaymentRequestImpl {
assert mBrowserPaymentRequest != null; assert mBrowserPaymentRequest != null;
mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER); mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER);
mBrowserPaymentRequest.disconnectFromClientWithDebugMessage(debugMessage); mBrowserPaymentRequest.disconnectFromClientWithDebugMessage(
debugMessage, PaymentErrorReason.USER_CANCEL);
} }
/** /**
...@@ -615,6 +712,12 @@ public class ComponentPaymentRequestImpl { ...@@ -615,6 +712,12 @@ public class ComponentPaymentRequestImpl {
return mTopLevelOrigin; return mTopLevelOrigin;
} }
/** @return The payment options requested by the merchant, can be null. */
@Nullable
public PaymentOptions getPaymentOptions() {
return mPaymentOptions;
}
/** @return The RendererFrameHost of the merchant page. */ /** @return The RendererFrameHost of the merchant page. */
public RenderFrameHost getRenderFrameHost() { public RenderFrameHost getRenderFrameHost() {
return mRenderFrameHost; return mRenderFrameHost;
......
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