[Payment Request] Show PaymentDetails.error.

Before this patch, Chrome would not show the merchant's shipping address
validation error message if the merchant also provided some shipping
options.

This patch shows the error message if the merchant sets it or if the
merchant clears the shipping options in an update.

After this patch, Chrome shows the merchant's shipping address
validation error message even if the merchant provided some shipping
options.

As before, if the merchant clears shipping options in an update without
providing an error message, Chrome displays a default error message.
This behavior has not changed.

Bug: 934902
Change-Id: Idfeef5963fd1c9e12d7cdfe1ec9afedd51563a8d
Reviewed-on: https://chromium-review.googlesource.com/c/1482720
Commit-Queue: Rouslan Solomakhin <rouslan@chromium.org>
Reviewed-by: Danyao Wang <danyao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636488}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index a44b878..0b2ee6f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -800,7 +800,8 @@
 
         if (!parseAndValidateDetailsOrDisconnectFromClient(details)) return;
 
-        if (mUiShippingOptions.isEmpty() && mShippingAddressesSection.getSelectedItem() != null) {
+        if ((mUiShippingOptions.isEmpty() || !TextUtils.isEmpty(details.error))
+                && mShippingAddressesSection.getSelectedItem() != null) {
             mShippingAddressesSection.getSelectedItem().setInvalid();
             mShippingAddressesSection.setSelectedItemIndex(SectionInformation.INVALID_SELECTION);
             mShippingAddressesSection.setErrorMessage(details.error);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java
index b11962b..be3e6cd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java
@@ -180,4 +180,19 @@
                 ModalDialogProperties.ButtonType.POSITIVE, mRule.getDismissed());
         mRule.expectResultContains(new String[] {"freeShipping"});
     }
+
+    /**
+     * Show the shipping address validation error message even if the merchant provided some
+     * shipping options.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testUpdateWithError() throws Throwable {
+        mRule.triggerUIAndWait("updateWithError", mRule.getReadyToPay());
+        mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyToPay());
+        mRule.clickOnShippingAddressSuggestionOptionAndWait(1, mRule.getReadyForInput());
+        CharSequence actualString = mRule.getShippingAddressOptionRowAtIndex(0).getLabelText();
+        Assert.assertEquals("This is an error for a browsertest", actualString);
+    }
 }
diff --git a/chrome/browser/ui/views/payments/payment_request_update_with_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_update_with_browsertest.cc
index e636197b..d6e152c 100644
--- a/chrome/browser/ui/views/payments/payment_request_update_with_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_update_with_browsertest.cc
@@ -296,4 +296,35 @@
   ExpectBodyContains({"freeShipping"});
 }
 
+// Show the shipping address validation error message even if the merchant
+// provided some shipping options.
+IN_PROC_BROWSER_TEST_F(PaymentRequestUpdateWithTest, UpdateWithError) {
+  NavigateTo("/payment_request_update_with_test.html");
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);
+
+  RunJavaScriptFunctionToOpenPaymentRequestUI("updateWithError");
+
+  OpenShippingAddressSectionScreen();
+  ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
+                               DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                               DialogEvent::SPEC_DONE_UPDATING});
+  ClickOnChildInListViewAndWait(
+      /* child_index=*/1, /*total_num_children=*/2,
+      DialogViewID::SHIPPING_ADDRESS_SHEET_LIST_VIEW,
+      /*wait_for_animation=*/false);
+  // Wait for the animation here explicitly, otherwise
+  // ClickOnChildInListViewAndWait tries to install an AnimationDelegate before
+  // the animation is kicked off (since that's triggered off of the spec being
+  // updated) and this hits a DCHECK.
+  WaitForAnimation();
+
+  EXPECT_EQ(base::ASCIIToUTF16("This is an error for a browsertest"),
+            GetLabelText(DialogViewID::SHIPPING_ADDRESS_SECTION_HEADER_LABEL));
+}
+
 }  // namespace payments
diff --git a/chrome/browser/ui/views/payments/profile_list_view_controller.cc b/chrome/browser/ui/views/payments/profile_list_view_controller.cc
index 1cc609d..6b52e8e 100644
--- a/chrome/browser/ui/views/payments/profile_list_view_controller.cc
+++ b/chrome/browser/ui/views/payments/profile_list_view_controller.cc
@@ -178,8 +178,10 @@
   // | Warning icon | Warning message            |
   // ---------------------------------------------
   std::unique_ptr<views::View> CreateHeaderView() override {
-    if (!spec()->GetShippingOptions().empty())
+    if (!spec()->GetShippingOptions().empty() &&
+        spec()->selected_shipping_option_error().empty()) {
       return nullptr;
+    }
 
     auto header_view = std::make_unique<views::View>();
     // 8 pixels between the warning icon view (if present) and the text.
diff --git a/components/payments/content/payment_request_spec.cc b/components/payments/content/payment_request_spec.cc
index c8dee60..d1312bc 100644
--- a/components/payments/content/payment_request_spec.cc
+++ b/components/payments/content/payment_request_spec.cc
@@ -358,7 +358,7 @@
 
   selected_shipping_option_ = nullptr;
   selected_shipping_option_error_.clear();
-  if (details_->shipping_options->empty()) {
+  if (details_->shipping_options->empty() || !details_->error.empty()) {
     // No options are provided by the merchant.
     if (after_update) {
       // This is after an update, which means that the selected address is not
diff --git a/components/test/data/payments/payment_request_update_with_test.html b/components/test/data/payments/payment_request_update_with_test.html
index c30f3ab..7ee44c9 100644
--- a/components/test/data/payments/payment_request_update_with_test.html
+++ b/components/test/data/payments/payment_request_update_with_test.html
@@ -17,6 +17,7 @@
 <button class="small" onclick="updateWithDisplayItems()" id="updateWithDisplayItems">updateWithDisplayItems</button>
 <button class="small" onclick="updateWithShippingOptions()" id="updateWithShippingOptions">updateWithShippingOptions</button>
 <button class="small" onclick="updateWithModifiers()" id="updateWithModifiers">updateWithModifiers</button>
+<button class="small" onclick="updateWithError()" id="updateWithError">updateWithError</button>
 <pre id="result"></pre>
 <script src="util.js"></script>
 <script src="update_with.js"></script>
diff --git a/components/test/data/payments/style.css b/components/test/data/payments/style.css
index 9126316..c38b6b6 100644
--- a/components/test/data/payments/style.css
+++ b/components/test/data/payments/style.css
@@ -11,8 +11,8 @@
 }
 
 button.small {
-  font-size: 2em;
-  height: 3em;
+  font-size: 1em;
+  height: 2em;
   width: 100%;
 }
 
diff --git a/components/test/data/payments/update_with.js b/components/test/data/payments/update_with.js
index 9791173..7feb3c0 100644
--- a/components/test/data/payments/update_with.js
+++ b/components/test/data/payments/update_with.js
@@ -157,3 +157,20 @@
   });
   showPaymentRequest(pr);
 }
+
+/**
+ * Calls updateWith() with an error.
+ */
+function updateWithError() {  // eslint-disable-line no-unused-vars
+  var pr = buildPaymentRequest();
+  var errorDetails = {
+    error: 'This is an error for a browsertest',
+  };
+  pr.addEventListener('shippingaddresschange', function(e) {
+    e.updateWith(errorDetails);
+  });
+  pr.addEventListener('shippingoptionchange', function(e) {
+    e.updateWith(errorDetails);
+  });
+  showPaymentRequest(pr);
+}