Commit 3c4e5a01 authored by Liquan (Max) Gu's avatar Liquan (Max) Gu Committed by Commit Bot

[PRImpl] Move MethodData into PaymentRequestSpec

* Move PaymentRequestImpl's mMethodData into PaymentRequestSpec.
* mSpec's parsed values are still be available after mSpec is closed.
This change is motivated by the fact that AutofillPaymentAppFactory
accesses PRImpl#getMethodData() after mSpec is closed, causing
NullPointerException[1]. In particular, this change includes:
 - mSpec is no longer set null when destroyed.
 - the callers check mSpec.isDestroyed() (added method) before using
its updateWith(), retry(), recomputeSpecForDetails(),
selectedShippingOptionError(). Other methods are accessible even after
mSpec is destroyed.

[1] https://ci.chromium.org/p/chromium/builders/try/android-marshmallow-arm64-rel/648624?

Bug: 1102522

Change-Id: I018d35f50bd2ab42aa4d3b0c80e5245ad86394fc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2390867
Commit-Queue: Liquan (Max) Gu <maxlg@chromium.org>
Reviewed-by: default avatarSahel Sharify <sahel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805322}
parent 753c2f64
...@@ -225,7 +225,6 @@ public class PaymentRequestImpl ...@@ -225,7 +225,6 @@ public class PaymentRequestImpl
private boolean mHasClosed; private boolean mHasClosed;
private PaymentRequestSpec mSpec; private PaymentRequestSpec mSpec;
private Map<String, PaymentMethodData> mMethodData;
private int mShippingType; private int mShippingType;
private boolean mIsFinishedQueryingPaymentApps; private boolean mIsFinishedQueryingPaymentApps;
private List<PaymentApp> mPendingApps = new ArrayList<>(); private List<PaymentApp> mPendingApps = new ArrayList<>();
...@@ -324,10 +323,9 @@ public class PaymentRequestImpl ...@@ -324,10 +323,9 @@ public class PaymentRequestImpl
// Implement BrowserPaymentRequest: // Implement BrowserPaymentRequest:
@Override @Override
public boolean initAndValidate(PaymentMethodData[] methodData, 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;
mMethodData = new HashMap<>();
mJourneyLogger.recordCheckoutStep(CheckoutFunnelStep.INITIATED); mJourneyLogger.recordCheckoutStep(CheckoutFunnelStep.INITIATED);
mPaymentOptions = options; mPaymentOptions = options;
...@@ -360,22 +358,23 @@ public class PaymentRequestImpl ...@@ -360,22 +358,23 @@ public class PaymentRequestImpl
} }
boolean googlePayBridgeActivated = googlePayBridgeEligible boolean googlePayBridgeActivated = googlePayBridgeEligible
&& SkipToGPayHelper.canActivateExperiment(mWebContents, methodData); && SkipToGPayHelper.canActivateExperiment(mWebContents, rawMethodData);
mMethodData = getValidatedMethodData( Map<String, PaymentMethodData> methodData = getValidatedMethodData(
methodData, googlePayBridgeActivated, mPaymentUIsManager.getCardEditor()); rawMethodData, googlePayBridgeActivated, mPaymentUIsManager.getCardEditor());
if (mMethodData == null) {
if (methodData == null) {
mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER); mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER);
disconnectFromClientWithDebugMessage(ErrorStrings.INVALID_PAYMENT_METHODS_OR_DATA); disconnectFromClientWithDebugMessage(ErrorStrings.INVALID_PAYMENT_METHODS_OR_DATA);
return false; return false;
} }
if (googlePayBridgeActivated) { if (googlePayBridgeActivated) {
PaymentMethodData data = mMethodData.get(MethodStrings.GOOGLE_PAY); PaymentMethodData data = methodData.get(MethodStrings.GOOGLE_PAY);
mSkipToGPayHelper = new SkipToGPayHelper(options, data.gpayBridgeData); mSkipToGPayHelper = new SkipToGPayHelper(options, data.gpayBridgeData);
} }
mQueryForQuota = new HashMap<>(mMethodData); mQueryForQuota = new HashMap<>(methodData);
if (mQueryForQuota.containsKey(MethodStrings.BASIC_CARD) if (mQueryForQuota.containsKey(MethodStrings.BASIC_CARD)
&& PaymentFeatureList.isEnabledOrExperimentalFeaturesEnabled( && PaymentFeatureList.isEnabledOrExperimentalFeaturesEnabled(
PaymentFeatureList.STRICT_HAS_ENROLLED_AUTOFILL_INSTRUMENT)) { PaymentFeatureList.STRICT_HAS_ENROLLED_AUTOFILL_INSTRUMENT)) {
...@@ -385,7 +384,7 @@ public class PaymentRequestImpl ...@@ -385,7 +384,7 @@ public class PaymentRequestImpl
mQueryForQuota.put("basic-card-payment-options", paymentMethodData); mQueryForQuota.put("basic-card-payment-options", paymentMethodData);
} }
mSpec = new PaymentRequestSpec(mPaymentOptions, mMethodData.values()); mSpec = new PaymentRequestSpec(mPaymentOptions, methodData);
if (parseAndValidateDetails(details) if (parseAndValidateDetails(details)
&& parseAndValidateDetailsForSkipToGPayHelper(details)) { && parseAndValidateDetailsForSkipToGPayHelper(details)) {
mPaymentUIsManager.updateDetailsOnPaymentRequestUI( mPaymentUIsManager.updateDetailsOnPaymentRequestUI(
...@@ -420,7 +419,7 @@ public class PaymentRequestImpl ...@@ -420,7 +419,7 @@ public class PaymentRequestImpl
// better captures this group of interest than requestedMethodBasicCard. // better captures this group of interest than requestedMethodBasicCard.
boolean requestedMethodOther = false; boolean requestedMethodOther = false;
mURLPaymentMethodIdentifiersSupported = false; mURLPaymentMethodIdentifiersSupported = false;
for (String methodName : mMethodData.keySet()) { for (String methodName : mSpec.getMethodData().keySet()) {
switch (methodName) { switch (methodName) {
case MethodStrings.ANDROID_PAY: case MethodStrings.ANDROID_PAY:
case MethodStrings.GOOGLE_PAY: case MethodStrings.GOOGLE_PAY:
...@@ -906,6 +905,8 @@ public class PaymentRequestImpl ...@@ -906,6 +905,8 @@ public class PaymentRequestImpl
@Override @Override
public void updateWith(PaymentDetails details) { public void updateWith(PaymentDetails details) {
if (mComponentPaymentRequestImpl == null) return; if (mComponentPaymentRequestImpl == null) return;
// mSpec.updateWith() can be used only when mSpec has not been destroyed.
assert !mSpec.isDestroyed();
if (mWaitForUpdatedDetails) { if (mWaitForUpdatedDetails) {
initializeWithUpdatedDetails(details); initializeWithUpdatedDetails(details);
...@@ -965,6 +966,8 @@ public class PaymentRequestImpl ...@@ -965,6 +966,8 @@ public class PaymentRequestImpl
private void initializeWithUpdatedDetails(PaymentDetails details) { private void initializeWithUpdatedDetails(PaymentDetails details) {
assert mWaitForUpdatedDetails; assert mWaitForUpdatedDetails;
// mSpec.updateWith() can be used only when mSpec has not been destroyed.
assert !mSpec.isDestroyed();
ChromeActivity chromeActivity = ChromeActivity.fromWebContents(mWebContents); ChromeActivity chromeActivity = ChromeActivity.fromWebContents(mWebContents);
if (chromeActivity == null) { if (chromeActivity == null) {
...@@ -1027,6 +1030,9 @@ public class PaymentRequestImpl ...@@ -1027,6 +1030,9 @@ public class PaymentRequestImpl
@Override @Override
public void onPaymentDetailsNotUpdated() { public void onPaymentDetailsNotUpdated() {
if (mComponentPaymentRequestImpl == null) return; if (mComponentPaymentRequestImpl == null) return;
// mSpec.recomputeSpecForDetails(), mSpec.selectedShippingOptionError() can be used only
// when mSpec has not been destroyed.
assert !mSpec.isDestroyed();
if (mPaymentUIsManager.getPaymentRequestUI() == null) { if (mPaymentUIsManager.getPaymentRequestUI() == null) {
mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER); mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER);
...@@ -1292,8 +1298,8 @@ public class PaymentRequestImpl ...@@ -1292,8 +1298,8 @@ public class PaymentRequestImpl
Map<String, PaymentDetailsModifier> modifiers = new HashMap<>(); Map<String, PaymentDetailsModifier> modifiers = new HashMap<>();
boolean isGooglePaymentApp = false; boolean isGooglePaymentApp = false;
for (String paymentMethodName : mInvokedPaymentApp.getInstrumentMethodNames()) { for (String paymentMethodName : mInvokedPaymentApp.getInstrumentMethodNames()) {
if (mMethodData.containsKey(paymentMethodName)) { if (mSpec.getMethodData().containsKey(paymentMethodName)) {
methodData.put(paymentMethodName, mMethodData.get(paymentMethodName)); methodData.put(paymentMethodName, mSpec.getMethodData().get(paymentMethodName));
} }
if (mSpec.getModifiers().containsKey(paymentMethodName)) { if (mSpec.getModifiers().containsKey(paymentMethodName)) {
modifiers.put(paymentMethodName, mSpec.getModifiers().get(paymentMethodName)); modifiers.put(paymentMethodName, mSpec.getModifiers().get(paymentMethodName));
...@@ -1464,6 +1470,8 @@ public class PaymentRequestImpl ...@@ -1464,6 +1470,8 @@ public class PaymentRequestImpl
@Override @Override
public void retry(PaymentValidationErrors errors) { public void retry(PaymentValidationErrors errors) {
if (mComponentPaymentRequestImpl == null) return; if (mComponentPaymentRequestImpl == null) return;
// mSpec.retry() can be used only when mSpec has not been destroyed.
assert !mSpec.isDestroyed();
if (!PaymentValidator.validatePaymentValidationErrors(errors)) { if (!PaymentValidator.validatePaymentValidationErrors(errors)) {
mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER); mJourneyLogger.setAborted(AbortReason.INVALID_DATA_FROM_RENDERER);
...@@ -1618,7 +1626,7 @@ public class PaymentRequestImpl ...@@ -1618,7 +1626,7 @@ public class PaymentRequestImpl
// PaymentAppFactoryParams implementation. // PaymentAppFactoryParams implementation.
@Override @Override
public Map<String, PaymentMethodData> getMethodData() { public Map<String, PaymentMethodData> getMethodData() {
return mMethodData; return mSpec.getMethodData();
} }
// PaymentAppFactoryParams implementation. // PaymentAppFactoryParams implementation.
...@@ -1899,11 +1907,11 @@ public class PaymentRequestImpl ...@@ -1899,11 +1907,11 @@ public class PaymentRequestImpl
} }
if (TextUtils.isEmpty(mRejectShowErrorMessage) && !isInTwa() if (TextUtils.isEmpty(mRejectShowErrorMessage) && !isInTwa()
&& mMethodData.get(MethodStrings.GOOGLE_PLAY_BILLING) != null) { && mSpec.getMethodData().get(MethodStrings.GOOGLE_PLAY_BILLING) != null) {
mRejectShowErrorMessage = ErrorStrings.APP_STORE_METHOD_ONLY_SUPPORTED_IN_TWA; mRejectShowErrorMessage = ErrorStrings.APP_STORE_METHOD_ONLY_SUPPORTED_IN_TWA;
} }
disconnectFromClientWithDebugMessage( disconnectFromClientWithDebugMessage(
ErrorMessageUtil.getNotSupportedErrorMessage(mMethodData.keySet()) ErrorMessageUtil.getNotSupportedErrorMessage(mSpec.getMethodData().keySet())
+ (TextUtils.isEmpty(mRejectShowErrorMessage) + (TextUtils.isEmpty(mRejectShowErrorMessage)
? "" ? ""
: " " + mRejectShowErrorMessage), : " " + mRejectShowErrorMessage),
...@@ -1928,7 +1936,7 @@ public class PaymentRequestImpl ...@@ -1928,7 +1936,7 @@ public class PaymentRequestImpl
* @return Whether client has been disconnected. * @return Whether client has been disconnected.
*/ */
private boolean disconnectForStrictShow() { private boolean disconnectForStrictShow() {
if (!mIsUserGestureShow || !mMethodData.containsKey(MethodStrings.BASIC_CARD) if (!mIsUserGestureShow || !mSpec.getMethodData().containsKey(MethodStrings.BASIC_CARD)
|| mHasEnrolledInstrument || mHasNonAutofillApp || mHasEnrolledInstrument || mHasNonAutofillApp
|| !PaymentFeatureList.isEnabledOrExperimentalFeaturesEnabled( || !PaymentFeatureList.isEnabledOrExperimentalFeaturesEnabled(
PaymentFeatureList.STRICT_HAS_ENROLLED_AUTOFILL_INSTRUMENT)) { PaymentFeatureList.STRICT_HAS_ENROLLED_AUTOFILL_INSTRUMENT)) {
...@@ -1940,7 +1948,7 @@ public class PaymentRequestImpl ...@@ -1940,7 +1948,7 @@ public class PaymentRequestImpl
} }
mRejectShowErrorMessage = ErrorStrings.STRICT_BASIC_CARD_SHOW_REJECT; mRejectShowErrorMessage = ErrorStrings.STRICT_BASIC_CARD_SHOW_REJECT;
disconnectFromClientWithDebugMessage( disconnectFromClientWithDebugMessage(
ErrorMessageUtil.getNotSupportedErrorMessage(mMethodData.keySet()) + " " ErrorMessageUtil.getNotSupportedErrorMessage(mSpec.getMethodData().keySet()) + " "
+ mRejectShowErrorMessage, + mRejectShowErrorMessage,
PaymentErrorReason.NOT_SUPPORTED); PaymentErrorReason.NOT_SUPPORTED);
...@@ -2133,7 +2141,6 @@ public class PaymentRequestImpl ...@@ -2133,7 +2141,6 @@ public class PaymentRequestImpl
if (mSpec != null) { if (mSpec != null) {
mSpec.destroy(); mSpec.destroy();
mSpec = null;
} }
PaymentDetailsUpdateServiceHelper.getInstance().reset(); PaymentDetailsUpdateServiceHelper.getInstance().reset();
} }
......
...@@ -19,7 +19,6 @@ import org.chromium.payments.mojom.PaymentShippingOption; ...@@ -19,7 +19,6 @@ import org.chromium.payments.mojom.PaymentShippingOption;
import org.chromium.payments.mojom.PaymentValidationErrors; import org.chromium.payments.mojom.PaymentValidationErrors;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
...@@ -38,17 +37,17 @@ public class PaymentRequestSpec { ...@@ -38,17 +37,17 @@ public class PaymentRequestSpec {
private List<PaymentItem> mRawLineItems; private List<PaymentItem> mRawLineItems;
private PaymentItem mRawTotal; private PaymentItem mRawTotal;
private final PaymentOptions mOptions; private final PaymentOptions mOptions;
private final Collection<PaymentMethodData> mMethodData; private final Map<String, PaymentMethodData> mMethodData;
private long mNativePointer; private long mNativePointer;
/** /**
* Creates an instance to store the information received from the renderer that invoked the * Creates an instance to store the information received from the renderer that invoked the
* Payment Request API. * Payment Request API.
* @param options The payment options, e.g., whether shipping is requested. * @param options The payment options, e.g., whether shipping is requested.
* @param methodData The list of supported payment method identifiers and corresponding payment * @param methodData The map of supported payment method identifiers and corresponding payment
* method specific data. * method specific data.
*/ */
public PaymentRequestSpec(PaymentOptions options, Collection<PaymentMethodData> methodData) { public PaymentRequestSpec(PaymentOptions options, Map<String, PaymentMethodData> methodData) {
mOptions = options; mOptions = options;
mMethodData = methodData; mMethodData = methodData;
} }
...@@ -60,20 +59,38 @@ public class PaymentRequestSpec { ...@@ -60,20 +59,38 @@ public class PaymentRequestSpec {
* @param appLocale The current application locale. * @param appLocale The current application locale.
*/ */
public void createNative(PaymentDetails details, String appLocale) { public void createNative(PaymentDetails details, String appLocale) {
mNativePointer = PaymentRequestSpecJni.get().create(mOptions.serialize(), mNativePointer =
details.serialize(), MojoStructCollection.serialize(mMethodData), appLocale); PaymentRequestSpecJni.get().create(mOptions.serialize(), details.serialize(),
MojoStructCollection.serialize(mMethodData.values()), appLocale);
}
/** @return Whether destroy() has been called. */
public boolean isDestroyed() {
return mNativePointer == 0;
}
/**
* @return The map of supported payment method identifiers and corresponding payment
* method specific data. This value is still available after the instance is destroyed.
*/
public Map<String, PaymentMethodData> getMethodData() {
return mMethodData;
} }
/** /**
* A mapping from method names to modifiers, which include modified totals and additional line * A mapping from method names to modifiers, which include modified totals and additional line
* items. Used to display modified totals for each payment apps, modified total in order * items. Used to display modified totals for each payment apps, modified total in order
* summary, and additional line items in order summary. * summary, and additional line items in order summary. This value is still available after the
* instance is destroyed.
*/ */
public Map<String, PaymentDetailsModifier> getModifiers() { public Map<String, PaymentDetailsModifier> getModifiers() {
return mModifiers; return mModifiers;
} }
/** @return The id of the request, found in PaymentDetails. */ /**
* @return The id of the request, found in PaymentDetails. This value is still available after
* the instance is destroyed.
*/
public String getId() { public String getId() {
return mId; return mId;
} }
...@@ -85,7 +102,8 @@ public class PaymentRequestSpec { ...@@ -85,7 +102,8 @@ public class PaymentRequestSpec {
/** /**
* The raw shipping options, as it was received from the website. This data is passed to the * The raw shipping options, as it was received from the website. This data is passed to the
* payment app when the app is responsible for handling shipping address. * payment app when the app is responsible for handling shipping address. This value is still
* available after the instance is destroyed.
*/ */
public List<PaymentShippingOption> getRawShippingOptions() { public List<PaymentShippingOption> getRawShippingOptions() {
return mRawShippingOptions; return mRawShippingOptions;
...@@ -98,7 +116,7 @@ public class PaymentRequestSpec { ...@@ -98,7 +116,7 @@ public class PaymentRequestSpec {
/** /**
* The raw items in the shopping cart, as they were received from the website. This data is * The raw items in the shopping cart, as they were received from the website. This data is
* passed to the payment app. * passed to the payment app. This value is still available after the instance is destroyed.
*/ */
public List<PaymentItem> getRawLineItems() { public List<PaymentItem> getRawLineItems() {
return mRawLineItems; return mRawLineItems;
...@@ -111,7 +129,7 @@ public class PaymentRequestSpec { ...@@ -111,7 +129,7 @@ public class PaymentRequestSpec {
/** /**
* The raw total amount being charged, as it was received from the website. This data is passed * The raw total amount being charged, as it was received from the website. This data is passed
* to the payment app. * to the payment app. This value is still available after the instance is destroyed.
*/ */
public PaymentItem getRawTotal() { public PaymentItem getRawTotal() {
return mRawTotal; return mRawTotal;
...@@ -124,7 +142,7 @@ public class PaymentRequestSpec { ...@@ -124,7 +142,7 @@ public class PaymentRequestSpec {
/** /**
* Called when the renderer updates the payment details in response to, e.g., new shipping * Called when the renderer updates the payment details in response to, e.g., new shipping
* address. * address. This cannot be used after the instance is destroyed.
* @param details The updated payment details, e.g., the updated total amount. * @param details The updated payment details, e.g., the updated total amount.
*/ */
public void updateWith(PaymentDetails details) { public void updateWith(PaymentDetails details) {
...@@ -132,7 +150,8 @@ public class PaymentRequestSpec { ...@@ -132,7 +150,8 @@ public class PaymentRequestSpec {
} }
/** /**
* Called when merchant retries a failed payment. * Called when merchant retries a failed payment. This cannot be used after the instance is
* destroyed.
* @param validationErrors The information about the fields that failed the validation. * @param validationErrors The information about the fields that failed the validation.
*/ */
public void retry(PaymentValidationErrors validationErrors) { public void retry(PaymentValidationErrors validationErrors) {
...@@ -140,14 +159,15 @@ public class PaymentRequestSpec { ...@@ -140,14 +159,15 @@ public class PaymentRequestSpec {
} }
/** /**
* Recomputes spec based on details. * Recomputes spec based on details. This cannot be used after the instance is destroyed.
*/ */
public void recomputeSpecForDetails() { public void recomputeSpecForDetails() {
PaymentRequestSpecJni.get().recomputeSpecForDetails(mNativePointer); PaymentRequestSpecJni.get().recomputeSpecForDetails(mNativePointer);
} }
/** /**
* Returns the selected shipping option error. * Returns the selected shipping option error. This cannot be used after the instance is
* destroyed.
*/ */
@Nullable @Nullable
public String selectedShippingOptionError() { public String selectedShippingOptionError() {
......
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