summaryrefslogtreecommitdiff
path: root/chromium/components/payments/core
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/payments/core')
-rw-r--r--chromium/components/payments/core/BUILD.gn42
-rw-r--r--chromium/components/payments/core/DEPS2
-rw-r--r--chromium/components/payments/core/OWNERS4
-rw-r--r--chromium/components/payments/core/address_normalization_manager.cc103
-rw-r--r--chromium/components/payments/core/address_normalization_manager.h107
-rw-r--r--chromium/components/payments/core/address_normalization_manager_unittest.cc65
-rw-r--r--chromium/components/payments/core/address_normalizer.h43
-rw-r--r--chromium/components/payments/core/address_normalizer_impl.cc (renamed from chromium/components/payments/core/address_normalizer.cc)16
-rw-r--r--chromium/components/payments/core/address_normalizer_impl.h62
-rw-r--r--chromium/components/payments/core/address_normalizer_impl_unittest.cc (renamed from chromium/components/payments/core/address_normalizer_unittest.cc)12
-rw-r--r--chromium/components/payments/core/autofill_payment_instrument.cc132
-rw-r--r--chromium/components/payments/core/autofill_payment_instrument.h28
-rw-r--r--chromium/components/payments/core/autofill_payment_instrument_unittest.cc250
-rw-r--r--chromium/components/payments/core/can_make_payment_query.cc44
-rw-r--r--chromium/components/payments/core/can_make_payment_query.h50
-rw-r--r--chromium/components/payments/core/currency_formatter.cc8
-rw-r--r--chromium/components/payments/core/currency_formatter_unittest.cc11
-rw-r--r--chromium/components/payments/core/journey_logger.cc119
-rw-r--r--chromium/components/payments/core/journey_logger.h146
-rw-r--r--chromium/components/payments/core/journey_logger_unittest.cc199
-rw-r--r--chromium/components/payments/core/payment_instrument.cc4
-rw-r--r--chromium/components/payments/core/payment_instrument.h14
-rw-r--r--chromium/components/payments/core/payment_prefs.cc18
-rw-r--r--chromium/components/payments/core/payment_prefs.h22
-rw-r--r--chromium/components/payments/core/payment_request_data_util.cc100
-rw-r--r--chromium/components/payments/core/payment_request_data_util.h20
-rw-r--r--chromium/components/payments/core/payment_request_data_util_unittest.cc170
-rw-r--r--chromium/components/payments/core/payment_request_delegate.h46
-rw-r--r--chromium/components/payments/core/payments_profile_comparator.cc286
-rw-r--r--chromium/components/payments/core/payments_profile_comparator.h124
-rw-r--r--chromium/components/payments/core/payments_profile_comparator_unittest.cc464
-rw-r--r--chromium/components/payments/core/profile_util.cc123
-rw-r--r--chromium/components/payments/core/profile_util.h69
-rw-r--r--chromium/components/payments/core/profile_util_unittest.cc231
-rw-r--r--chromium/components/payments/core/strings_util.cc41
-rw-r--r--chromium/components/payments/core/strings_util.h16
-rw-r--r--chromium/components/payments/core/subkey_requester.cc129
-rw-r--r--chromium/components/payments/core/subkey_requester.h73
-rw-r--r--chromium/components/payments/core/subkey_requester_unittest.cc194
-rw-r--r--chromium/components/payments/core/test_address_normalizer.cc38
-rw-r--r--chromium/components/payments/core/test_address_normalizer.h46
-rw-r--r--chromium/components/payments/core/test_payment_request_delegate.cc88
-rw-r--r--chromium/components/payments/core/test_payment_request_delegate.h59
43 files changed, 3071 insertions, 747 deletions
diff --git a/chromium/components/payments/core/BUILD.gn b/chromium/components/payments/core/BUILD.gn
index 1892be27352..eaa3d417ba7 100644
--- a/chromium/components/payments/core/BUILD.gn
+++ b/chromium/components/payments/core/BUILD.gn
@@ -4,12 +4,17 @@
static_library("core") {
sources = [
- "address_normalizer.cc",
+ "address_normalization_manager.cc",
+ "address_normalization_manager.h",
"address_normalizer.h",
+ "address_normalizer_impl.cc",
+ "address_normalizer_impl.h",
"autofill_payment_instrument.cc",
"autofill_payment_instrument.h",
"basic_card_response.cc",
"basic_card_response.h",
+ "can_make_payment_query.cc",
+ "can_make_payment_query.h",
"currency_formatter.cc",
"currency_formatter.h",
"journey_logger.cc",
@@ -21,18 +26,24 @@ static_library("core") {
"payment_method_data.cc",
"payment_method_data.h",
"payment_options_provider.h",
+ "payment_prefs.cc",
+ "payment_prefs.h",
"payment_request_data_util.cc",
"payment_request_data_util.h",
"payment_request_delegate.h",
- "profile_util.cc",
- "profile_util.h",
+ "payments_profile_comparator.cc",
+ "payments_profile_comparator.h",
"strings_util.cc",
"strings_util.h",
+ "subkey_requester.cc",
+ "subkey_requester.h",
]
deps = [
"//base",
"//components/autofill/core/browser",
+ "//components/keyed_service/core",
+ "//components/pref_registry",
"//components/strings:components_strings_grit",
"//components/ukm",
"//third_party/libphonenumber",
@@ -46,10 +57,27 @@ static_library("core") {
]
}
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "test_address_normalizer.cc",
+ "test_address_normalizer.h",
+ "test_payment_request_delegate.cc",
+ "test_payment_request_delegate.h",
+ ]
+
+ deps = [
+ ":core",
+ "//base",
+ "//components/autofill/core/browser",
+ ]
+}
+
source_set("unit_tests") {
testonly = true
sources = [
- "address_normalizer_unittest.cc",
+ "address_normalization_manager_unittest.cc",
+ "address_normalizer_impl_unittest.cc",
"autofill_payment_instrument_unittest.cc",
"basic_card_response_unittest.cc",
"currency_formatter_unittest.cc",
@@ -57,20 +85,24 @@ source_set("unit_tests") {
"payment_address_unittest.cc",
"payment_method_data_unittest.cc",
"payment_request_data_util_unittest.cc",
- "profile_util_unittest.cc",
+ "payments_profile_comparator_unittest.cc",
+ "subkey_requester_unittest.cc",
]
deps = [
":core",
+ ":test_support",
"//base",
"//base/test:test_support",
"//components/autofill/core/browser",
"//components/autofill/core/browser:test_support",
"//components/metrics/proto",
+ "//components/strings:components_strings_grit",
"//components/ukm",
"//components/ukm:test_support",
"//testing/gmock",
"//testing/gtest",
"//third_party/libaddressinput:test_support",
+ "//ui/base",
]
}
diff --git a/chromium/components/payments/core/DEPS b/chromium/components/payments/core/DEPS
index 9347542b4fc..20bc8d9c09b 100644
--- a/chromium/components/payments/core/DEPS
+++ b/chromium/components/payments/core/DEPS
@@ -2,7 +2,9 @@ include_rules = [
"-components/payments/content",
"-content",
"+components/autofill/core",
+ "+components/keyed_service/core",
"+components/metrics",
+ "+components/pref_registry",
"+components/strings",
"+components/ukm",
"+third_party/libaddressinput",
diff --git a/chromium/components/payments/core/OWNERS b/chromium/components/payments/core/OWNERS
new file mode 100644
index 00000000000..396d240ce37
--- /dev/null
+++ b/chromium/components/payments/core/OWNERS
@@ -0,0 +1,4 @@
+per-file journey_logger*=sebsg@chromium.org
+per-file address_normalizer*=sebsg@chromium.org
+
+# COMPONENT: UI>Browser>Autofill>Payments
diff --git a/chromium/components/payments/core/address_normalization_manager.cc b/chromium/components/payments/core/address_normalization_manager.cc
new file mode 100644
index 00000000000..21475900670
--- /dev/null
+++ b/chromium/components/payments/core/address_normalization_manager.cc
@@ -0,0 +1,103 @@
+// Copyright 2017 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.
+
+#include "components/payments/core/address_normalization_manager.h"
+
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_data_util.h"
+#include "components/autofill/core/browser/field_types.h"
+
+namespace payments {
+
+namespace {
+constexpr int kAddressNormalizationTimeoutSeconds = 5;
+} // namespace
+
+AddressNormalizationManager::AddressNormalizationManager(
+ std::unique_ptr<AddressNormalizer> address_normalizer,
+ const std::string& default_country_code)
+ : default_country_code_(default_country_code),
+ address_normalizer_(std::move(address_normalizer)) {
+ DCHECK(autofill::data_util::IsValidCountryCode(default_country_code));
+ DCHECK(address_normalizer_);
+
+ // Start loading rules for the default country code. This happens
+ // asynchronously, and will speed up normalization later if the rules for the
+ // address' region have already been loaded.
+ address_normalizer_->LoadRulesForRegion(default_country_code);
+}
+
+AddressNormalizationManager::~AddressNormalizationManager() {}
+
+void AddressNormalizationManager::FinalizeWithCompletionCallback(
+ base::OnceClosure completion_callback) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ completion_callback_ = std::move(completion_callback);
+ accepting_requests_ = false;
+ MaybeRunCompletionCallback();
+}
+
+void AddressNormalizationManager::StartNormalizingAddress(
+ autofill::AutofillProfile* profile) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(accepting_requests_) << "FinalizeWithCompletionCallback has been "
+ "called, cannot normalize more addresses";
+
+ delegates_.push_back(base::MakeUnique<NormalizerDelegate>(
+ this, address_normalizer_.get(), profile));
+}
+
+void AddressNormalizationManager::MaybeRunCompletionCallback() {
+ if (accepting_requests_ || !completion_callback_)
+ return;
+
+ for (const auto& delegate : delegates_) {
+ if (!delegate->has_completed())
+ return;
+ }
+
+ // We're no longer accepting requests, and all the delegates have completed.
+ // Now's the time to run the completion callback.
+ std::move(completion_callback_).Run();
+}
+
+AddressNormalizationManager::NormalizerDelegate::NormalizerDelegate(
+ AddressNormalizationManager* owner,
+ AddressNormalizer* address_normalizer,
+ autofill::AutofillProfile* profile)
+ : owner_(owner), profile_(profile) {
+ DCHECK(owner_);
+ DCHECK(profile_);
+
+ std::string country_code =
+ base::UTF16ToUTF8(profile_->GetRawInfo(autofill::ADDRESS_HOME_COUNTRY));
+ if (!autofill::data_util::IsValidCountryCode(country_code))
+ country_code = owner_->default_country_code_;
+
+ address_normalizer->StartAddressNormalization(
+ *profile_, country_code, kAddressNormalizationTimeoutSeconds, this);
+}
+
+void AddressNormalizationManager::NormalizerDelegate::OnAddressNormalized(
+ const autofill::AutofillProfile& normalized_profile) {
+ OnCompletion(normalized_profile);
+}
+
+void AddressNormalizationManager::NormalizerDelegate::OnCouldNotNormalize(
+ const autofill::AutofillProfile& profile) {
+ // Since the phone number is formatted in either case, this profile should
+ // be used.
+ OnCompletion(profile);
+}
+
+void AddressNormalizationManager::NormalizerDelegate::OnCompletion(
+ const autofill::AutofillProfile& profile) {
+ DCHECK(!has_completed_);
+ has_completed_ = true;
+ *profile_ = profile;
+ owner_->MaybeRunCompletionCallback();
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/core/address_normalization_manager.h b/chromium/components/payments/core/address_normalization_manager.h
new file mode 100644
index 00000000000..a838dbce183
--- /dev/null
+++ b/chromium/components/payments/core/address_normalization_manager.h
@@ -0,0 +1,107 @@
+// Copyright 2017 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.
+
+#ifndef COMPONENTS_PAYMENTS_CORE_ADDRESS_NORMALIZATION_MANAGER_H_
+#define COMPONENTS_PAYMENTS_CORE_ADDRESS_NORMALIZATION_MANAGER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/threading/thread_checker.h"
+#include "components/payments/core/address_normalizer.h"
+
+namespace autofill {
+class AutofillProfile;
+} // namespace autofill
+
+namespace payments {
+
+class AddressNormalizer;
+
+// Class to handle multiple concurrent address normalization requests. This
+// class is not thread-safe.
+class AddressNormalizationManager {
+ public:
+ // Initializes an AddressNormalizationManager. |default_country_code| will be
+ // used if the country code in an AutofillProfile to normalize is not valid.
+ // The AddressNormalizationManager takes ownership of |address_normalizer|.
+ AddressNormalizationManager(
+ std::unique_ptr<AddressNormalizer> address_normalizer,
+ const std::string& default_country_code);
+
+ ~AddressNormalizationManager();
+
+ // Stops accepting normalization requests. If all the address normalization
+ // requests have already completed, |completion_callback| will be called
+ // before this method returns. Otherwise, it will be called as soon as the
+ // last pending request completes.
+ void FinalizeWithCompletionCallback(base::OnceClosure completion_callback);
+
+ // Normalizes the address in |profile|. This may or may not happen
+ // asynchronously. On completion, the address in |profile| will be updated
+ // with the normalized address.
+ void StartNormalizingAddress(autofill::AutofillProfile* profile);
+
+ private:
+ // Implements the payments::AddressNormalizer::Delegate interface, and
+ // notifies its parent AddressNormalizationManager when normalization has
+ // completed.
+ class NormalizerDelegate : public AddressNormalizer::Delegate {
+ public:
+ // |owner| is the parent AddressNormalizationManager, |address_normalizer|
+ // is a pointer to an instance of AddressNormalizer which will handle
+ // normalization of |profile|. |profile| will be updated when normalization
+ // is complete.
+ NormalizerDelegate(AddressNormalizationManager* owner,
+ AddressNormalizer* address_normalizer,
+ autofill::AutofillProfile* profile);
+
+ // Returns whether this delegate has completed or not.
+ bool has_completed() const { return has_completed_; }
+
+ // payments::AddressNormalizer::Delegate:
+ void OnAddressNormalized(
+ const autofill::AutofillProfile& normalized_profile) override;
+ void OnCouldNotNormalize(const autofill::AutofillProfile& profile) override;
+
+ private:
+ // Helper method that handles when normalization has completed.
+ void OnCompletion(const autofill::AutofillProfile& profile);
+
+ bool has_completed_ = false;
+ AddressNormalizationManager* owner_ = nullptr;
+ autofill::AutofillProfile* profile_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(NormalizerDelegate);
+ };
+
+ friend class NormalizerDelegate;
+
+ // Runs the completion callback if all the delegates have completed.
+ void MaybeRunCompletionCallback();
+
+ // Whether the AddressNormalizationManager is still accepting requests or not.
+ bool accepting_requests_ = true;
+
+ // The default country code to use if a profile does not have a valid country.
+ const std::string default_country_code_;
+
+ // The callback to execute when all addresses have been normalized.
+ base::OnceClosure completion_callback_;
+
+ // Storage for all the delegates that handle the normalization requests.
+ std::vector<std::unique_ptr<NormalizerDelegate>> delegates_;
+
+ // The AddressNormalizer to use. Owned by this class.
+ std::unique_ptr<AddressNormalizer> address_normalizer_;
+
+ THREAD_CHECKER(thread_checker_);
+ DISALLOW_COPY_AND_ASSIGN(AddressNormalizationManager);
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CORE_ADDRESS_NORMALIZATION_MANAGER_H_
diff --git a/chromium/components/payments/core/address_normalization_manager_unittest.cc b/chromium/components/payments/core/address_normalization_manager_unittest.cc
new file mode 100644
index 00000000000..c0a60fa2c80
--- /dev/null
+++ b/chromium/components/payments/core/address_normalization_manager_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright 2017 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.
+
+#include "components/payments/core/address_normalization_manager.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/ptr_util.h"
+#include "components/payments/core/test_address_normalizer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace payments {
+
+class AddressNormalizationManagerTest : public testing::Test {
+ protected:
+ AddressNormalizationManagerTest() {}
+
+ void Initialize(const std::string& country_code) {
+ std::unique_ptr<TestAddressNormalizer> address_normalizer =
+ base::MakeUnique<TestAddressNormalizer>();
+ address_normalizer_ = address_normalizer.get();
+ manager_ = base::MakeUnique<AddressNormalizationManager>(
+ std::move(address_normalizer), country_code);
+ }
+
+ void Finalize() {
+ manager_->FinalizeWithCompletionCallback(
+ base::BindOnce(&AddressNormalizationManagerTest::CompletionCallback,
+ base::Unretained(this)));
+ }
+
+ void CompletionCallback() { completion_callback_called_ = true; }
+
+ std::unique_ptr<AddressNormalizationManager> manager_;
+ TestAddressNormalizer* address_normalizer_ = nullptr; // Weak.
+ bool completion_callback_called_ = false;
+};
+
+TEST_F(AddressNormalizationManagerTest, SynchronousResult) {
+ Initialize("US");
+
+ autofill::AutofillProfile profile_to_normalize;
+ manager_->StartNormalizingAddress(&profile_to_normalize);
+
+ EXPECT_FALSE(completion_callback_called_);
+ Finalize();
+ EXPECT_TRUE(completion_callback_called_);
+}
+
+TEST_F(AddressNormalizationManagerTest, AsynchronousResult) {
+ Initialize("US");
+ address_normalizer_->DelayNormalization();
+
+ autofill::AutofillProfile profile_to_normalize;
+ manager_->StartNormalizingAddress(&profile_to_normalize);
+
+ EXPECT_FALSE(completion_callback_called_);
+ Finalize();
+ EXPECT_FALSE(completion_callback_called_);
+ address_normalizer_->CompleteAddressNormalization();
+ EXPECT_TRUE(completion_callback_called_);
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/core/address_normalizer.h b/chromium/components/payments/core/address_normalizer.h
index f3d2a240fb5..c46371cfcbc 100644
--- a/chromium/components/payments/core/address_normalizer.h
+++ b/chromium/components/payments/core/address_normalizer.h
@@ -5,25 +5,14 @@
#ifndef COMPONENTS_PAYMENTS_CORE_ADDRESS_NORMALIZER_H_
#define COMPONENTS_PAYMENTS_CORE_ADDRESS_NORMALIZER_H_
-#include <map>
-#include <memory>
#include <string>
-#include <vector>
-#include "base/macros.h"
#include "third_party/libaddressinput/chromium/chrome_address_validator.h"
namespace autofill {
class AutofillProfile;
}
-namespace i18n {
-namespace libadderssinput {
-class Source;
-class Storage;
-}
-}
-
namespace payments {
// A class used to normalize addresses.
@@ -49,16 +38,12 @@ class AddressNormalizer : public autofill::LoadRulesListener {
virtual ~Request() {}
};
- AddressNormalizer(std::unique_ptr<::i18n::addressinput::Source> source,
- std::unique_ptr<::i18n::addressinput::Storage> storage);
- ~AddressNormalizer() override;
-
// Start loading the validation rules for the specified |region_code|.
- virtual void LoadRulesForRegion(const std::string& region_code);
+ virtual void LoadRulesForRegion(const std::string& region_code) = 0;
// Returns whether the rules for the specified |region_code| have finished
// loading.
- bool AreRulesLoadedForRegion(const std::string& region_code);
+ virtual bool AreRulesLoadedForRegion(const std::string& region_code) = 0;
// Starts the normalization of the |profile| based on the |region_code|. The
// normalized profile will be returned to the |requester| possibly
@@ -68,25 +53,11 @@ class AddressNormalizer : public autofill::LoadRulesListener {
// happen synchronously, or not at all if the rules are not already loaded.
// Will start loading the rules for the |region_code| if they had not started
// loading.
- void StartAddressNormalization(const autofill::AutofillProfile& profile,
- const std::string& region_code,
- int timeout_seconds,
- Delegate* requester);
-
- private:
- // Called when the validation rules for the |region_code| have finished
- // loading. Implementation of the LoadRulesListener interface.
- void OnAddressValidationRulesLoaded(const std::string& region_code,
- bool success) override;
-
- // Map associating a region code to pending normalizations.
- std::map<std::string, std::vector<std::unique_ptr<Request>>>
- pending_normalization_;
-
- // The address validator used to normalize addresses.
- autofill::AddressValidator address_validator_;
-
- DISALLOW_COPY_AND_ASSIGN(AddressNormalizer);
+ virtual void StartAddressNormalization(
+ const autofill::AutofillProfile& profile,
+ const std::string& region_code,
+ int timeout_seconds,
+ Delegate* requester) = 0;
};
} // namespace payments
diff --git a/chromium/components/payments/core/address_normalizer.cc b/chromium/components/payments/core/address_normalizer_impl.cc
index d432ff11e15..12961bd99c6 100644
--- a/chromium/components/payments/core/address_normalizer.cc
+++ b/chromium/components/payments/core/address_normalizer_impl.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/payments/core/address_normalizer.h"
+#include "components/payments/core/address_normalizer_impl.h"
#include <stddef.h>
#include <utility>
@@ -123,22 +123,22 @@ class AddressNormalizationRequest : public AddressNormalizer::Request {
} // namespace
-AddressNormalizer::AddressNormalizer(std::unique_ptr<Source> source,
- std::unique_ptr<Storage> storage)
+AddressNormalizerImpl::AddressNormalizerImpl(std::unique_ptr<Source> source,
+ std::unique_ptr<Storage> storage)
: address_validator_(std::move(source), std::move(storage), this) {}
-AddressNormalizer::~AddressNormalizer() {}
+AddressNormalizerImpl::~AddressNormalizerImpl() {}
-void AddressNormalizer::LoadRulesForRegion(const std::string& region_code) {
+void AddressNormalizerImpl::LoadRulesForRegion(const std::string& region_code) {
address_validator_.LoadRules(region_code);
}
-bool AddressNormalizer::AreRulesLoadedForRegion(
+bool AddressNormalizerImpl::AreRulesLoadedForRegion(
const std::string& region_code) {
return address_validator_.AreRulesLoadedForRegion(region_code);
}
-void AddressNormalizer::StartAddressNormalization(
+void AddressNormalizerImpl::StartAddressNormalization(
const AutofillProfile& profile,
const std::string& region_code,
int timeout_seconds,
@@ -173,7 +173,7 @@ void AddressNormalizer::StartAddressNormalization(
}
}
-void AddressNormalizer::OnAddressValidationRulesLoaded(
+void AddressNormalizerImpl::OnAddressValidationRulesLoaded(
const std::string& region_code,
bool success) {
// Check if an address normalization is pending.
diff --git a/chromium/components/payments/core/address_normalizer_impl.h b/chromium/components/payments/core/address_normalizer_impl.h
new file mode 100644
index 00000000000..1444e8ad43f
--- /dev/null
+++ b/chromium/components/payments/core/address_normalizer_impl.h
@@ -0,0 +1,62 @@
+// Copyright 2017 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.
+
+#ifndef COMPONENTS_PAYMENTS_CORE_ADDRESS_NORMALIZER_IMPL_H_
+#define COMPONENTS_PAYMENTS_CORE_ADDRESS_NORMALIZER_IMPL_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/payments/core/address_normalizer.h"
+
+namespace autofill {
+class AutofillProfile;
+}
+
+namespace i18n {
+namespace addressinput {
+class Source;
+class Storage;
+}
+}
+
+namespace payments {
+
+// A class used to normalize addresses.
+class AddressNormalizerImpl : public AddressNormalizer {
+ public:
+ AddressNormalizerImpl(std::unique_ptr<::i18n::addressinput::Source> source,
+ std::unique_ptr<::i18n::addressinput::Storage> storage);
+ ~AddressNormalizerImpl() override;
+
+ // AddressNormalizer implementation.
+ void LoadRulesForRegion(const std::string& region_code) override;
+ bool AreRulesLoadedForRegion(const std::string& region_code) override;
+ void StartAddressNormalization(const autofill::AutofillProfile& profile,
+ const std::string& region_code,
+ int timeout_seconds,
+ Delegate* requester) override;
+
+ private:
+ // Called when the validation rules for the |region_code| have finished
+ // loading. Implementation of the LoadRulesListener interface.
+ void OnAddressValidationRulesLoaded(const std::string& region_code,
+ bool success) override;
+
+ // Map associating a region code to pending normalizations.
+ std::map<std::string, std::vector<std::unique_ptr<Request>>>
+ pending_normalization_;
+
+ // The address validator used to normalize addresses.
+ autofill::AddressValidator address_validator_;
+
+ DISALLOW_COPY_AND_ASSIGN(AddressNormalizerImpl);
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CORE_ADDRESS_NORMALIZER_IMPL_H_
diff --git a/chromium/components/payments/core/address_normalizer_unittest.cc b/chromium/components/payments/core/address_normalizer_impl_unittest.cc
index da9760b3ef2..495dd04030c 100644
--- a/chromium/components/payments/core/address_normalizer_unittest.cc
+++ b/chromium/components/payments/core/address_normalizer_impl_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "components/payments/core/address_normalizer.h"
+#include "components/payments/core/address_normalizer_impl.h"
#include <utility>
@@ -79,13 +79,13 @@ class ChromiumTestdataSource : public TestdataSource {
DISALLOW_COPY_AND_ASSIGN(ChromiumTestdataSource);
};
-// A test subclass of the AddressNormalizer. Used to simulate rules not being
-// loaded.
-class TestAddressNormalizer : public AddressNormalizer {
+// A test subclass of the AddressNormalizerImpl. Used to simulate rules not
+// being loaded.
+class TestAddressNormalizer : public AddressNormalizerImpl {
public:
TestAddressNormalizer(std::unique_ptr<::i18n::addressinput::Source> source,
std::unique_ptr<::i18n::addressinput::Storage> storage)
- : AddressNormalizer(std::move(source), std::move(storage)),
+ : AddressNormalizerImpl(std::move(source), std::move(storage)),
should_load_rules_(true) {}
~TestAddressNormalizer() override {}
@@ -96,7 +96,7 @@ class TestAddressNormalizer : public AddressNormalizer {
void LoadRulesForRegion(const std::string& region_code) override {
if (should_load_rules_) {
- AddressNormalizer::LoadRulesForRegion(region_code);
+ AddressNormalizerImpl::LoadRulesForRegion(region_code);
}
}
diff --git a/chromium/components/payments/core/autofill_payment_instrument.cc b/chromium/components/payments/core/autofill_payment_instrument.cc
index 398edb70f90..82ff24d2c62 100644
--- a/chromium/components/payments/core/autofill_payment_instrument.cc
+++ b/chromium/components/payments/core/autofill_payment_instrument.cc
@@ -4,12 +4,17 @@
#include "components/payments/core/autofill_payment_instrument.h"
+#include <memory>
+
#include "base/json/json_writer.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
+#include "components/autofill/core/browser/autofill_country.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/payments/core/basic_card_response.h"
#include "components/payments/core/payment_request_data_util.h"
@@ -17,20 +22,6 @@
namespace payments {
-namespace {
-
-// Returns whether |card| has a non-empty number and cardholder name. Server
-// cards will have a non-empty number.
-bool CreditCardHasNumberAndName(const autofill::CreditCard& card,
- const std::string& app_locale) {
- return !card.number().empty() &&
- !card.GetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
- app_locale)
- .empty();
-}
-
-} // namespace
-
AutofillPaymentInstrument::AutofillPaymentInstrument(
const std::string& method_name,
const autofill::CreditCard& card,
@@ -39,11 +30,7 @@ AutofillPaymentInstrument::AutofillPaymentInstrument(
PaymentRequestDelegate* payment_request_delegate)
: PaymentInstrument(
method_name,
- /* label= */ card.TypeAndLastFourDigits(),
- /* sublabel= */
- card.GetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
- app_locale),
- autofill::data_util::GetPaymentRequestData(card.type())
+ autofill::data_util::GetPaymentRequestData(card.network())
.icon_resource_id,
PaymentInstrument::Type::AUTOFILL),
credit_card_(card),
@@ -63,21 +50,70 @@ void AutofillPaymentInstrument::InvokePaymentApp(
DCHECK(!delegate_);
delegate_ = delegate;
+ // Get the billing address.
+ if (!credit_card_.billing_address_id().empty()) {
+ autofill::AutofillProfile* billing_address =
+ autofill::PersonalDataManager::GetProfileFromProfilesByGUID(
+ credit_card_.billing_address_id(), billing_profiles_);
+ if (billing_address)
+ billing_address_ = *billing_address;
+ }
+
+ is_waiting_for_billing_address_normalization_ = true;
+ is_waiting_for_card_unmask_ = true;
+
+ // Start the normalization of the billing address.
+ // Use the country code from the profile if it is set, otherwise infer it
+ // from the |app_locale_|.
+ std::string country_code = base::UTF16ToUTF8(
+ billing_address_.GetRawInfo(autofill::ADDRESS_HOME_COUNTRY));
+ if (!autofill::data_util::IsValidCountryCode(country_code)) {
+ country_code = autofill::AutofillCountry::CountryCodeForLocale(app_locale_);
+ }
+ payment_request_delegate_->GetAddressNormalizer()->StartAddressNormalization(
+ billing_address_, country_code, /*timeout_seconds=*/5, this);
+
payment_request_delegate_->DoFullCardRequest(credit_card_,
weak_ptr_factory_.GetWeakPtr());
}
bool AutofillPaymentInstrument::IsCompleteForPayment() {
- // A card is complete for payment if it's not expired, its number is not
- // empty (a server card fills this condition) and there is a cardholder name.
- // TODO(crbug.com/709776): Check for billing address association.
- return !credit_card_.IsExpired(autofill::AutofillClock::Now()) &&
- CreditCardHasNumberAndName(credit_card_, app_locale_);
+ // COMPLETE or EXPIRED cards are considered valid for payment. The user will
+ // be prompted to enter the new expiration at the CVC step.
+ return autofill::GetCompletionStatusForCard(credit_card_, app_locale_,
+ billing_profiles_) <=
+ autofill::CREDIT_CARD_EXPIRED;
+}
+
+base::string16 AutofillPaymentInstrument::GetMissingInfoLabel() {
+ return autofill::GetCompletionMessageForCard(
+ autofill::GetCompletionStatusForCard(credit_card_, app_locale_,
+ billing_profiles_));
}
bool AutofillPaymentInstrument::IsValidForCanMakePayment() {
- // An expired card is still valid for the purposes of canMakePayment.
- return CreditCardHasNumberAndName(credit_card_, app_locale_);
+ autofill::CreditCardCompletionStatus status =
+ autofill::GetCompletionStatusForCard(credit_card_, app_locale_,
+ billing_profiles_);
+ // Card has to have a cardholder name and number for the purposes of
+ // CanMakePayment. An expired card is still valid at this stage.
+ return !(status & autofill::CREDIT_CARD_NO_CARDHOLDER ||
+ status & autofill::CREDIT_CARD_NO_NUMBER);
+}
+
+void AutofillPaymentInstrument::RecordUse() {
+ // Record the use of the credit card.
+ payment_request_delegate_->GetPersonalDataManager()->RecordUseOf(
+ credit_card_);
+}
+
+base::string16 AutofillPaymentInstrument::GetLabel() const {
+ return credit_card_.NetworkAndLastFourDigits();
+}
+
+base::string16 AutofillPaymentInstrument::GetSublabel() const {
+ return credit_card_.GetInfo(
+ autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL), app_locale_);
}
void AutofillPaymentInstrument::OnFullCardRequestSucceeded(
@@ -85,19 +121,53 @@ void AutofillPaymentInstrument::OnFullCardRequestSucceeded(
const base::string16& cvc) {
DCHECK(delegate_);
credit_card_ = card;
+ cvc_ = cvc;
+ is_waiting_for_card_unmask_ = false;
+
+ if (!is_waiting_for_billing_address_normalization_)
+ GenerateBasicCardResponse();
+}
+
+void AutofillPaymentInstrument::OnFullCardRequestFailed() {
+ // The user may have cancelled the unmask or something has gone wrong (e.g.,
+ // the network request failed). In all cases, reset the |delegate_| so another
+ // request can start.
+ delegate_ = nullptr;
+}
+
+void AutofillPaymentInstrument::OnAddressNormalized(
+ const autofill::AutofillProfile& normalized_profile) {
+ DCHECK(is_waiting_for_billing_address_normalization_);
+
+ billing_address_ = normalized_profile;
+ is_waiting_for_billing_address_normalization_ = false;
+
+ if (!is_waiting_for_card_unmask_)
+ GenerateBasicCardResponse();
+}
+
+void AutofillPaymentInstrument::OnCouldNotNormalize(
+ const autofill::AutofillProfile& profile) {
+ // Since the phone number is formatted in either case, this profile should be
+ // used.
+ OnAddressNormalized(profile);
+}
+
+void AutofillPaymentInstrument::GenerateBasicCardResponse() {
+ DCHECK(!is_waiting_for_billing_address_normalization_);
+ DCHECK(!is_waiting_for_card_unmask_);
+ DCHECK(delegate_);
+
std::unique_ptr<base::DictionaryValue> response_value =
payments::data_util::GetBasicCardResponseFromAutofillCreditCard(
- credit_card_, cvc, billing_profiles_, app_locale_)
+ credit_card_, cvc_, billing_address_, app_locale_)
.ToDictionaryValue();
std::string stringified_details;
base::JSONWriter::Write(*response_value, &stringified_details);
delegate_->OnInstrumentDetailsReady(method_name(), stringified_details);
- delegate_ = nullptr;
-}
-void AutofillPaymentInstrument::OnFullCardRequestFailed() {
- // TODO(anthonyvd): Do something with the error.
delegate_ = nullptr;
+ cvc_ = base::UTF8ToUTF16("");
}
} // namespace payments
diff --git a/chromium/components/payments/core/autofill_payment_instrument.h b/chromium/components/payments/core/autofill_payment_instrument.h
index 84b343ab4c6..6df075f621a 100644
--- a/chromium/components/payments/core/autofill_payment_instrument.h
+++ b/chromium/components/payments/core/autofill_payment_instrument.h
@@ -10,14 +10,13 @@
#include <vector>
#include "base/macros.h"
+#include "base/strings/string16.h"
+#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/payments/full_card_request.h"
+#include "components/payments/core/address_normalizer.h"
#include "components/payments/core/payment_instrument.h"
-namespace autofill {
-class AutofillProfile;
-}
-
namespace payments {
class PaymentRequestDelegate;
@@ -26,7 +25,8 @@ class PaymentRequestDelegate;
// Request.
class AutofillPaymentInstrument
: public PaymentInstrument,
- public autofill::payments::FullCardRequest::ResultDelegate {
+ public autofill::payments::FullCardRequest::ResultDelegate,
+ public AddressNormalizer::Delegate {
public:
// |billing_profiles| is owned by the caller and should outlive this object.
// |payment_request_delegate| must outlive this object.
@@ -41,16 +41,28 @@ class AutofillPaymentInstrument
// PaymentInstrument:
void InvokePaymentApp(PaymentInstrument::Delegate* delegate) override;
bool IsCompleteForPayment() override;
+ base::string16 GetMissingInfoLabel() override;
bool IsValidForCanMakePayment() override;
+ void RecordUse() override;
+ base::string16 GetLabel() const override;
+ base::string16 GetSublabel() const override;
// autofill::payments::FullCardRequest::ResultDelegate:
void OnFullCardRequestSucceeded(const autofill::CreditCard& card,
const base::string16& cvc) override;
void OnFullCardRequestFailed() override;
+ // AddressNormalizer::Delegate:
+ void OnAddressNormalized(
+ const autofill::AutofillProfile& normalized_profile) override;
+ void OnCouldNotNormalize(const autofill::AutofillProfile& profile) override;
+
autofill::CreditCard* credit_card() { return &credit_card_; }
private:
+ // Generates the basic card response and sends it to the delegate.
+ void GenerateBasicCardResponse();
+
// A copy of the card is owned by this object.
autofill::CreditCard credit_card_;
// Not owned by this object, should outlive this.
@@ -60,6 +72,12 @@ class AutofillPaymentInstrument
PaymentInstrument::Delegate* delegate_;
PaymentRequestDelegate* payment_request_delegate_;
+ autofill::AutofillProfile billing_address_;
+
+ base::string16 cvc_;
+
+ bool is_waiting_for_card_unmask_;
+ bool is_waiting_for_billing_address_normalization_;
base::WeakPtrFactory<AutofillPaymentInstrument> weak_ptr_factory_;
diff --git a/chromium/components/payments/core/autofill_payment_instrument_unittest.cc b/chromium/components/payments/core/autofill_payment_instrument_unittest.cc
index 7b9db0be6a9..7f84726ad79 100644
--- a/chromium/components/payments/core/autofill_payment_instrument_unittest.cc
+++ b/chromium/components/payments/core/autofill_payment_instrument_unittest.cc
@@ -5,14 +5,141 @@
#include "components/payments/core/autofill_payment_instrument.h"
#include "base/macros.h"
+#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/credit_card.h"
+#include "components/payments/core/address_normalizer.h"
+#include "components/payments/core/test_payment_request_delegate.h"
+#include "components/strings/grit/components_strings.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
namespace payments {
+namespace {
+
+class FakePaymentInstrumentDelegate : public PaymentInstrument::Delegate {
+ public:
+ FakePaymentInstrumentDelegate() {}
+
+ void OnInstrumentDetailsReady(
+ const std::string& method_name,
+ const std::string& stringified_details) override {
+ on_instrument_details_ready_called_ = true;
+ }
+
+ void OnInstrumentDetailsError() override {
+ on_instrument_details_error_called_ = true;
+ }
+
+ bool WasOnInstrumentDetailsReadyCalled() {
+ return on_instrument_details_ready_called_;
+ }
+
+ bool WasOnInstrumentDetailsErrorCalled() {
+ return on_instrument_details_error_called_;
+ }
+
+ private:
+ bool on_instrument_details_ready_called_ = false;
+ bool on_instrument_details_error_called_ = false;
+};
+
+class FakeAddressNormalizer : public AddressNormalizer {
+ public:
+ FakeAddressNormalizer() {}
+
+ void LoadRulesForRegion(const std::string& region_code) override {}
+
+ bool AreRulesLoadedForRegion(const std::string& region_code) override {
+ return true;
+ }
+
+ void StartAddressNormalization(
+ const autofill::AutofillProfile& profile,
+ const std::string& region_code,
+ int timeout_seconds,
+ AddressNormalizer::Delegate* requester) override {
+ profile_ = profile;
+ requester_ = requester;
+ }
+
+ void OnAddressValidationRulesLoaded(const std::string& region_code,
+ bool success) override {}
+
+ void CompleteAddressNormalization() {
+ requester_->OnAddressNormalized(profile_);
+ }
+
+ private:
+ autofill::AutofillProfile profile_;
+ AddressNormalizer::Delegate* requester_;
+};
+
+class FakePaymentRequestDelegate : public PaymentRequestDelegate {
+ public:
+ FakePaymentRequestDelegate()
+ : locale_("en-US"), last_committed_url_("https://shop.com") {}
+ void ShowDialog(PaymentRequest* request) override {}
+
+ void CloseDialog() override {}
+
+ void ShowErrorMessage() override {}
+
+ autofill::PersonalDataManager* GetPersonalDataManager() override {
+ return nullptr;
+ }
+
+ const std::string& GetApplicationLocale() const override { return locale_; }
+
+ bool IsIncognito() const override { return false; }
+
+ bool IsSslCertificateValid() override { return true; }
+
+ const GURL& GetLastCommittedURL() const override {
+ return last_committed_url_;
+ }
+
+ void DoFullCardRequest(
+ const autofill::CreditCard& credit_card,
+ base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
+ result_delegate) override {
+ full_card_request_card_ = credit_card;
+ full_card_result_delegate_ = result_delegate;
+ }
+
+ AddressNormalizer* GetAddressNormalizer() override {
+ return &address_normalizer_;
+ }
+
+ FakeAddressNormalizer* GetTestAddressNormalizer() {
+ return &address_normalizer_;
+ }
+
+ void CompleteFullCardRequest() {
+ full_card_result_delegate_->OnFullCardRequestSucceeded(
+ full_card_request_card_, base::ASCIIToUTF16("123"));
+ }
+
+ autofill::RegionDataLoader* GetRegionDataLoader() override { return nullptr; }
+
+ ukm::UkmRecorder* GetUkmRecorder() override { return nullptr; }
+
+ private:
+ std::string locale_;
+ const GURL last_committed_url_;
+ FakeAddressNormalizer address_normalizer_;
+
+ autofill::CreditCard full_card_request_card_;
+ base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
+ full_card_result_delegate_;
+ DISALLOW_COPY_AND_ASSIGN(FakePaymentRequestDelegate);
+};
+
+} // namespace
+
class AutofillPaymentInstrumentTest : public testing::Test {
protected:
AutofillPaymentInstrumentTest()
@@ -40,15 +167,17 @@ TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment) {
AutofillPaymentInstrument instrument("visa", local_credit_card(),
billing_profiles(), "en-US", nullptr);
EXPECT_TRUE(instrument.IsCompleteForPayment());
+ EXPECT_TRUE(instrument.GetMissingInfoLabel().empty());
}
-// An expired local card is not a valid instrument for payment.
+// An expired local card is still a valid instrument for payment.
TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_Expired) {
autofill::CreditCard& card = local_credit_card();
card.SetExpirationYear(2016); // Expired.
AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
"en-US", nullptr);
- EXPECT_FALSE(instrument.IsCompleteForPayment());
+ EXPECT_TRUE(instrument.IsCompleteForPayment());
+ EXPECT_EQ(base::string16(), instrument.GetMissingInfoLabel());
}
// A local card with no name is not a valid instrument for payment.
@@ -56,35 +185,92 @@ TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_NoName) {
autofill::CreditCard& card = local_credit_card();
card.SetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
base::ASCIIToUTF16(""), "en-US");
+ base::string16 missing_info;
AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
"en-US", nullptr);
EXPECT_FALSE(instrument.IsCompleteForPayment());
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_NAME_ON_CARD_REQUIRED),
+ instrument.GetMissingInfoLabel());
}
// A local card with no name is not a valid instrument for payment.
TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_NoNumber) {
autofill::CreditCard& card = local_credit_card();
card.SetNumber(base::ASCIIToUTF16(""));
+ base::string16 missing_info;
+ AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
+ "en-US", nullptr);
+ EXPECT_FALSE(instrument.IsCompleteForPayment());
+ EXPECT_EQ(l10n_util::GetStringUTF16(
+ IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE),
+ instrument.GetMissingInfoLabel());
+}
+
+// A local card with no billing address id is not a valid instrument for
+// payment.
+TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_NoBillinbAddressId) {
+ autofill::CreditCard& card = local_credit_card();
+ card.set_billing_address_id("");
+ base::string16 missing_info;
+ AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
+ "en-US", nullptr);
+ EXPECT_FALSE(instrument.IsCompleteForPayment());
+ EXPECT_EQ(
+ l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
+ instrument.GetMissingInfoLabel());
+}
+
+// A local card with an invalid billing address id is not a valid instrument for
+// payment.
+TEST_F(AutofillPaymentInstrumentTest,
+ IsCompleteForPayment_InvalidBillinbAddressId) {
+ autofill::CreditCard& card = local_credit_card();
+ card.set_billing_address_id("InvalidBillingAddressId");
+ base::string16 missing_info;
AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
"en-US", nullptr);
EXPECT_FALSE(instrument.IsCompleteForPayment());
+ EXPECT_EQ(
+ l10n_util::GetStringUTF16(IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED),
+ instrument.GetMissingInfoLabel());
+}
+
+// A local card with no name and no number is not a valid instrument for
+// payment.
+TEST_F(AutofillPaymentInstrumentTest,
+ IsCompleteForPayment_MultipleThingsMissing) {
+ autofill::CreditCard& card = local_credit_card();
+ card.SetNumber(base::ASCIIToUTF16(""));
+ card.SetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL),
+ base::ASCIIToUTF16(""), "en-US");
+ AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
+ "en-US", nullptr);
+ EXPECT_FALSE(instrument.IsCompleteForPayment());
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_MORE_INFORMATION_REQUIRED),
+ instrument.GetMissingInfoLabel());
}
// A Masked (server) card is a valid instrument for payment.
TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_MaskedCard) {
autofill::CreditCard card = autofill::test::GetMaskedServerCard();
+ ASSERT_GT(billing_profiles().size(), 0UL);
+ card.set_billing_address_id(billing_profiles()[0]->guid());
AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
"en-US", nullptr);
EXPECT_TRUE(instrument.IsCompleteForPayment());
+ EXPECT_TRUE(instrument.GetMissingInfoLabel().empty());
}
-// An expired masked (server) card is not a valid instrument for payment.
+// An expired masked (server) card is still a valid instrument for payment.
TEST_F(AutofillPaymentInstrumentTest, IsCompleteForPayment_ExpiredMaskedCard) {
autofill::CreditCard card = autofill::test::GetMaskedServerCard();
+ ASSERT_GT(billing_profiles().size(), 0UL);
+ card.set_billing_address_id(billing_profiles()[0]->guid());
card.SetExpirationYear(2016); // Expired.
AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
"en-US", nullptr);
- EXPECT_FALSE(instrument.IsCompleteForPayment());
+ EXPECT_TRUE(instrument.IsCompleteForPayment());
+ EXPECT_EQ(base::string16(), instrument.GetMissingInfoLabel());
}
// An expired card is a valid instrument for canMakePayment.
@@ -124,4 +310,60 @@ TEST_F(AutofillPaymentInstrumentTest, IsValidForCanMakePayment_NoNumber) {
EXPECT_FALSE(instrument.IsValidForCanMakePayment());
}
+// Tests that the autofill instrument only calls OnInstrumentDetailsReady when
+// the billing address has been normalized and the card has been unmasked.
+TEST_F(AutofillPaymentInstrumentTest,
+ InvokePaymentApp_NormalizationBeforeUnmask) {
+ TestPaymentRequestDelegate delegate(/*personal_data_manager=*/nullptr);
+ delegate.DelayFullCardRequestCompletion();
+ delegate.test_address_normalizer()->DelayNormalization();
+
+ autofill::CreditCard& card = local_credit_card();
+ card.SetNumber(base::ASCIIToUTF16(""));
+ AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
+ "en-US", &delegate);
+
+ FakePaymentInstrumentDelegate instrument_delegate;
+
+ instrument.InvokePaymentApp(&instrument_delegate);
+ EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+ EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+
+ delegate.test_address_normalizer()->CompleteAddressNormalization();
+ EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+ EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+
+ delegate.CompleteFullCardRequest();
+ EXPECT_TRUE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+ EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+}
+
+// Tests that the autofill instrument only calls OnInstrumentDetailsReady when
+// the billing address has been normalized and the card has been unmasked.
+TEST_F(AutofillPaymentInstrumentTest,
+ InvokePaymentApp_UnmaskBeforeNormalization) {
+ TestPaymentRequestDelegate delegate(/*personal_data_manager=*/nullptr);
+ delegate.DelayFullCardRequestCompletion();
+ delegate.test_address_normalizer()->DelayNormalization();
+
+ autofill::CreditCard& card = local_credit_card();
+ card.SetNumber(base::ASCIIToUTF16(""));
+ AutofillPaymentInstrument instrument("visa", card, billing_profiles(),
+ "en-US", &delegate);
+
+ FakePaymentInstrumentDelegate instrument_delegate;
+
+ instrument.InvokePaymentApp(&instrument_delegate);
+ EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+ EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+
+ delegate.CompleteFullCardRequest();
+ EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+ EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+
+ delegate.test_address_normalizer()->CompleteAddressNormalization();
+ EXPECT_TRUE(instrument_delegate.WasOnInstrumentDetailsReadyCalled());
+ EXPECT_FALSE(instrument_delegate.WasOnInstrumentDetailsErrorCalled());
+}
+
} // namespace payments
diff --git a/chromium/components/payments/core/can_make_payment_query.cc b/chromium/components/payments/core/can_make_payment_query.cc
new file mode 100644
index 00000000000..c4264be89be
--- /dev/null
+++ b/chromium/components/payments/core/can_make_payment_query.cc
@@ -0,0 +1,44 @@
+// Copyright 2017 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.
+
+#include "components/payments/core/can_make_payment_query.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/memory/ptr_util.h"
+#include "base/time/time.h"
+
+namespace payments {
+
+CanMakePaymentQuery::CanMakePaymentQuery() {}
+
+CanMakePaymentQuery::~CanMakePaymentQuery() {}
+
+bool CanMakePaymentQuery::CanQuery(
+ const GURL& frame_origin,
+ const std::map<std::string, std::set<std::string>>& query) {
+ const auto& it = queries_.find(frame_origin);
+ if (it == queries_.end()) {
+ std::unique_ptr<base::OneShotTimer> timer =
+ base::MakeUnique<base::OneShotTimer>();
+ timer->Start(FROM_HERE, base::TimeDelta::FromMinutes(30),
+ base::Bind(&CanMakePaymentQuery::ExpireQuotaForFrameOrigin,
+ base::Unretained(this), frame_origin));
+ timers_.insert(std::make_pair(frame_origin, std::move(timer)));
+ queries_.insert(std::make_pair(frame_origin, query));
+ return true;
+ }
+
+ return it->second == query;
+}
+
+void CanMakePaymentQuery::ExpireQuotaForFrameOrigin(const GURL& frame_origin) {
+ timers_.erase(frame_origin);
+ queries_.erase(frame_origin);
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/core/can_make_payment_query.h b/chromium/components/payments/core/can_make_payment_query.h
new file mode 100644
index 00000000000..69da5761453
--- /dev/null
+++ b/chromium/components/payments/core/can_make_payment_query.h
@@ -0,0 +1,50 @@
+// Copyright 2017 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.
+
+#ifndef COMPONENTS_PAYMENTS_CORE_CAN_MAKE_PAYMENT_QUERY_H_
+#define COMPONENTS_PAYMENTS_CORE_CAN_MAKE_PAYMENT_QUERY_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/macros.h"
+#include "base/timer/timer.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "url/gurl.h"
+
+namespace payments {
+
+// Keeps track of canMakePayment() queries per browser context.
+class CanMakePaymentQuery : public KeyedService {
+ public:
+ CanMakePaymentQuery();
+ ~CanMakePaymentQuery() override;
+
+ // Returns whether |frame_origin| can call canMakePayment() with |query|,
+ // which is a mapping of payment method names to the corresponding
+ // JSON-stringified payment method data. Remembers the frame-to-query mapping
+ // for 30 minutes to enforce the quota.
+ bool CanQuery(const GURL& frame_origin,
+ const std::map<std::string, std::set<std::string>>& query);
+
+ private:
+ void ExpireQuotaForFrameOrigin(const GURL& frame_origin);
+
+ // A mapping of frame origin to the timer that, when fired, allows the frame
+ // to invoke canMakePayment() with a different query.
+ std::map<GURL, std::unique_ptr<base::OneShotTimer>> timers_;
+
+ // A mapping of frame origin to its last query. Each query is a mapping of
+ // payment method names to the corresponding JSON-stringified payment method
+ // data.
+ std::map<GURL, std::map<std::string, std::set<std::string>>> queries_;
+
+ DISALLOW_COPY_AND_ASSIGN(CanMakePaymentQuery);
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CORE_CAN_MAKE_PAYMENT_QUERY_H_
diff --git a/chromium/components/payments/core/currency_formatter.cc b/chromium/components/payments/core/currency_formatter.cc
index 4eb4f74ea06..dae2383c932 100644
--- a/chromium/components/payments/core/currency_formatter.cc
+++ b/chromium/components/payments/core/currency_formatter.cc
@@ -104,19 +104,23 @@ base::string16 CurrencyFormatter::Format(const std::string& amount) {
if (output.isEmpty())
return base::UTF8ToUTF16(amount);
- // Explicitly removes the currency code (truncated to its 3-letter and
- // 2-letter versions) from the output, because callers are expected to
+ // Explicitly removes the currency code (truncated to its 3-letter, 2-letter
+ // and 1-letter versions) from the output, because callers are expected to
// display the currency code alongside this result.
//
// 3+ letters: If currency code is "ABCDEF" or "BTX", this code will
// transform "ABC55.00"/"BTX55.00" to "55.00".
// 2 letters: If currency code is "CAD", this code will transform "CA$55.00"
// to "$55.00" (en_US) or "55,00 $ CA" to "55,00 $" (fr_FR).
+ // 1 letter: If currency code is "AUD", this code will transform "A$55.00"
+ // to "$55.00" (en_US).
icu::UnicodeString tmp_currency_code(*currency_code_);
tmp_currency_code.truncate(3);
output.findAndReplace(tmp_currency_code, "");
tmp_currency_code.truncate(2);
output.findAndReplace(tmp_currency_code, "");
+ tmp_currency_code.truncate(1);
+ output.findAndReplace(tmp_currency_code, "");
// Trims any unicode whitespace (including non-breaking space).
if (u_isUWhiteSpace(output[0])) {
output.remove(0, 1);
diff --git a/chromium/components/payments/core/currency_formatter_unittest.cc b/chromium/components/payments/core/currency_formatter_unittest.cc
index e55a2fe29a5..41793ef4c0c 100644
--- a/chromium/components/payments/core/currency_formatter_unittest.cc
+++ b/chromium/components/payments/core/currency_formatter_unittest.cc
@@ -81,6 +81,11 @@ INSTANTIATE_TEST_CASE_P(
TestCase("55.00", "CAD", "fr_CA", "55,00 $", "CAD"),
TestCase("55.00", "CAD", "fr_FR", "55,00 $", "CAD"),
+ TestCase("55.00", "AUD", "en_US", "$55.00", "AUD"),
+ TestCase("55.00", "AUD", "en_CA", "$55.00", "AUD"),
+ TestCase("55.00", "AUD", "fr_CA", "55,00 $", "AUD"),
+ TestCase("55.00", "AUD", "fr_FR", "55,00 $", "AUD"),
+
TestCase("55.00", "BRL", "en_US", "R$55.00", "BRL"),
TestCase("55.00", "BRL", "fr_CA", "55,00 R$", "BRL"),
TestCase("55.00", "BRL", "pt_BR", "R$55,00", "BRL"),
@@ -119,8 +124,12 @@ INSTANTIATE_TEST_CASE_P(
"USD",
"fr_FR",
"123 456 789 012 345 678 901 234 567 890,123456789 $",
- "USD"),
+ "USD")));
+INSTANTIATE_TEST_CASE_P(
+ CurrencySystems,
+ PaymentsCurrencyFormatterTest,
+ testing::Values(
// When the currency system is not ISO4217, only the amount is formatted
// using the locale (there is no other indication of currency).
TestCase("55.00",
diff --git a/chromium/components/payments/core/journey_logger.cc b/chromium/components/payments/core/journey_logger.cc
index dc25cf6f1bf..7eee988f1be 100644
--- a/chromium/components/payments/core/journey_logger.cc
+++ b/chromium/components/payments/core/journey_logger.cc
@@ -8,9 +8,8 @@
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
-#include "components/autofill/core/browser/autofill_experiments.h"
-#include "components/ukm/ukm_entry_builder.h"
-#include "components/ukm/ukm_service.h"
+#include "components/ukm/public/ukm_entry_builder.h"
+#include "components/ukm/public/ukm_recorder.h"
namespace payments {
@@ -66,16 +65,16 @@ std::string GetHistogramNameSuffix(
JourneyLogger::JourneyLogger(bool is_incognito,
const GURL& url,
- ukm::UkmService* ukm_service)
- : was_can_make_payments_used_(false),
- could_make_payment_(false),
- was_show_called_(false),
- is_incognito_(is_incognito),
+ ukm::UkmRecorder* ukm_recorder)
+ : is_incognito_(is_incognito),
events_(EVENT_INITIATED),
url_(url),
- ukm_service_(ukm_service) {}
+ ukm_recorder_(ukm_recorder) {}
-JourneyLogger::~JourneyLogger() {}
+JourneyLogger::~JourneyLogger() {
+ if (was_show_called_)
+ DCHECK(has_recorded_);
+}
void JourneyLogger::IncrementSelectionAdds(Section section) {
DCHECK_LT(section, SECTION_MAX);
@@ -107,12 +106,68 @@ void JourneyLogger::SetShowCalled() {
was_show_called_ = true;
}
+void JourneyLogger::SetCompleted() {
+ UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Completed", true);
+
+ RecordJourneyStatsHistograms(COMPLETION_STATUS_COMPLETED);
+}
+
+void JourneyLogger::SetAborted(AbortReason reason) {
+ base::UmaHistogramEnumeration("PaymentRequest.CheckoutFunnel.Aborted", reason,
+ ABORT_REASON_MAX);
+
+ if (reason == ABORT_REASON_ABORTED_BY_USER ||
+ reason == ABORT_REASON_USER_NAVIGATION)
+ RecordJourneyStatsHistograms(COMPLETION_STATUS_USER_ABORTED);
+ else
+ RecordJourneyStatsHistograms(COMPLETION_STATUS_OTHER_ABORTED);
+}
+
+#ifdef OS_ANDROID
+void JourneyLogger::SetNotShown(NotShownReason reason) {
+ base::UmaHistogramEnumeration("PaymentRequest.CheckoutFunnel.NoShow", reason,
+ NOT_SHOWN_REASON_MAX);
+
+ // Record that that Payment Request was initiated here, because nothing else
+ // will be recorded for a Payment Request that was not shown to the user.
+ UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Initiated", true);
+}
+#endif
+
void JourneyLogger::SetEventOccurred(Event event) {
events_ |= event;
}
+void JourneyLogger::SetSelectedPaymentMethod(
+ SelectedPaymentMethod payment_method) {
+ payment_method_ = payment_method;
+}
+
+void JourneyLogger::SetRequestedInformation(bool requested_shipping,
+ bool requested_email,
+ bool requested_phone,
+ bool requested_name) {
+ // This method should only be called once per Payment Request.
+ DCHECK(requested_information_ == REQUESTED_INFORMATION_MAX);
+
+ requested_information_ =
+ (requested_shipping ? REQUESTED_INFORMATION_SHIPPING : 0) |
+ (requested_email ? REQUESTED_INFORMATION_EMAIL : 0) |
+ (requested_phone ? REQUESTED_INFORMATION_PHONE : 0) |
+ (requested_name ? REQUESTED_INFORMATION_NAME : 0);
+}
+
void JourneyLogger::RecordJourneyStatsHistograms(
CompletionStatus completion_status) {
+ DCHECK(!has_recorded_);
+ has_recorded_ = true;
+
+ RecordCheckoutFlowMetrics();
+
+ RecordPaymentMethodMetric();
+
+ RecordRequestedInformationMetrics();
+
RecordSectionSpecificStats(completion_status);
// Record the CanMakePayment metrics based on whether the transaction was
@@ -122,6 +177,33 @@ void JourneyLogger::RecordJourneyStatsHistograms(
RecordUrlKeyedMetrics(completion_status);
}
+void JourneyLogger::RecordCheckoutFlowMetrics() {
+ UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Initiated", true);
+
+ if (events_ & EVENT_SHOWN)
+ UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.Shown", true);
+
+ if (events_ & EVENT_PAY_CLICKED)
+ UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.PayClicked", true);
+
+ if (events_ & EVENT_RECEIVED_INSTRUMENT_DETAILS)
+ UMA_HISTOGRAM_BOOLEAN(
+ "PaymentRequest.CheckoutFunnel.ReceivedInstrumentDetails", true);
+
+ if (events_ & EVENT_SKIPPED_SHOW)
+ UMA_HISTOGRAM_BOOLEAN("PaymentRequest.CheckoutFunnel.SkippedShow", true);
+}
+
+void JourneyLogger::RecordPaymentMethodMetric() {
+ base::UmaHistogramEnumeration("PaymentRequest.SelectedPaymentMethod",
+ payment_method_, SELECTED_PAYMENT_METHOD_MAX);
+}
+
+void JourneyLogger::RecordRequestedInformationMetrics() {
+ UMA_HISTOGRAM_ENUMERATION("PaymentRequest.RequestedInformation",
+ requested_information_, REQUESTED_INFORMATION_MAX);
+}
+
void JourneyLogger::RecordSectionSpecificStats(
CompletionStatus completion_status) {
// Record whether the user had suggestions for each requested information.
@@ -194,12 +276,12 @@ void JourneyLogger::RecordCanMakePaymentEffectOnShow() {
int effect_on_show = 0;
if (was_show_called_)
- effect_on_show |= CMP_SHOW_DID_SHOW;
+ effect_on_show |= CMP_EFFECT_ON_SHOW_DID_SHOW;
if (could_make_payment_)
- effect_on_show |= CMP_SHOW_COULD_MAKE_PAYMENT;
+ effect_on_show |= CMP_EFFECT_ON_SHOW_COULD_MAKE_PAYMENT;
UMA_HISTOGRAM_ENUMERATION("PaymentRequest.CanMakePayment.Used.EffectOnShow",
- effect_on_show, CMP_SHOW_MAX);
+ effect_on_show, CMP_EFFECT_ON_SHOW_MAX);
}
void JourneyLogger::RecordCanMakePaymentEffectOnCompletion(
@@ -221,14 +303,15 @@ void JourneyLogger::RecordCanMakePaymentEffectOnCompletion(
}
void JourneyLogger::RecordUrlKeyedMetrics(CompletionStatus completion_status) {
- if (!autofill::IsUkmLoggingEnabled() || !ukm_service_ || !url_.is_valid())
+ if (!ukm_recorder_ || !url_.is_valid())
return;
// Record the Checkout Funnel UKM.
- int32_t source_id = ukm_service_->GetNewSourceID();
- ukm_service_->UpdateSourceURL(source_id, url_);
- std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder(
- source_id, internal::kUKMCheckoutEventsEntryName);
+ ukm::SourceId source_id = ukm_recorder_->GetNewSourceID();
+ ukm_recorder_->UpdateSourceURL(source_id, url_);
+ std::unique_ptr<ukm::UkmEntryBuilder> builder =
+ ukm_recorder_->GetEntryBuilder(source_id,
+ internal::kUKMCheckoutEventsEntryName);
builder->AddMetric(internal::kUKMCompletionStatusMetricName,
completion_status);
builder->AddMetric(internal::kUKMEventsMetricName, events_);
diff --git a/chromium/components/payments/core/journey_logger.h b/chromium/components/payments/core/journey_logger.h
index c3330eb8540..b37f7ae48dd 100644
--- a/chromium/components/payments/core/journey_logger.h
+++ b/chromium/components/payments/core/journey_logger.h
@@ -11,7 +11,7 @@
#include "url/gurl.h"
namespace ukm {
-class UkmService;
+class UkmRecorder;
}
namespace payments {
@@ -30,11 +30,12 @@ extern const char kUKMEventsMetricName[];
// of the Payment Request.
class JourneyLogger {
public:
- // Note: These constants should always be in sync with their counterpart in
- // components/payments/content/android/java/src/org/chromium/components/
- // payments/JourneyLogger.java.
+ // Note: Java counterparts will be generated for these enums.
+
// The different sections of a Payment Request. Used to record journey
// stats.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.payments
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: Section
enum Section {
SECTION_CONTACT_INFO = 0,
SECTION_CREDIT_CARDS = 1,
@@ -43,14 +44,51 @@ class JourneyLogger {
};
// For the CanMakePayment histograms.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.payments
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: CanMakePaymentUsage
enum CanMakePaymentUsage {
CAN_MAKE_PAYMENT_USED = 0,
CAN_MAKE_PAYMENT_NOT_USED = 1,
CAN_MAKE_PAYMENT_USE_MAX,
};
+ // The information requested by the merchant.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.payments
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: RequestedInformation
+ enum RequestedInformation {
+ REQUESTED_INFORMATION_NONE = 0,
+ REQUESTED_INFORMATION_EMAIL = 1 << 0,
+ REQUESTED_INFORMATION_PHONE = 1 << 1,
+ REQUESTED_INFORMATION_SHIPPING = 1 << 2,
+ REQUESTED_INFORMATION_NAME = 1 << 3,
+ REQUESTED_INFORMATION_MAX = 16,
+ };
+
+ // The payment method that was used by the user to complete the transaction.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.payments
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: SelectedPaymentMethod
+ enum SelectedPaymentMethod {
+ SELECTED_PAYMENT_METHOD_CREDIT_CARD = 0,
+ SELECTED_PAYMENT_METHOD_ANDROID_PAY = 1,
+ SELECTED_PAYMENT_METHOD_OTHER_PAYMENT_APP = 2,
+ SELECTED_PAYMENT_METHOD_MAX = 3,
+ };
+
+ // Used to mesure the impact of the CanMakePayment return value on whether the
+ // Payment Request is shown to the user.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.payments
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: CanMakePaymentEffectOnShow
+ enum CmpEffectOnShow {
+ CMP_EFFECT_ON_SHOW_COULD_NOT_MAKE_PAYMENT_AND_DID_NOT_SHOW = 0,
+ CMP_EFFECT_ON_SHOW_DID_SHOW = 1 << 0,
+ CMP_EFFECT_ON_SHOW_COULD_MAKE_PAYMENT = 1 << 1,
+ CMP_EFFECT_ON_SHOW_MAX = 4,
+ };
+
// Used to log different parameters' effect on whether the transaction was
// completed.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.payments
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: CompletionStatus
enum CompletionStatus {
COMPLETION_STATUS_COMPLETED = 0,
COMPLETION_STATUS_USER_ABORTED = 1,
@@ -60,25 +98,51 @@ class JourneyLogger {
// Used to record the different events that happened during the Payment
// Request.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.payments
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: Event
enum Event {
EVENT_INITIATED = 0,
EVENT_SHOWN = 1 << 0,
EVENT_PAY_CLICKED = 1 << 1,
EVENT_RECEIVED_INSTRUMENT_DETAILS = 1 << 2,
EVENT_SKIPPED_SHOW = 1 << 3,
- EVENT_MAX = 16,
+ EVENT_ENUM_MAX = 16,
};
- // Used to mesure the impact of the CanMakePayment return value on whether the
- // Payment Request is shown to the user.
- static const int CMP_SHOW_COULD_NOT_MAKE_PAYMENT_AND_DID_NOT_SHOW = 0;
- static const int CMP_SHOW_DID_SHOW = 1 << 0;
- static const int CMP_SHOW_COULD_MAKE_PAYMENT = 1 << 1;
- static const int CMP_SHOW_MAX = 4;
+ // The reason why the Payment Request was aborted.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.payments
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: AbortReason
+ enum AbortReason {
+ ABORT_REASON_ABORTED_BY_USER = 0,
+ ABORT_REASON_ABORTED_BY_MERCHANT = 1,
+ ABORT_REASON_INVALID_DATA_FROM_RENDERER = 2,
+ ABORT_REASON_MOJO_CONNECTION_ERROR = 3,
+ ABORT_REASON_MOJO_RENDERER_CLOSING = 4,
+ ABORT_REASON_INSTRUMENT_DETAILS_ERROR = 5,
+ ABORT_REASON_NO_MATCHING_PAYMENT_METHOD = 6, // Deprecated.
+ ABORT_REASON_NO_SUPPORTED_PAYMENT_METHOD = 7, // Deprecated.
+ ABORT_REASON_OTHER = 8,
+ ABORT_REASON_USER_NAVIGATION = 9,
+ ABORT_REASON_MERCHANT_NAVIGATION = 10,
+ ABORT_REASON_MAX,
+ };
+
+#ifdef OS_ANDROID
+ // The reason why the Payment Request was not shown to the user.
+ // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.chrome.browser.payments
+ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: NotShownReason
+ enum NotShownReason {
+ NOT_SHOWN_REASON_NO_MATCHING_PAYMENT_METHOD = 0,
+ NOT_SHOWN_REASON_NO_SUPPORTED_PAYMENT_METHOD = 1,
+ NOT_SHOWN_REASON_CONCURRENT_REQUESTS = 2,
+ NOT_SHOWN_REASON_OTHER = 3,
+ NOT_SHOWN_REASON_MAX = 4,
+ };
+#endif
JourneyLogger(bool is_incognito,
const GURL& url,
- ukm::UkmService* ukm_service);
+ ukm::UkmRecorder* ukm_recorder);
~JourneyLogger();
// Increments the number of selection adds for the specified section.
@@ -103,11 +167,28 @@ class JourneyLogger {
// Records that an event occurred.
void SetEventOccurred(Event event);
- // Records the histograms for all the sections that were requested by the
- // merchant and for the usage of the CanMakePayment method and its effect on
- // the transaction. This method should be called when the Payment Request has
- // either been completed or aborted.
- void RecordJourneyStatsHistograms(CompletionStatus completion_status);
+ // Records the payment method that was used to complete the Payment Request.
+ void SetSelectedPaymentMethod(SelectedPaymentMethod payment_method);
+
+ // Records the user information requested by the merchant.
+ void SetRequestedInformation(bool requested_shipping,
+ bool requested_email,
+ bool requested_phone,
+ bool requested_name);
+
+ // Records that the Payment Request was completed successfully, and starts the
+ // logging of all the journey metrics.
+ void SetCompleted();
+
+ // Records that the Payment Request was aborted along with the reason. Also
+ // starts the logging of all the journey metrics.
+ void SetAborted(AbortReason reason);
+
+#ifdef OS_ANDROID
+ // Records that the Payment Request was not shown to the user, along with the
+ // reason.
+ void SetNotShown(NotShownReason reason);
+#endif
private:
static const int NUMBER_OF_SECTIONS = 3;
@@ -137,6 +218,22 @@ class JourneyLogger {
};
// Records the histograms for all the sections that were requested by the
+ // merchant and for the usage of the CanMakePayment method and its effect on
+ // the transaction. This method should be called when the Payment Request has
+ // either been completed or aborted.
+ void RecordJourneyStatsHistograms(CompletionStatus completion_status);
+
+ // Records the histograms for all the steps of a complete checkout flow that
+ // were reached.
+ void RecordCheckoutFlowMetrics();
+
+ // Records the metric about the selected payment method.
+ void RecordPaymentMethodMetric();
+
+ // Records the user information that the merchant requested.
+ void RecordRequestedInformationMetrics();
+
+ // Records the histograms for all the sections that were requested by the
// merchant.
void RecordSectionSpecificStats(CompletionStatus completion_status);
@@ -157,18 +254,25 @@ class JourneyLogger {
void RecordUrlKeyedMetrics(CompletionStatus completion_status);
SectionStats sections_[NUMBER_OF_SECTIONS];
- bool was_can_make_payments_used_;
- bool could_make_payment_;
- bool was_show_called_;
+ bool has_recorded_ = false;
+ bool was_can_make_payments_used_ = false;
+ bool could_make_payment_ = false;
+ bool was_show_called_ = false;
bool is_incognito_;
// Accumulates the many events that have happened during the Payment Request.
int events_;
+ // To keep track of the selected payment method.
+ SelectedPaymentMethod payment_method_ = SELECTED_PAYMENT_METHOD_MAX;
+
+ // Keeps track of the user information requested by the merchant.
+ int requested_information_ = REQUESTED_INFORMATION_MAX;
+
const GURL url_;
// Not owned, will outlive this object.
- ukm::UkmService* ukm_service_;
+ ukm::UkmRecorder* ukm_recorder_;
DISALLOW_COPY_AND_ASSIGN(JourneyLogger);
};
diff --git a/chromium/components/payments/core/journey_logger_unittest.cc b/chromium/components/payments/core/journey_logger_unittest.cc
index fbb1efd4cbd..941fccb580d 100644
--- a/chromium/components/payments/core/journey_logger_unittest.cc
+++ b/chromium/components/payments/core/journey_logger_unittest.cc
@@ -7,10 +7,8 @@
#include "base/metrics/metrics_hashes.h"
#include "base/test/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
-#include "components/autofill/core/browser/autofill_experiments.h"
#include "components/metrics/proto/ukm/entry.pb.h"
-#include "components/ukm/test_ukm_service.h"
-#include "components/ukm/ukm_entry.h"
+#include "components/ukm/test_ukm_recorder.h"
#include "components/ukm/ukm_source.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -19,29 +17,15 @@ using testing::ContainerEq;
namespace payments {
-namespace {
-// Finds the specified UKM metric by |name| in the specified UKM |metrics|.
-const ukm::Entry_Metric* FindMetric(
- const char* name,
- const google::protobuf::RepeatedPtrField<ukm::Entry_Metric>& metrics) {
- for (const auto& metric : metrics) {
- if (metric.metric_hash() == base::HashMetricName(name))
- return &metric;
- }
- return nullptr;
-}
-} // namespace
-
// Tests the canMakePayment stats for the case where the merchant does not use
// it and does not show the PaymentRequest to the user.
TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentNotCalled_NoShow) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_COMPLETED);
+ logger.SetCompleted();
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_NOT_USED,
@@ -60,7 +44,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndUserAbort) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -70,8 +54,7 @@ TEST(JourneyLoggerTest,
// The merchant does not query CanMakePayment, show the PaymentRequest and the
// user aborts it.
logger.SetShowCalled();
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_NOT_USED,
@@ -90,7 +73,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndOtherAbort) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -100,8 +83,7 @@ TEST(JourneyLoggerTest,
// The merchant does not query CanMakePayment, show the PaymentRequest and
// there is an abort not initiated by the user.
logger.SetShowCalled();
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_NOT_USED,
@@ -120,7 +102,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndComplete) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -130,8 +112,7 @@ TEST(JourneyLoggerTest,
// The merchant does not query CanMakePayment, show the PaymentRequest and the
// user completes it.
logger.SetShowCalled();
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_COMPLETED);
+ logger.SetCompleted();
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_NOT_USED,
@@ -150,7 +131,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseAndNoShow) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -159,8 +140,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetCanMakePaymentValue(false);
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
@@ -169,7 +149,8 @@ TEST(JourneyLoggerTest,
// shown.
histogram_tester.ExpectBucketCount(
"PaymentRequest.CanMakePayment.Used.EffectOnShow",
- JourneyLogger::CMP_SHOW_COULD_NOT_MAKE_PAYMENT_AND_DID_NOT_SHOW, 1);
+ JourneyLogger::CMP_EFFECT_ON_SHOW_COULD_NOT_MAKE_PAYMENT_AND_DID_NOT_SHOW,
+ 1);
// There should be no completion stats since PR was not shown to the user.
EXPECT_THAT(
@@ -184,7 +165,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueAndNoShow) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -193,8 +174,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetCanMakePaymentValue(true);
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
@@ -203,7 +183,7 @@ TEST(JourneyLoggerTest,
// shown.
histogram_tester.ExpectBucketCount(
"PaymentRequest.CanMakePayment.Used.EffectOnShow",
- JourneyLogger::CMP_SHOW_COULD_MAKE_PAYMENT, 1);
+ JourneyLogger::CMP_EFFECT_ON_SHOW_COULD_MAKE_PAYMENT, 1);
// There should be no completion stats since PR was not shown to the user.
EXPECT_THAT(
@@ -218,7 +198,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndUserAbort) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -228,8 +208,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
logger.SetCanMakePaymentValue(false);
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
@@ -238,7 +217,7 @@ TEST(JourneyLoggerTest,
// shown.
histogram_tester.ExpectBucketCount(
"PaymentRequest.CanMakePayment.Used.EffectOnShow",
- JourneyLogger::CMP_SHOW_DID_SHOW, 1);
+ JourneyLogger::CMP_EFFECT_ON_SHOW_DID_SHOW, 1);
// There should be a record for an abort when CanMakePayment is false but the
// PR is shown to the user.
histogram_tester.ExpectBucketCount(
@@ -252,7 +231,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndOtherAbort) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -262,8 +241,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
logger.SetCanMakePaymentValue(false);
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
@@ -272,7 +250,7 @@ TEST(JourneyLoggerTest,
// shown.
histogram_tester.ExpectBucketCount(
"PaymentRequest.CanMakePayment.Used.EffectOnShow",
- JourneyLogger::CMP_SHOW_DID_SHOW, 1);
+ JourneyLogger::CMP_EFFECT_ON_SHOW_DID_SHOW, 1);
// There should be a record for an abort when CanMakePayment is false but the
// PR is shown to the user.
histogram_tester.ExpectBucketCount(
@@ -286,7 +264,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndComplete) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -296,8 +274,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
logger.SetCanMakePaymentValue(false);
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_COMPLETED);
+ logger.SetCompleted();
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
@@ -306,7 +283,7 @@ TEST(JourneyLoggerTest,
// shown.
histogram_tester.ExpectBucketCount(
"PaymentRequest.CanMakePayment.Used.EffectOnShow",
- JourneyLogger::CMP_SHOW_DID_SHOW, 1);
+ JourneyLogger::CMP_EFFECT_ON_SHOW_DID_SHOW, 1);
// There should be a record for an completion when CanMakePayment is false but
// the PR is shown to the user.
@@ -321,7 +298,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndUserAbort) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -331,8 +308,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
logger.SetCanMakePaymentValue(true);
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
@@ -341,8 +317,8 @@ TEST(JourneyLoggerTest,
// shown.
histogram_tester.ExpectBucketCount(
"PaymentRequest.CanMakePayment.Used.EffectOnShow",
- JourneyLogger::CMP_SHOW_DID_SHOW |
- JourneyLogger::CMP_SHOW_COULD_MAKE_PAYMENT,
+ JourneyLogger::CMP_EFFECT_ON_SHOW_DID_SHOW |
+ JourneyLogger::CMP_EFFECT_ON_SHOW_COULD_MAKE_PAYMENT,
1);
// There should be a record for an abort when CanMakePayment is true and the
// PR is shown to the user.
@@ -357,7 +333,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndOtherAbort) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -367,8 +343,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
logger.SetCanMakePaymentValue(true);
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
@@ -377,8 +352,8 @@ TEST(JourneyLoggerTest,
// shown.
histogram_tester.ExpectBucketCount(
"PaymentRequest.CanMakePayment.Used.EffectOnShow",
- JourneyLogger::CMP_SHOW_DID_SHOW |
- JourneyLogger::CMP_SHOW_COULD_MAKE_PAYMENT,
+ JourneyLogger::CMP_EFFECT_ON_SHOW_DID_SHOW |
+ JourneyLogger::CMP_EFFECT_ON_SHOW_COULD_MAKE_PAYMENT,
1);
// There should be a record for an abort when CanMakePayment is true and the
// PR is shown to the user.
@@ -393,7 +368,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndComplete) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -403,8 +378,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
logger.SetCanMakePaymentValue(true);
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_COMPLETED);
+ logger.SetCompleted();
histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
@@ -413,8 +387,8 @@ TEST(JourneyLoggerTest,
// shown.
histogram_tester.ExpectBucketCount(
"PaymentRequest.CanMakePayment.Used.EffectOnShow",
- JourneyLogger::CMP_SHOW_DID_SHOW |
- JourneyLogger::CMP_SHOW_COULD_MAKE_PAYMENT,
+ JourneyLogger::CMP_EFFECT_ON_SHOW_DID_SHOW |
+ JourneyLogger::CMP_EFFECT_ON_SHOW_COULD_MAKE_PAYMENT,
1);
// There should be a record for a completion when CanMakePayment is true and
// the PR is shown to the user.
@@ -429,7 +403,7 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_CanMakePayment_IncognitoTab) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -439,8 +413,7 @@ TEST(JourneyLoggerTest,
// The user cannot make payment and the PaymentRequest is not shown.
logger.SetShowCalled();
logger.SetCanMakePaymentValue(true);
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_COMPLETED);
+ logger.SetCompleted();
// Expect no log for CanMakePayment.
EXPECT_THAT(
@@ -454,14 +427,13 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_SuggestionsForEverything_Completed) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Simulate that the user had suggestions for all the requested sections.
logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
// Simulate that the user completes the checkout.
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_COMPLETED);
+ logger.SetCompleted();
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
@@ -479,14 +451,13 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_SuggestionsForEverything_UserAborted) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Simulate that the user had suggestions for all the requested sections.
logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
// Simulate that the user aborts the checkout.
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
@@ -504,14 +475,13 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_SuggestionsForEverything_OtherAborted) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Simulate that the user had suggestions for all the requested sections.
logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
// Simulate that the checkout is aborted.
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
@@ -530,14 +500,13 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_SuggestionsForEverything_Incognito) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Simulate that the user had suggestions for all the requested sections.
logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
// Simulate that the user completes the checkout.
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_COMPLETED);
+ logger.SetCompleted();
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
@@ -555,14 +524,13 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_NoSuggestionsForEverything_Completed) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Simulate that the user had suggestions for all the requested sections.
logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
// Simulate that the user completes the checkout.
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_COMPLETED);
+ logger.SetCompleted();
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
@@ -581,14 +549,13 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_NoSuggestionsForEverything_UserAborted) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Simulate that the user had suggestions for all the requested sections.
logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
// Simulate that the user aborts the checkout.
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
@@ -607,14 +574,13 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_NoSuggestionsForEverything_OtherAborted) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Simulate that the user had suggestions for all the requested sections.
logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
- // Simulate that the user aborts the checkout.
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+ // Simulate that the the checkout is aborted.
+ logger.SetAborted(JourneyLogger::ABORT_REASON_OTHER);
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
@@ -634,14 +600,13 @@ TEST(JourneyLoggerTest,
RecordJourneyStatsHistograms_NoSuggestionsForEverything_Incognito) {
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Simulate that the user had suggestions for all the requested sections.
logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
// Simulate that the user aborts the checkout.
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
histogram_tester.ExpectBucketCount(
"PaymentRequest.UserDidNotHaveSuggestionsForEverything."
@@ -659,9 +624,9 @@ TEST(JourneyLoggerTest,
TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_TwoPaymentRequests) {
base::HistogramTester histogram_tester;
JourneyLogger logger1(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
JourneyLogger logger2(/*is_incognito=*/false, /*url=*/GURL(""),
- /*ukm_service=*/nullptr);
+ /*ukm_recorder=*/nullptr);
// Make the two loggers have different data.
logger1.SetShowCalled();
@@ -673,10 +638,8 @@ TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_TwoPaymentRequests) {
logger2.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
// Simulate that the user completes one checkout and aborts the other.
- logger1.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_COMPLETED);
- logger2.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+ logger1.SetCompleted();
+ logger2.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
// Make sure the appropriate metrics were logged for logger1.
histogram_tester.ExpectBucketCount(
@@ -703,51 +666,41 @@ TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_TwoPaymentRequests) {
// Tests that the Payment Request UKMs are logged correctly.
TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_CheckoutFunnelUkm) {
- base::test::ScopedFeatureList scoped_feature_list_;
- scoped_feature_list_.InitAndEnableFeature(autofill::kAutofillUkmLogging);
-
- ukm::UkmServiceTestingHarness ukm_service_test_harness;
- ukm::TestUkmService* ukm_service =
- ukm_service_test_harness.test_ukm_service();
+ ukm::TestUkmRecorder ukm_recorder;
char test_url[] = "http://www.google.com/";
base::HistogramTester histogram_tester;
JourneyLogger logger(/*is_incognito=*/true, /*url=*/GURL(test_url),
- /*ukm_service=*/ukm_service);
+ /*ukm_recorder=*/&ukm_recorder);
// Simulate that the user aborts after being shown the Payment Request and
// clicking pay.
logger.SetEventOccurred(JourneyLogger::EVENT_SHOWN);
logger.SetEventOccurred(JourneyLogger::EVENT_PAY_CLICKED);
- logger.RecordJourneyStatsHistograms(
- JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+ logger.SetAborted(JourneyLogger::ABORT_REASON_ABORTED_BY_USER);
// Make sure the UKM was logged correctly.
- ASSERT_EQ(1U, ukm_service->sources_count());
- const ukm::UkmSource* source = ukm_service->GetSourceForUrl(test_url);
+ ASSERT_EQ(1U, ukm_recorder.sources_count());
+ const ukm::UkmSource* source = ukm_recorder.GetSourceForUrl(test_url);
ASSERT_NE(nullptr, source);
- ASSERT_EQ(1U, ukm_service->entries_count());
- const ukm::UkmEntry* entry = ukm_service->GetEntry(0);
- EXPECT_EQ(source->id(), entry->source_id());
-
- ukm::Entry entry_proto;
- entry->PopulateProto(&entry_proto);
- EXPECT_EQ(source->id(), entry_proto.source_id());
+ ASSERT_EQ(1U, ukm_recorder.entries_count());
+ const ukm::mojom::UkmEntry* entry = ukm_recorder.GetEntry(0);
+ EXPECT_EQ(source->id(), entry->source_id);
EXPECT_EQ(base::HashMetricName(internal::kUKMCheckoutEventsEntryName),
- entry_proto.event_hash());
+ entry->event_hash);
- const ukm::Entry_Metric* status_metric = FindMetric(
- internal::kUKMCompletionStatusMetricName, entry_proto.metrics());
+ const ukm::mojom::UkmMetric* status_metric = ukm::TestUkmRecorder::FindMetric(
+ entry, internal::kUKMCompletionStatusMetricName);
ASSERT_NE(nullptr, status_metric);
EXPECT_EQ(JourneyLogger::COMPLETION_STATUS_USER_ABORTED,
- status_metric->value());
+ status_metric->value);
- const ukm::Entry_Metric* step_metric =
- FindMetric(internal::kUKMEventsMetricName, entry_proto.metrics());
+ const ukm::mojom::UkmMetric* step_metric =
+ ukm::TestUkmRecorder::FindMetric(entry, internal::kUKMEventsMetricName);
ASSERT_NE(nullptr, step_metric);
EXPECT_EQ(JourneyLogger::EVENT_SHOWN | JourneyLogger::EVENT_PAY_CLICKED,
- step_metric->value());
+ step_metric->value);
}
-} // namespace payments \ No newline at end of file
+} // namespace payments
diff --git a/chromium/components/payments/core/payment_instrument.cc b/chromium/components/payments/core/payment_instrument.cc
index 097917c8427..7f2c8a0f0e8 100644
--- a/chromium/components/payments/core/payment_instrument.cc
+++ b/chromium/components/payments/core/payment_instrument.cc
@@ -7,13 +7,9 @@
namespace payments {
PaymentInstrument::PaymentInstrument(const std::string& method_name,
- const base::string16& label,
- const base::string16& sublabel,
int icon_resource_id,
Type type)
: method_name_(method_name),
- label_(label),
- sublabel_(sublabel),
icon_resource_id_(icon_resource_id),
type_(type) {}
diff --git a/chromium/components/payments/core/payment_instrument.h b/chromium/components/payments/core/payment_instrument.h
index 5d70847f4f2..6da07693d8e 100644
--- a/chromium/components/payments/core/payment_instrument.h
+++ b/chromium/components/payments/core/payment_instrument.h
@@ -39,27 +39,29 @@ class PaymentInstrument {
// Returns whether the instrument is complete to be used as a payment method
// without further editing.
virtual bool IsCompleteForPayment() = 0;
+ // Returns a message to indicate to the user what's missing for the instrument
+ // to be complete for payment.
+ virtual base::string16 GetMissingInfoLabel() = 0;
// Returns whether the instrument is valid for the purposes of responding to
// canMakePayment.
virtual bool IsValidForCanMakePayment() = 0;
+ // Records the use of this payment instrument.
+ virtual void RecordUse() = 0;
+ // Return the sub/label of payment instrument, to be displayed to the user.
+ virtual base::string16 GetLabel() const = 0;
+ virtual base::string16 GetSublabel() const = 0;
const std::string& method_name() const { return method_name_; }
- const base::string16& label() const { return label_; }
- const base::string16& sublabel() const { return sublabel_; }
int icon_resource_id() const { return icon_resource_id_; }
Type type() { return type_; }
protected:
PaymentInstrument(const std::string& method_name,
- const base::string16& label,
- const base::string16& sublabel,
int icon_resource_id,
Type type);
private:
const std::string method_name_;
- const base::string16 label_;
- const base::string16 sublabel_;
int icon_resource_id_;
Type type_;
diff --git a/chromium/components/payments/core/payment_prefs.cc b/chromium/components/payments/core/payment_prefs.cc
new file mode 100644
index 00000000000..854769dad25
--- /dev/null
+++ b/chromium/components/payments/core/payment_prefs.cc
@@ -0,0 +1,18 @@
+// Copyright 2017 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.
+
+#include "components/payments/core/payment_prefs.h"
+
+#include "components/pref_registry/pref_registry_syncable.h"
+
+namespace payments {
+
+const char kPaymentsFirstTransactionCompleted[] =
+ "payments.first_transaction_completed";
+
+void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterBooleanPref(kPaymentsFirstTransactionCompleted, false);
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/core/payment_prefs.h b/chromium/components/payments/core/payment_prefs.h
new file mode 100644
index 00000000000..1d5917ff2ac
--- /dev/null
+++ b/chromium/components/payments/core/payment_prefs.h
@@ -0,0 +1,22 @@
+// Copyright 2017 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.
+
+#ifndef COMPONENTS_PAYMENTS_CORE_PAYMENT_PREFS_H_
+#define COMPONENTS_PAYMENTS_CORE_PAYMENT_PREFS_H_
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+namespace payments {
+
+// True if the profile has already successfully completed at least one payment
+// request transaction.
+extern const char kPaymentsFirstTransactionCompleted[];
+
+void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CORE_PAYMENT_PREFS_H_
diff --git a/chromium/components/payments/core/payment_request_data_util.cc b/chromium/components/payments/core/payment_request_data_util.cc
index 8fb65723122..7f689419603 100644
--- a/chromium/components/payments/core/payment_request_data_util.cc
+++ b/chromium/components/payments/core/payment_request_data_util.cc
@@ -7,10 +7,13 @@
#include "base/strings/string16.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/credit_card.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/personal_data_manager.h"
+#include "components/autofill/core/browser/validation.h"
#include "components/payments/core/basic_card_response.h"
#include "components/payments/core/payment_address.h"
#include "components/payments/core/payment_method_data.h"
@@ -71,7 +74,7 @@ PaymentAddress GetPaymentAddressFromAutofillProfile(
BasicCardResponse GetBasicCardResponseFromAutofillCreditCard(
const autofill::CreditCard& card,
const base::string16& cvc,
- const std::vector<autofill::AutofillProfile*>& billing_profiles,
+ const autofill::AutofillProfile& billing_profile,
const std::string& app_locale) {
BasicCardResponse response;
response.cardholder_name = card.GetRawInfo(autofill::CREDIT_CARD_NAME_FULL);
@@ -81,79 +84,99 @@ BasicCardResponse GetBasicCardResponseFromAutofillCreditCard(
card.GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR);
response.card_security_code = cvc;
- // TODO(crbug.com/602666): Ensure we reach here only if the card has a billing
- // address. Then add DCHECK(!card->billing_address_id().empty()).
- if (!card.billing_address_id().empty()) {
- const autofill::AutofillProfile* billing_address =
- autofill::PersonalDataManager::GetProfileFromProfilesByGUID(
- card.billing_address_id(), billing_profiles);
- DCHECK(billing_address);
- response.billing_address =
- GetPaymentAddressFromAutofillProfile(*billing_address, app_locale);
- }
+ response.billing_address =
+ GetPaymentAddressFromAutofillProfile(billing_profile, app_locale);
return response;
}
-bool ParseBasicCardSupportedNetworks(
+void ParseBasicCardSupportedNetworks(
const std::vector<PaymentMethodData>& method_data,
std::vector<std::string>* out_supported_networks,
std::set<std::string>* out_basic_card_specified_networks) {
DCHECK(out_supported_networks->empty());
DCHECK(out_basic_card_specified_networks->empty());
- std::set<std::string> card_networks{"amex", "diners", "discover",
- "jcb", "mastercard", "mir",
- "unionpay", "visa"};
+ const std::set<std::string> kBasicCardNetworks{
+ "amex", "diners", "discover", "jcb",
+ "mastercard", "mir", "unionpay", "visa"};
+ std::set<std::string> remaining_card_networks(kBasicCardNetworks);
for (const PaymentMethodData& method_data_entry : method_data) {
if (method_data_entry.supported_methods.empty())
- return false;
+ return;
for (const std::string& method : method_data_entry.supported_methods) {
if (method.empty())
continue;
- // If a card network is specified right in "supportedMethods", add it.
const char kBasicCardMethodName[] = "basic-card";
- auto card_it = card_networks.find(method);
- if (card_it != card_networks.end()) {
+ // If a card network is specified right in "supportedMethods", add it.
+ auto card_it = remaining_card_networks.find(method);
+ if (card_it != remaining_card_networks.end()) {
out_supported_networks->push_back(method);
- // |method| removed from |card_networks| so that it is not doubly added
- // to |supported_card_networks_| if "basic-card" is specified with no
- // supported networks.
- card_networks.erase(card_it);
+ // |method| removed from |remaining_card_networks| so that it is not
+ // doubly added to |out_supported_networks|.
+ remaining_card_networks.erase(card_it);
} else if (method == kBasicCardMethodName) {
// For the "basic-card" method, check "supportedNetworks".
if (method_data_entry.supported_networks.empty()) {
// Empty |supported_networks| means all networks are supported.
out_supported_networks->insert(out_supported_networks->end(),
- card_networks.begin(),
- card_networks.end());
- out_basic_card_specified_networks->insert(card_networks.begin(),
- card_networks.end());
+ remaining_card_networks.begin(),
+ remaining_card_networks.end());
+ out_basic_card_specified_networks->insert(kBasicCardNetworks.begin(),
+ kBasicCardNetworks.end());
// Clear the set so that no further networks are added to
// |out_supported_networks|.
- card_networks.clear();
+ remaining_card_networks.clear();
} else {
// The merchant has specified a few basic card supported networks. Use
// the mapping to transform to known basic-card types.
for (const std::string& supported_network :
method_data_entry.supported_networks) {
// Make sure that the network was not already added to
- // |out_supported_networks|. If it's still in |card_networks| it's
- // fair game.
- auto it = card_networks.find(supported_network);
- if (it != card_networks.end()) {
+ // |out_supported_networks|. If it's still in
+ // |remaining_card_networks| it's fair game.
+ auto it = remaining_card_networks.find(supported_network);
+ if (it != remaining_card_networks.end()) {
out_supported_networks->push_back(supported_network);
+ remaining_card_networks.erase(it);
+ }
+ if (kBasicCardNetworks.find(supported_network) !=
+ kBasicCardNetworks.end()) {
out_basic_card_specified_networks->insert(supported_network);
- card_networks.erase(it);
}
}
}
}
}
}
- return true;
+}
+
+base::string16 GetFormattedPhoneNumberForDisplay(
+ const autofill::AutofillProfile& profile,
+ const std::string& locale) {
+ // Since the "+" is removed for some country's phone numbers, try to add a "+"
+ // and see if it is a valid phone number for a country.
+ // Having two "+" in front of a number has no effect on the formatted number.
+ // The reason for this is international phone numbers for another country. For
+ // example, without adding a "+", the US number 1-415-123-1234 for an AU
+ // address would be wrongly formatted as +61 1-415-123-1234 which is invalid.
+ std::string phone = base::UTF16ToUTF8(profile.GetInfo(
+ autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER), locale));
+ std::string tentative_intl_phone = "+" + phone;
+
+ // Always favor the tentative international phone number if it's determined as
+ // being a valid number.
+ if (autofill::IsValidPhoneNumber(
+ base::UTF8ToUTF16(tentative_intl_phone),
+ GetCountryCodeWithFallback(&profile, locale))) {
+ return base::UTF8ToUTF16(FormatPhoneForDisplay(
+ tentative_intl_phone, GetCountryCodeWithFallback(&profile, locale)));
+ }
+
+ return base::UTF8ToUTF16(FormatPhoneForDisplay(
+ phone, GetCountryCodeWithFallback(&profile, locale)));
}
std::string FormatPhoneForDisplay(const std::string& phone_number,
@@ -168,5 +191,14 @@ std::string FormatPhoneForResponse(const std::string& phone_number,
PhoneNumberUtil::PhoneNumberFormat::E164);
}
+std::string GetCountryCodeWithFallback(const autofill::AutofillProfile* profile,
+ const std::string& app_locale) {
+ std::string country_code =
+ base::UTF16ToUTF8(profile->GetRawInfo(autofill::ADDRESS_HOME_COUNTRY));
+ if (!autofill::data_util::IsValidCountryCode(country_code))
+ country_code = autofill::AutofillCountry::CountryCodeForLocale(app_locale);
+ return country_code;
+}
+
} // namespace data_util
} // namespace payments
diff --git a/chromium/components/payments/core/payment_request_data_util.h b/chromium/components/payments/core/payment_request_data_util.h
index f3120c8c3d6..c301946d5e5 100644
--- a/chromium/components/payments/core/payment_request_data_util.h
+++ b/chromium/components/payments/core/payment_request_data_util.h
@@ -35,7 +35,7 @@ PaymentAddress GetPaymentAddressFromAutofillProfile(
BasicCardResponse GetBasicCardResponseFromAutofillCreditCard(
const autofill::CreditCard& card,
const base::string16& cvc,
- const std::vector<autofill::AutofillProfile*>& billing_profiles,
+ const autofill::AutofillProfile& billing_profile,
const std::string& app_locale);
// Parse the supported card networks from supportedMethods and "basic-card"'s
@@ -44,15 +44,19 @@ BasicCardResponse GetBasicCardResponseFromAutofillCreditCard(
// |out_basic_card_supported_networks| is a subset of |out_supported_networks|
// that includes all networks that were specified as part of "basic-card". This
// is used to know whether to return the card network name (e.g., "visa") or
-// "basic-card" in the PaymentResponse. Returns true on success, false on
-// invalid data specified. |method_data.supported_networks| is expected to only
-// contain basic-card card network names (the list is at
+// "basic-card" in the PaymentResponse. |method_data.supported_networks| is
+// expected to only contain basic-card card network names (the list is at
// https://www.w3.org/Payments/card-network-ids).
-bool ParseBasicCardSupportedNetworks(
+void ParseBasicCardSupportedNetworks(
const std::vector<PaymentMethodData>& method_data,
std::vector<std::string>* out_supported_networks,
std::set<std::string>* out_basic_card_supported_networks);
+// Returns the phone number from the given |profile| formatted for display.
+base::string16 GetFormattedPhoneNumberForDisplay(
+ const autofill::AutofillProfile& profile,
+ const std::string& locale);
+
// Formats the given number |phone_number| to
// i18n::phonenumbers::PhoneNumberUtil::PhoneNumberFormat::INTERNATIONAL format
// by using i18n::phonenumbers::PhoneNumberUtil::Format.
@@ -67,6 +71,12 @@ std::string FormatPhoneForDisplay(const std::string& phone_number,
std::string FormatPhoneForResponse(const std::string& phone_number,
const std::string& country_code);
+// Returns a country code to be used when validating this profile. If the
+// profile has a valid country code set, it is returned. If not, a country code
+// associated with |app_locale| is used as a fallback.
+std::string GetCountryCodeWithFallback(const autofill::AutofillProfile* profile,
+ const std::string& app_locale);
+
} // namespace data_util
} // namespace payments
diff --git a/chromium/components/payments/core/payment_request_data_util_unittest.cc b/chromium/components/payments/core/payment_request_data_util_unittest.cc
index 5ad7e109637..3181a47d282 100644
--- a/chromium/components/payments/core/payment_request_data_util_unittest.cc
+++ b/chromium/components/payments/core/payment_request_data_util_unittest.cc
@@ -7,6 +7,7 @@
#include <memory>
#include "base/json/json_writer.h"
+#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "components/autofill/core/browser/autofill_profile.h"
@@ -49,8 +50,7 @@ TEST(PaymentRequestDataUtilTest, GetBasicCardResponseFromAutofillCreditCard) {
card.set_billing_address_id(address.guid());
std::unique_ptr<base::DictionaryValue> response_value =
payments::data_util::GetBasicCardResponseFromAutofillCreditCard(
- card, base::ASCIIToUTF16("123"),
- std::vector<autofill::AutofillProfile*>{&address}, "en-US")
+ card, base::ASCIIToUTF16("123"), address, "en-US")
.ToDictionaryValue();
std::string json_response;
base::JSONWriter::Write(*response_value, &json_response);
@@ -91,5 +91,171 @@ TEST(PaymentRequestDataUtilTest, FormatPhoneForDisplay) {
payments::data_util::FormatPhoneForDisplay("142685300", "FR"));
}
+// Test for the GetFormattedPhoneNumberForDisplay method.
+struct PhoneNumberFormatCase {
+ PhoneNumberFormatCase(const char* phone,
+ const char* country,
+ const char* expected_format,
+ const char* locale = "")
+ : phone(phone),
+ country(country),
+ expected_format(expected_format),
+ locale(locale) {}
+
+ const char* phone;
+ const char* country;
+ const char* expected_format;
+ const char* locale;
+};
+
+class GetFormattedPhoneNumberForDisplayTest
+ : public testing::TestWithParam<PhoneNumberFormatCase> {};
+
+TEST_P(GetFormattedPhoneNumberForDisplayTest,
+ GetFormattedPhoneNumberForDisplay) {
+ autofill::AutofillProfile profile;
+ profile.SetRawInfo(autofill::PHONE_HOME_WHOLE_NUMBER,
+ base::UTF8ToUTF16(GetParam().phone));
+ profile.SetRawInfo(autofill::ADDRESS_HOME_COUNTRY,
+ base::UTF8ToUTF16(GetParam().country));
+ EXPECT_EQ(GetParam().expected_format,
+ base::UTF16ToUTF8(
+ GetFormattedPhoneNumberForDisplay(profile, GetParam().locale)));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ GetFormattedPhoneNumberForDisplay,
+ GetFormattedPhoneNumberForDisplayTest,
+ testing::Values(
+ //////////////////////////
+ // US phone in US.
+ //////////////////////////
+ // Formatted phone numbers.
+ PhoneNumberFormatCase("+1 415-555-5555", "US", "+1 415-555-5555"),
+ PhoneNumberFormatCase("1 415-555-5555", "US", "+1 415-555-5555"),
+ PhoneNumberFormatCase("415-555-5555", "US", "+1 415-555-5555"),
+ // Raw phone numbers.
+ PhoneNumberFormatCase("+14155555555", "US", "+1 415-555-5555"),
+ PhoneNumberFormatCase("14155555555", "US", "+1 415-555-5555"),
+ PhoneNumberFormatCase("4155555555", "US", "+1 415-555-5555"),
+
+ //////////////////////////
+ // US phone in CA.
+ //////////////////////////
+ // Formatted phone numbers.
+ PhoneNumberFormatCase("+1 415-555-5555", "CA", "+1 415-555-5555"),
+ PhoneNumberFormatCase("1 415-555-5555", "CA", "+1 415-555-5555"),
+ PhoneNumberFormatCase("415-555-5555", "CA", "+1 415-555-5555"),
+ // Raw phone numbers.
+ PhoneNumberFormatCase("+14155555555", "CA", "+1 415-555-5555"),
+ PhoneNumberFormatCase("14155555555", "CA", "+1 415-555-5555"),
+ PhoneNumberFormatCase("4155555555", "CA", "+1 415-555-5555"),
+
+ //////////////////////////
+ // US phone in AU.
+ //////////////////////////
+ // A US phone with the country code is correctly formatted as an US
+ // number.
+ PhoneNumberFormatCase("+1 415-555-5555", "AU", "+1 415-555-5555"),
+ PhoneNumberFormatCase("1 415-555-5555", "AU", "+1 415-555-5555"),
+ // Without a country code, the phone is formatted for the profile's
+ // country, even if it's invalid.
+ PhoneNumberFormatCase("415-555-5555", "AU", "+61 4155555555"),
+
+ //////////////////////////
+ // US phone in MX.
+ //////////////////////////
+ // A US phone with the country code is correctly formatted as an US
+ // number.
+ PhoneNumberFormatCase("+1 415-555-5555", "MX", "+1 415-555-5555"),
+ // "+52 1 415 555 5555" is a valid number for Mexico,
+ PhoneNumberFormatCase("1 415-555-5555", "MX", "+52 1 415 555 5555"),
+ // Without a country code, the phone is formatted for the profile's
+ // country.
+ PhoneNumberFormatCase("415-555-5555", "MX", "+52 415 555 5555"),
+
+ //////////////////////////
+ // AU phone in AU.
+ //////////////////////////
+ // Formatted phone numbers.
+ PhoneNumberFormatCase("+61 2 9374 4000", "AU", "+61 2 9374 4000"),
+ PhoneNumberFormatCase("61 2 9374 4000", "AU", "+61 2 9374 4000"),
+ PhoneNumberFormatCase("02 9374 4000", "AU", "+61 2 9374 4000"),
+ PhoneNumberFormatCase("2 9374 4000", "AU", "+61 2 9374 4000"),
+ // Raw phone numbers.
+ PhoneNumberFormatCase("+61293744000", "AU", "+61 2 9374 4000"),
+ PhoneNumberFormatCase("61293744000", "AU", "+61 2 9374 4000"),
+ PhoneNumberFormatCase("0293744000", "AU", "+61 2 9374 4000"),
+ PhoneNumberFormatCase("293744000", "AU", "+61 2 9374 4000"),
+
+ //////////////////////////
+ // AU phone in US.
+ //////////////////////////
+ // An AU phone with the country code is correctly formatted as an AU
+ // number.
+ PhoneNumberFormatCase("+61 2 9374 4000", "US", "+61 2 9374 4000"),
+ PhoneNumberFormatCase("61 2 9374 4000", "US", "+61 2 9374 4000"),
+ // Without a country code, the phone is formatted for the profile's
+ // country.
+ // This local AU number fits the length of a US number, so it's
+ // formatted for US.
+ PhoneNumberFormatCase("02 9374 4000", "US", "+1 029-374-4000"),
+ // This local AU number is formatted as an US number, even if it's
+ // invlaid.
+ PhoneNumberFormatCase("2 9374 4000", "US", "+1 293744000"),
+
+ //////////////////////////
+ // MX phone in MX.
+ //////////////////////////
+ // Formatted phone numbers.
+ PhoneNumberFormatCase("+52 55 5342 8400", "MX", "+52 55 5342 8400"),
+ PhoneNumberFormatCase("52 55 5342 8400", "MX", "+52 55 5342 8400"),
+ PhoneNumberFormatCase("55 5342 8400", "MX", "+52 55 5342 8400"),
+ // Raw phone numbers.
+ PhoneNumberFormatCase("+525553428400", "MX", "+52 55 5342 8400"),
+ PhoneNumberFormatCase("525553428400", "MX", "+52 55 5342 8400"),
+ PhoneNumberFormatCase("5553428400", "MX", "+52 55 5342 8400"),
+
+ //////////////////////////
+ // MX phone in US.
+ //////////////////////////
+ // A MX phone with the country code is correctly formatted as a MX
+ // number.
+ PhoneNumberFormatCase("+52 55 5342 8400", "US", "+52 55 5342 8400"),
+ PhoneNumberFormatCase("52 55 5342 8400", "US", "+52 55 5342 8400"),
+ // This local MX number fits the length of a US number, so it's
+ // formatted for US.
+ PhoneNumberFormatCase("55 5342 8400", "US", "+1 555-342-8400")));
+
+INSTANTIATE_TEST_CASE_P(
+ GetFormattedPhoneNumberForDisplay_EdgeCases,
+ GetFormattedPhoneNumberForDisplayTest,
+ testing::Values(
+ //////////////////////////
+ // No country.
+ //////////////////////////
+ // Fallback to locale if no country is set.
+ PhoneNumberFormatCase("52 55 5342 8400",
+ "",
+ "+52 55 5342 8400",
+ "es_MX"),
+ PhoneNumberFormatCase("55 5342 8400", "", "+52 55 5342 8400", "es_MX"),
+ PhoneNumberFormatCase("55 5342 8400", "", "+1 555-342-8400", "en_US"),
+ PhoneNumberFormatCase("61 2 9374 4000", "", "+61 2 9374 4000", "en_AU"),
+ PhoneNumberFormatCase("02 9374 4000", "", "+61 2 9374 4000", "en_AU"),
+
+ //////////////////////////
+ // No country or locale.
+ //////////////////////////
+ // Format according to the country code.
+ PhoneNumberFormatCase("61 2 9374 4000", "", "+61 2 9374 4000"),
+ PhoneNumberFormatCase("52 55 5342 8400", "", "+52 55 5342 8400"),
+ PhoneNumberFormatCase("1 415 555 5555", "", "+1 415-555-5555"),
+ // If no country code is found, formats for US.
+ PhoneNumberFormatCase("02 9374 4000", "", "+1 029-374-4000"),
+ PhoneNumberFormatCase("2 9374 4000", "", "+1 293744000"),
+ PhoneNumberFormatCase("55 5342 8400", "", "+1 555-342-8400"),
+ PhoneNumberFormatCase("52 55 5342 8400", "", "+52 55 5342 8400"),
+ PhoneNumberFormatCase("415-555-5555", "", "+1 415-555-5555")));
} // namespace data_util
} // namespace payments
diff --git a/chromium/components/payments/core/payment_request_delegate.h b/chromium/components/payments/core/payment_request_delegate.h
index aab278af6f2..53a44c70626 100644
--- a/chromium/components/payments/core/payment_request_delegate.h
+++ b/chromium/components/payments/core/payment_request_delegate.h
@@ -10,23 +10,24 @@
#include "base/memory/weak_ptr.h"
#include "components/autofill/core/browser/payments/full_card_request.h"
-#include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h"
-#include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h"
-namespace i18n {
-namespace addressinput {
-class Storage;
-class Source;
-} // namespace addressinput
-} // namespace i18n
+class GURL;
namespace autofill {
class CreditCard;
class PersonalDataManager;
+class RegionDataLoader;
} // namespace autofill
+class PrefService;
+
+namespace ukm {
+class UkmRecorder;
+} // namespace ukm
+
namespace payments {
+class AddressNormalizer;
class PaymentRequest;
class PaymentRequestDelegate {
@@ -53,17 +54,36 @@ class PaymentRequestDelegate {
// Returns whether the user is in Incognito mode.
virtual bool IsIncognito() const = 0;
+ // Returns true if the SSL certificate is valid. Should be called only for
+ // cryptographic schemes.
+ virtual bool IsSslCertificateValid() = 0;
+
+ // Returns the URL of the page that is currently being displayed.
+ virtual const GURL& GetLastCommittedURL() const = 0;
+
// Starts a FullCardRequest to unmask |credit_card|.
virtual void DoFullCardRequest(
const autofill::CreditCard& credit_card,
base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
result_delegate) = 0;
- // Returns the source and storage for country/region data loads.
- virtual std::unique_ptr<const ::i18n::addressinput::Source>
- GetAddressInputSource() = 0;
- virtual std::unique_ptr<::i18n::addressinput::Storage>
- GetAddressInputStorage() = 0;
+ // Returns a pointer to the address normalizer to use for the duration of this
+ // Payment Request.
+ virtual AddressNormalizer* GetAddressNormalizer() = 0;
+
+ // Creates a new region data loader that will self delete, or a test mock.
+ virtual autofill::RegionDataLoader* GetRegionDataLoader() = 0;
+
+ // Returns a pointer to the UKM service.
+ virtual ukm::UkmRecorder* GetUkmRecorder() = 0;
+
+ // Returns the user's signed-in email address, or empty string if not signed
+ // in.
+ virtual std::string GetAuthenticatedEmail() const = 0;
+
+ // Gets the pref service for the browser context associated with this
+ // PaymentRequest.
+ virtual PrefService* GetPrefService() = 0;
};
} // namespace payments
diff --git a/chromium/components/payments/core/payments_profile_comparator.cc b/chromium/components/payments/core/payments_profile_comparator.cc
new file mode 100644
index 00000000000..3190bacf2a9
--- /dev/null
+++ b/chromium/components/payments/core/payments_profile_comparator.cc
@@ -0,0 +1,286 @@
+// Copyright 2017 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.
+
+#include "components/payments/core/payments_profile_comparator.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/address_i18n.h"
+#include "components/autofill/core/browser/autofill_country.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/validation.h"
+#include "components/payments/core/payment_options_provider.h"
+#include "components/payments/core/payment_request_data_util.h"
+#include "components/strings/grit/components_strings.h"
+#include "third_party/libaddressinput/chromium/addressinput_util.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace payments {
+
+PaymentsProfileComparator::PaymentsProfileComparator(
+ const std::string& app_locale,
+ const PaymentOptionsProvider& options)
+ : autofill::AutofillProfileComparator(app_locale), options_(options) {}
+
+PaymentsProfileComparator::~PaymentsProfileComparator() {}
+
+PaymentsProfileComparator::ProfileFields
+PaymentsProfileComparator::GetMissingProfileFields(
+ const autofill::AutofillProfile* profile) const {
+ if (!profile)
+ return kName | kPhone | kEmail | kAddress;
+
+ if (!cache_.count(profile->guid())) {
+ cache_[profile->guid()] = ComputeMissingFields(*profile);
+ } else {
+ // Cache hit. In debug mode, recompute and check that invalidation has
+ // occurred where necessary.
+ DCHECK_EQ(cache_[profile->guid()], ComputeMissingFields(*profile))
+ << "Profiles must be invalidated when their contents change.";
+ }
+
+ return cache_[profile->guid()];
+}
+
+std::vector<autofill::AutofillProfile*>
+PaymentsProfileComparator::FilterProfilesForContact(
+ const std::vector<autofill::AutofillProfile*>& profiles) const {
+ // We will be removing profiles, so we operate on a copy.
+ std::vector<autofill::AutofillProfile*> processed = profiles;
+
+ // Stable sort, since profiles are expected to be passed in frecency order.
+ std::stable_sort(
+ processed.begin(), processed.end(),
+ std::bind(&PaymentsProfileComparator::IsContactMoreComplete, this,
+ std::placeholders::_1, std::placeholders::_2));
+
+ auto it = processed.begin();
+ while (it != processed.end()) {
+ if (GetContactCompletenessScore(*it) == 0) {
+ // Since profiles are sorted by completeness, this and any further
+ // profiles can be discarded.
+ processed.erase(it, processed.end());
+ break;
+ }
+
+ // Attempt to find a matching element in the vector before the current.
+ // This is quadratic, but the number of elements is generally small
+ // (< 10), so a more complicated algorithm would be overkill.
+ if (std::find_if(processed.begin(), it,
+ [&](autofill::AutofillProfile* prior) {
+ return IsContactEqualOrSuperset(*prior, **it);
+ }) != it) {
+ // Remove the subset profile. |it| will point to the next element after
+ // erasure.
+ it = processed.erase(it);
+ } else {
+ it++;
+ }
+ }
+
+ return processed;
+}
+
+bool PaymentsProfileComparator::IsContactEqualOrSuperset(
+ const autofill::AutofillProfile& super,
+ const autofill::AutofillProfile& sub) const {
+ if (options_.request_payer_name()) {
+ if (sub.HasInfo(autofill::NAME_FULL) &&
+ !super.HasInfo(autofill::NAME_FULL)) {
+ return false;
+ }
+ if (!HaveMergeableNames(super, sub))
+ return false;
+ }
+ if (options_.request_payer_phone()) {
+ if (sub.HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER) &&
+ !super.HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER)) {
+ return false;
+ }
+ if (!HaveMergeablePhoneNumbers(super, sub))
+ return false;
+ }
+ if (options_.request_payer_email()) {
+ if (sub.HasInfo(autofill::EMAIL_ADDRESS) &&
+ !super.HasInfo(autofill::EMAIL_ADDRESS)) {
+ return false;
+ }
+ if (!HaveMergeableEmailAddresses(super, sub))
+ return false;
+ }
+ return true;
+}
+
+int PaymentsProfileComparator::GetContactCompletenessScore(
+ const autofill::AutofillProfile* profile) const {
+ // Create a bitmask of the fields that are both present and required.
+ ProfileFields present =
+ ~GetMissingProfileFields(profile) & GetRequiredProfileFieldsForContact();
+
+ // Count how many are set.
+ return !!(present & kName) + !!(present & kPhone) + !!(present & kEmail);
+}
+
+bool PaymentsProfileComparator::IsContactInfoComplete(
+ const autofill::AutofillProfile* profile) const {
+ // Mask the fields that are missing with those that are requried. If any bits
+ // are set (i.e., the result is nonzero), then contact info is incomplete.
+ return !(GetMissingProfileFields(profile) &
+ GetRequiredProfileFieldsForContact());
+}
+
+base::string16 PaymentsProfileComparator::GetStringForMissingContactFields(
+ const autofill::AutofillProfile& profile) const {
+ return GetStringForMissingFields(GetMissingProfileFields(&profile) &
+ GetRequiredProfileFieldsForContact());
+}
+
+std::vector<autofill::AutofillProfile*>
+PaymentsProfileComparator::FilterProfilesForShipping(
+ const std::vector<autofill::AutofillProfile*>& profiles) const {
+ // Since we'll be changing the order/contents of the const input vector,
+ // we make a copy.
+ std::vector<autofill::AutofillProfile*> processed = profiles;
+
+ std::stable_sort(
+ processed.begin(), processed.end(),
+ std::bind(&PaymentsProfileComparator::IsShippingMoreComplete, this,
+ std::placeholders::_1, std::placeholders::_2));
+
+ // TODO(crbug.com/722949): Remove profiles with no relevant information, or
+ // which are subsets of more-complete profiles.
+
+ return processed;
+}
+
+int PaymentsProfileComparator::GetShippingCompletenessScore(
+ const autofill::AutofillProfile* profile) const {
+ // Create a bitmask of the fields that are both present and required.
+ ProfileFields present =
+ ~GetMissingProfileFields(profile) & GetRequiredProfileFieldsForShipping();
+
+ // Count how many are set. The completeness of the address is weighted so as
+ // to dominate the other fields.
+ return !!(present & kName) + !!(present & kPhone) +
+ (10 * !!(present & kAddress));
+}
+
+bool PaymentsProfileComparator::IsShippingComplete(
+ const autofill::AutofillProfile* profile) const {
+ // Mask the fields that are missing with those that are requried. If any bits
+ // are set (i.e., the result is nonzero), then shipping is incomplete.
+ return !(GetMissingProfileFields(profile) &
+ GetRequiredProfileFieldsForShipping());
+}
+
+base::string16 PaymentsProfileComparator::GetStringForMissingShippingFields(
+ const autofill::AutofillProfile& profile) const {
+ return GetStringForMissingFields(GetMissingProfileFields(&profile) &
+ GetRequiredProfileFieldsForShipping());
+}
+
+void PaymentsProfileComparator::Invalidate(
+ const autofill::AutofillProfile& profile) {
+ cache_.erase(profile.guid());
+}
+
+PaymentsProfileComparator::ProfileFields
+PaymentsProfileComparator::ComputeMissingFields(
+ const autofill::AutofillProfile& profile) const {
+ ProfileFields missing = 0;
+
+ if (!profile.HasInfo(autofill::NAME_FULL))
+ missing |= kName;
+
+ // Determine the country code to use when validating the phone number. Use
+ // the profile's country if it has one, or the code for the app locale
+ // otherwise. Note that international format numbers will always work--this
+ // is just the region that will be used to check if the number is
+ // potentially in a local format.
+ std::string country =
+ data_util::GetCountryCodeWithFallback(&profile, app_locale());
+
+ base::string16 phone = profile.GetInfo(
+ autofill::AutofillType(autofill::PHONE_HOME_WHOLE_NUMBER), app_locale());
+ base::string16 intl_phone = base::UTF8ToUTF16("+" + base::UTF16ToUTF8(phone));
+ if (!(autofill::IsValidPhoneNumber(phone, country) ||
+ autofill::IsValidPhoneNumber(intl_phone, country)))
+ missing |= kPhone;
+
+ base::string16 email = profile.GetInfo(
+ autofill::AutofillType(autofill::EMAIL_ADDRESS), app_locale());
+ if (!autofill::IsValidEmailAddress(email))
+ missing |= kEmail;
+
+ if (!AreRequiredAddressFieldsPresent(profile))
+ missing |= kAddress;
+
+ return missing;
+}
+
+PaymentsProfileComparator::ProfileFields
+PaymentsProfileComparator::GetRequiredProfileFieldsForContact() const {
+ ProfileFields required = 0;
+ if (options_.request_payer_name())
+ required |= kName;
+ if (options_.request_payer_phone())
+ required |= kPhone;
+ if (options_.request_payer_email())
+ required |= kEmail;
+ return required;
+}
+
+PaymentsProfileComparator::ProfileFields
+PaymentsProfileComparator::GetRequiredProfileFieldsForShipping() const {
+ return options_.request_shipping() ? (kAddress | kName | kPhone) : 0;
+}
+
+base::string16 PaymentsProfileComparator::GetStringForMissingFields(
+ PaymentsProfileComparator::ProfileFields fields) const {
+ switch (fields) {
+ case 0:
+ // No bits are set, so no fields are missing.
+ return base::string16();
+ case kName:
+ return l10n_util::GetStringUTF16(IDS_PAYMENTS_NAME_REQUIRED);
+ case kPhone:
+ return l10n_util::GetStringUTF16(IDS_PAYMENTS_PHONE_NUMBER_REQUIRED);
+ case kEmail:
+ return l10n_util::GetStringUTF16(IDS_PAYMENTS_EMAIL_REQUIRED);
+ case kAddress:
+ return l10n_util::GetStringUTF16(IDS_PAYMENTS_INVALID_ADDRESS);
+ default:
+ // Either multiple bits are set (likely) or one bit that doesn't
+ // correspond to a named constant is set (shouldn't happen). Return a
+ // generic "More information" message.
+ return l10n_util::GetStringUTF16(IDS_PAYMENTS_MORE_INFORMATION_REQUIRED);
+ }
+}
+
+bool PaymentsProfileComparator::AreRequiredAddressFieldsPresent(
+ const autofill::AutofillProfile& profile) const {
+ std::unique_ptr<::i18n::addressinput::AddressData> data =
+ autofill::i18n::CreateAddressDataFromAutofillProfile(profile,
+ app_locale());
+
+ return autofill::addressinput::HasAllRequiredFields(*data);
+}
+
+bool PaymentsProfileComparator::IsContactMoreComplete(
+ const autofill::AutofillProfile* p1,
+ const autofill::AutofillProfile* p2) const {
+ return GetContactCompletenessScore(p1) > GetContactCompletenessScore(p2);
+}
+
+bool PaymentsProfileComparator::IsShippingMoreComplete(
+ const autofill::AutofillProfile* p1,
+ const autofill::AutofillProfile* p2) const {
+ return GetShippingCompletenessScore(p1) > GetShippingCompletenessScore(p2);
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/core/payments_profile_comparator.h b/chromium/components/payments/core/payments_profile_comparator.h
new file mode 100644
index 00000000000..e186e05efad
--- /dev/null
+++ b/chromium/components/payments/core/payments_profile_comparator.h
@@ -0,0 +1,124 @@
+// Copyright 2017 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.
+
+#ifndef COMPONENTS_PAYMENTS_CORE_PAYMENTS_PROFILE_COMPARATOR_H_
+#define COMPONENTS_PAYMENTS_CORE_PAYMENTS_PROFILE_COMPARATOR_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "components/autofill/core/browser/autofill_profile_comparator.h"
+
+// Utility functions used for processing and filtering address profiles
+// (AutofillProfile).
+
+namespace autofill {
+class AutofillProfile;
+} // namespace autofill
+
+namespace payments {
+
+class PaymentOptionsProvider;
+
+// Helper class which evaluates profiles for similarity and completeness.
+// Profiles are evaluated once for completeness, and the result is cached,
+// meaning one instance of this class should be used per-request to avoid
+// redoing expensive validation checks.
+// Note that, if a profile is modified and saved during the course of the
+// PaymentRequest, it is important to call the Invalidate method to ensure
+// it is properly evaluated.
+class PaymentsProfileComparator : public autofill::AutofillProfileComparator {
+ public:
+ // Bitmask of potentially-required fields used in evaluating completeness.
+ using ProfileFields = uint32_t;
+ const static ProfileFields kName = 1 << 0;
+ const static ProfileFields kPhone = 1 << 1;
+ const static ProfileFields kEmail = 1 << 2;
+ const static ProfileFields kAddress = 1 << 3;
+
+ PaymentsProfileComparator(const std::string& app_locale,
+ const PaymentOptionsProvider& options);
+ ~PaymentsProfileComparator();
+
+ // Returns a bitmask indicating which fields (or groups of fields) on this
+ // profile are not complete and valid.
+ ProfileFields GetMissingProfileFields(
+ const autofill::AutofillProfile* profile) const;
+
+ // Returns profiles for contact info, ordered by completeness and
+ // deduplicated. |profiles| should be passed in order of frecency, and this
+ // order will be preserved among equally-complete profiles. Deduplication here
+ // means that profiles returned are excluded if they are a subset of a more
+ // complete or more frecent profile. Completeness here refers only to the
+ // presence of the fields requested per the request_payer_* fields in
+ // |options|.
+ std::vector<autofill::AutofillProfile*> FilterProfilesForContact(
+ const std::vector<autofill::AutofillProfile*>& profiles) const;
+
+ // Returns true iff all of the contact info in |sub| also appears in |super|.
+ // Only operates on fields requested in |options|.
+ bool IsContactEqualOrSuperset(const autofill::AutofillProfile& super,
+ const autofill::AutofillProfile& sub) const;
+
+ // Returns the number of contact fields requested in |options| which are
+ // nonempty in |profile|.
+ int GetContactCompletenessScore(
+ const autofill::AutofillProfile* profile) const;
+
+ // Returns true iff every contact field requested in |options| is nonempty in
+ // |profile|.
+ bool IsContactInfoComplete(const autofill::AutofillProfile* profile) const;
+
+ // Returns profiles for shipping, ordered by completeness. |profiles| should
+ // be passed in order of frecency, and this order will be preserved among
+ // equally-complete profiles.
+ std::vector<autofill::AutofillProfile*> FilterProfilesForShipping(
+ const std::vector<autofill::AutofillProfile*>& profiles) const;
+
+ int GetShippingCompletenessScore(
+ const autofill::AutofillProfile* profile) const;
+
+ // Returns true iff every field needed to use |profile| as a shipping address
+ // is populated.
+ bool IsShippingComplete(const autofill::AutofillProfile* profile) const;
+
+ // Returns a localized string to be displayed in UI indicating what action,
+ // if any, must be taken for the given profile to be used as contact info.
+ base::string16 GetStringForMissingContactFields(
+ const autofill::AutofillProfile& profile) const;
+
+ // Returns a localized string to be displayed in UI indicating what action,
+ // if any, must be taken for the given profile to be used as a shipping
+ // address.
+ base::string16 GetStringForMissingShippingFields(
+ const autofill::AutofillProfile& profile) const;
+
+ // Clears the cached evaluation result for |profile|. Must be called when a
+ // profile is modified and saved during the course of a PaymentRequest.
+ void Invalidate(const autofill::AutofillProfile& profile);
+
+ private:
+ ProfileFields ComputeMissingFields(
+ const autofill::AutofillProfile& profile) const;
+ ProfileFields GetRequiredProfileFieldsForContact() const;
+ ProfileFields GetRequiredProfileFieldsForShipping() const;
+ base::string16 GetStringForMissingFields(ProfileFields fields) const;
+ bool AreRequiredAddressFieldsPresent(
+ const autofill::AutofillProfile& profile) const;
+
+ // Comparison functions suitable for sorting profiles by completeness
+ // score with std::sort.
+ bool IsContactMoreComplete(const autofill::AutofillProfile* p1,
+ const autofill::AutofillProfile* p2) const;
+ bool IsShippingMoreComplete(const autofill::AutofillProfile* p1,
+ const autofill::AutofillProfile* p2) const;
+
+ mutable std::map<std::string, ProfileFields> cache_;
+ const PaymentOptionsProvider& options_;
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CORE_PAYMENTS_PROFILE_COMPARATOR_H_ \ No newline at end of file
diff --git a/chromium/components/payments/core/payments_profile_comparator_unittest.cc b/chromium/components/payments/core/payments_profile_comparator_unittest.cc
new file mode 100644
index 00000000000..18f3e58ae15
--- /dev/null
+++ b/chromium/components/payments/core/payments_profile_comparator_unittest.cc
@@ -0,0 +1,464 @@
+// Copyright 2017 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.
+
+#include "components/payments/core/payments_profile_comparator.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/guid.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/payments/core/payment_options_provider.h"
+#include "components/strings/grit/components_strings.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/l10n/l10n_util.h"
+
+using autofill::AutofillProfile;
+
+namespace payments {
+
+constexpr uint32_t kRequestPayerName = 1 << 0;
+constexpr uint32_t kRequestPayerEmail = 1 << 1;
+constexpr uint32_t kRequestPayerPhone = 1 << 2;
+constexpr uint32_t kRequestShipping = 1 << 3;
+
+class MockPaymentOptionsProvider : public PaymentOptionsProvider {
+ public:
+ MockPaymentOptionsProvider(uint32_t options) : options_(options) {}
+
+ ~MockPaymentOptionsProvider() override {}
+ bool request_payer_name() const override {
+ return options_ & kRequestPayerName;
+ }
+ bool request_payer_email() const override {
+ return options_ & kRequestPayerEmail;
+ }
+ bool request_payer_phone() const override {
+ return options_ & kRequestPayerPhone;
+ }
+ bool request_shipping() const override { return options_ & kRequestShipping; }
+ PaymentShippingType shipping_type() const override {
+ return PaymentShippingType::SHIPPING;
+ }
+
+ private:
+ uint32_t options_;
+};
+
+AutofillProfile CreateProfileWithContactInfo(const char* name,
+ const char* email,
+ const char* phone) {
+ AutofillProfile profile(base::GenerateGUID(), "http://www.example.com/");
+ autofill::test::SetProfileInfo(&profile, name, "", "", email, "", "", "", "",
+ "", "", "", phone);
+ return profile;
+}
+
+AutofillProfile CreateProfileWithCompleteAddress(const char* name,
+ const char* phone) {
+ AutofillProfile profile(base::GenerateGUID(), "http://www.example.com/");
+ autofill::test::SetProfileInfo(&profile, name, "", "", "", "", "123 Fake St.",
+ "", "Fakesville", "MN", "54000", "US", phone);
+ return profile;
+}
+
+AutofillProfile CreateProfileWithPartialAddress(const char* name,
+ const char* phone) {
+ AutofillProfile profile(base::GenerateGUID(), "http://www.example.com/");
+ autofill::test::SetProfileInfo(&profile, name, "", "", "", "", "123 Fake St.",
+ "", "", "", "54000", "", phone);
+ return profile;
+}
+
+TEST(PaymentRequestProfileUtilTest, FilterProfilesForContact) {
+ // These profiles are subset/equal, so only the first complete one is
+ // included.
+ AutofillProfile exclude_1 =
+ CreateProfileWithContactInfo("Homer", "", "6515553226");
+
+ AutofillProfile exclude_2 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
+
+ AutofillProfile include_1 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "6515553226");
+
+ AutofillProfile exclude_3 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "6515553226");
+
+ // This profile is different, so it should also be included. Since it is
+ // less complete than |include_1|, it will appear after.
+ AutofillProfile include_2 =
+ CreateProfileWithContactInfo("Marge", "marge@simpson.net", "");
+
+ // This profile is different, so it should also be included. Since it is
+ // equally complete with |include_1|, it will appear before |include_2|, but
+ // after |include_1| since order is preserved amongst profiles of equal
+ // completeness.
+ AutofillProfile include_3 = CreateProfileWithContactInfo(
+ "Bart", "eatmyshorts@simpson.net", "6515553226");
+
+ std::vector<AutofillProfile*> profiles = {&exclude_1, &exclude_2, &include_1,
+ &exclude_3, &include_2, &include_3};
+
+ MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerEmail |
+ kRequestPayerPhone);
+ PaymentsProfileComparator comp("en-US", provider);
+
+ std::vector<AutofillProfile*> filtered =
+ comp.FilterProfilesForContact(profiles);
+
+ ASSERT_EQ(3u, filtered.size());
+ EXPECT_EQ(&include_1, filtered[0]);
+ EXPECT_EQ(&include_3, filtered[1]);
+ EXPECT_EQ(&include_2, filtered[2]);
+
+ // Repeat the filter using a provider set to only request phone numbers.
+ // Under these rules, since all profiles have the same (or no) phone number,
+ // we should only see the first profile with a phone number, |exclude_1|.
+ MockPaymentOptionsProvider phone_only_provider(kRequestPayerPhone);
+ PaymentsProfileComparator phone_only_comp("en-US", phone_only_provider);
+ std::vector<AutofillProfile*> filtered_phones =
+ phone_only_comp.FilterProfilesForContact(profiles);
+ ASSERT_EQ(1u, filtered_phones.size());
+ EXPECT_EQ(&exclude_1, filtered_phones[0]);
+}
+
+TEST(PaymentRequestProfileUtilTest, IsContactEqualOrSuperset) {
+ MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerEmail |
+ kRequestPayerPhone);
+ PaymentsProfileComparator comp("en-US", provider);
+
+ AutofillProfile p1 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "6515553226");
+
+ // Candidate subset profile is equal.
+ AutofillProfile p2 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "6515553226");
+ EXPECT_TRUE(comp.IsContactEqualOrSuperset(p1, p2));
+ EXPECT_TRUE(comp.IsContactEqualOrSuperset(p2, p1));
+
+ // Candidate subset profile has non-matching fields.
+ AutofillProfile p3 = CreateProfileWithContactInfo(
+ "Homer", "homer@springfieldnuclear.gov", "6515553226");
+ EXPECT_FALSE(comp.IsContactEqualOrSuperset(p1, p3));
+ EXPECT_FALSE(comp.IsContactEqualOrSuperset(p3, p1));
+
+ // Candidate subset profile is equal, except for missing fields.
+ AutofillProfile p4 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
+ EXPECT_TRUE(comp.IsContactEqualOrSuperset(p1, p4));
+ EXPECT_FALSE(comp.IsContactEqualOrSuperset(p4, p1));
+
+ // One field is common, but each has a field which the other is missing.
+ AutofillProfile p5 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
+ AutofillProfile p6 = CreateProfileWithContactInfo("Homer", "", "6515553226");
+ EXPECT_FALSE(comp.IsContactEqualOrSuperset(p5, p6));
+ EXPECT_FALSE(comp.IsContactEqualOrSuperset(p6, p5));
+}
+
+TEST(PaymentRequestProfileUtilTest, IsContactEqualOrSuperset_WithFieldIgnored) {
+ // Discrepancies in email should be ignored throughout this test.
+ MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerPhone);
+ PaymentsProfileComparator comp("en-US", provider);
+
+ AutofillProfile p1 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "6515553226");
+
+ // Candidate subset profile is equal.
+ AutofillProfile p2 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "6515553226");
+ EXPECT_TRUE(comp.IsContactEqualOrSuperset(p1, p2));
+ EXPECT_TRUE(comp.IsContactEqualOrSuperset(p2, p1));
+
+ // Email fields don't match, but profiles are still equal.
+ AutofillProfile p3 = CreateProfileWithContactInfo(
+ "Homer", "homer@springfieldnuclear.gov", "6515553226");
+ EXPECT_TRUE(comp.IsContactEqualOrSuperset(p1, p3));
+ EXPECT_TRUE(comp.IsContactEqualOrSuperset(p3, p1));
+
+ // Profile without an email is mutual subset of profile with an email.
+ AutofillProfile p4 = CreateProfileWithContactInfo("Homer", "", "6515553226");
+ EXPECT_TRUE(comp.IsContactEqualOrSuperset(p1, p4));
+ EXPECT_TRUE(comp.IsContactEqualOrSuperset(p4, p1));
+}
+
+TEST(PaymentRequestProfileUtilTest, GetContactCompletenessScore) {
+ MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerPhone);
+ PaymentsProfileComparator comp("en-US", provider);
+
+ // Two completeness points: One each for name and phone number, but not email
+ // as it was not requested.
+ AutofillProfile p1 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "6515553226");
+ EXPECT_EQ(2, comp.GetContactCompletenessScore(&p1));
+
+ // One completeness point for name, no points for phone number (missing) or
+ // email (not requested).
+ AutofillProfile p2 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
+ EXPECT_EQ(1, comp.GetContactCompletenessScore(&p2));
+
+ // No completeness points, as the only field present was not requested.
+ AutofillProfile p3 =
+ CreateProfileWithContactInfo("", "homer@simpson.net", "");
+ EXPECT_EQ(0, comp.GetContactCompletenessScore(&p3));
+
+ // Null profile returns 0.
+ EXPECT_EQ(0, comp.GetContactCompletenessScore(nullptr));
+}
+
+TEST(PaymentRequestProfileUtilTest, IsContactInfoComplete) {
+ MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerEmail);
+ PaymentsProfileComparator comp("en-US", provider);
+
+ // If name and email are present, return true regardless of the (ignored)
+ // phone value.
+ AutofillProfile p1 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "6515553226");
+ AutofillProfile p2 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
+
+ EXPECT_TRUE(comp.IsContactInfoComplete(&p1));
+ EXPECT_TRUE(comp.IsContactInfoComplete(&p2));
+
+ // If name is not present, return false regardless of the (ignored)
+ // phone value.
+ AutofillProfile p3 =
+ CreateProfileWithContactInfo("", "homer@simpson.net", "6515553226");
+ AutofillProfile p4 =
+ CreateProfileWithContactInfo("", "homer@simpson.net", "");
+
+ EXPECT_FALSE(comp.IsContactInfoComplete(&p3));
+ EXPECT_FALSE(comp.IsContactInfoComplete(&p4));
+
+ // If no fields are requested, any profile (even empty or null) is complete.
+ MockPaymentOptionsProvider empty_provider(0);
+ PaymentsProfileComparator empty_comp("en-US", empty_provider);
+
+ AutofillProfile p5 = CreateProfileWithContactInfo("", "", "");
+
+ EXPECT_TRUE(empty_comp.IsContactInfoComplete(&p1));
+ EXPECT_TRUE(empty_comp.IsContactInfoComplete(&p5));
+ EXPECT_TRUE(empty_comp.IsContactInfoComplete(nullptr));
+}
+
+TEST(PaymentRequestProfileUtilTest, FilterProfilesForShipping) {
+ MockPaymentOptionsProvider provider(kRequestShipping);
+ PaymentsProfileComparator comp("en-US", provider);
+
+ AutofillProfile address_only = CreateProfileWithCompleteAddress("", "");
+
+ AutofillProfile no_name = CreateProfileWithCompleteAddress("", "6515553226");
+ AutofillProfile no_phone = CreateProfileWithCompleteAddress("Homer", "");
+
+ AutofillProfile empty = CreateProfileWithContactInfo("", "", "");
+
+ AutofillProfile complete1 =
+ CreateProfileWithCompleteAddress("Homer", "6515553226");
+
+ AutofillProfile partial_address =
+ CreateProfileWithPartialAddress("Homer", "6515553226");
+ AutofillProfile no_address =
+ CreateProfileWithContactInfo("Homer", "", "6515553226");
+
+ AutofillProfile complete2 =
+ CreateProfileWithCompleteAddress("Bart", "6515553226");
+
+ AutofillProfile partial_no_phone =
+ CreateProfileWithPartialAddress("", "6515553226");
+ AutofillProfile partial_no_name =
+ CreateProfileWithPartialAddress("Homer", "");
+
+ std::vector<AutofillProfile*> profiles = {
+ &address_only, &no_name, &no_phone, &empty,
+ &complete1, &partial_address, &no_address, &complete2,
+ &partial_no_phone, &partial_no_name};
+
+ std::vector<AutofillProfile*> filtered =
+ comp.FilterProfilesForShipping(profiles);
+
+ // Current logic does not remove profiles, only reorder them.
+ ASSERT_EQ(10u, filtered.size());
+
+ // First, the complete profiles should be hoisted to the top, keeping their
+ // relative order.
+ EXPECT_EQ(&complete1, filtered[0]);
+ EXPECT_EQ(&complete2, filtered[1]);
+
+ // Next are profiles with a complete address but missing one other field.
+ EXPECT_EQ(&no_name, filtered[2]);
+ EXPECT_EQ(&no_phone, filtered[3]);
+
+ // A profile with only a complete address should still appear before profiles
+ // with partial/empty addresses.
+ EXPECT_EQ(&address_only, filtered[4]);
+
+ // Profiles with partial/no address then are sorted by whether or not they
+ // have names and/or phone numbers.
+ EXPECT_EQ(&partial_address, filtered[5]);
+ EXPECT_EQ(&no_address, filtered[6]);
+
+ EXPECT_EQ(&partial_no_phone, filtered[7]);
+ EXPECT_EQ(&partial_no_name, filtered[8]);
+
+ EXPECT_EQ(&empty, filtered[9]);
+}
+
+TEST(PaymentRequestProfileUtilTest, GetShippingCompletenessScore) {
+ MockPaymentOptionsProvider provider(kRequestShipping);
+ PaymentsProfileComparator comp("en-US", provider);
+
+ // 12 points for a complete profile: 10 for address, 1 each for name/phone.
+ AutofillProfile p1 = CreateProfileWithCompleteAddress("Homer", "6515553226");
+ EXPECT_EQ(12, comp.GetShippingCompletenessScore(&p1));
+
+ // 11 points if name or phone is missing.
+ AutofillProfile p2 = CreateProfileWithCompleteAddress("", "6515553226");
+ AutofillProfile p3 = CreateProfileWithCompleteAddress("Homer", "");
+ EXPECT_EQ(11, comp.GetShippingCompletenessScore(&p2));
+ EXPECT_EQ(11, comp.GetShippingCompletenessScore(&p3));
+
+ // 10 points for complete address only.
+ AutofillProfile p4 = CreateProfileWithCompleteAddress("", "");
+ EXPECT_EQ(10, comp.GetShippingCompletenessScore(&p4));
+
+ // 2 points for name and phone without address.
+ AutofillProfile p5 = CreateProfileWithContactInfo("Homer", "", "6515553226");
+ EXPECT_EQ(2, comp.GetShippingCompletenessScore(&p5));
+
+ // 1 point for name or phone alone.
+ AutofillProfile p6 = CreateProfileWithContactInfo("Homer", "", "");
+ AutofillProfile p7 = CreateProfileWithContactInfo("", "", "6515553226");
+ EXPECT_EQ(1, comp.GetShippingCompletenessScore(&p6));
+ EXPECT_EQ(1, comp.GetShippingCompletenessScore(&p7));
+
+ // No points for empty profile, or profile with only a partial address.
+ AutofillProfile p8 = CreateProfileWithContactInfo("", "", "");
+ AutofillProfile p9 = CreateProfileWithPartialAddress("", "");
+ EXPECT_EQ(0, comp.GetShippingCompletenessScore(&p8));
+ EXPECT_EQ(0, comp.GetShippingCompletenessScore(&p9));
+}
+
+TEST(PaymentRequestProfileUtilTest, IsShippingComplete) {
+ MockPaymentOptionsProvider provider(kRequestShipping);
+ PaymentsProfileComparator comp("en-US", provider);
+
+ // True if name, phone, and address are all populated.
+ AutofillProfile p1 = CreateProfileWithCompleteAddress("Homer", "6515553226");
+ EXPECT_TRUE(comp.IsShippingComplete(&p1));
+
+ // False if address is partially populated.
+ AutofillProfile p2 = CreateProfileWithPartialAddress("Homer", "6515553226");
+ EXPECT_FALSE(comp.IsShippingComplete(&p2));
+
+ // False if name isn't populated.
+ AutofillProfile p3 = CreateProfileWithCompleteAddress("", "6515553226");
+ EXPECT_FALSE(comp.IsShippingComplete(&p3));
+
+ // False if phone isn't populated.
+ AutofillProfile p4 = CreateProfileWithCompleteAddress("Homer", "");
+ EXPECT_FALSE(comp.IsShippingComplete(&p4));
+
+ // False if only contact info (no address fields) is populated.
+ AutofillProfile p5 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "6515553226");
+ EXPECT_FALSE(comp.IsShippingComplete(&p5));
+
+ MockPaymentOptionsProvider provider_no_shipping(0);
+ PaymentsProfileComparator comp_no_shipping("en-US", provider_no_shipping);
+ // nullptr is handled correctly: false if shipping requested, true if not.
+ EXPECT_FALSE(comp.IsShippingComplete(nullptr));
+ EXPECT_TRUE(comp_no_shipping.IsShippingComplete(nullptr));
+}
+
+TEST(PaymentRequestProfileUtilTest, GetStringForMissingContactFields) {
+ MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerPhone |
+ kRequestPayerEmail | kRequestShipping);
+ PaymentsProfileComparator comp("en-US", provider);
+
+ // No error message for complete profile.
+ AutofillProfile p1 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "6515553226");
+ EXPECT_TRUE(comp.GetStringForMissingContactFields(p1).empty());
+
+ MockPaymentOptionsProvider provider_no_email(
+ kRequestPayerName | kRequestPayerPhone | kRequestShipping);
+ PaymentsProfileComparator comp_no_email("en-US", provider_no_email);
+
+ // No error message if missing field wasn't required.
+ AutofillProfile p2 = CreateProfileWithContactInfo("Homer", "", "6515553226");
+ EXPECT_TRUE(comp_no_email.GetStringForMissingContactFields(p2).empty());
+
+ // Error message for email address if email address is missing and required.
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_EMAIL_REQUIRED),
+ comp.GetStringForMissingContactFields(p2));
+
+ // Error message for phone number if phone is missing and required.
+ AutofillProfile p3 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_PHONE_NUMBER_REQUIRED),
+ comp.GetStringForMissingContactFields(p3));
+
+ // Error message for name if name is missing and required.
+ AutofillProfile p4 =
+ CreateProfileWithContactInfo("", "homer@simpson.net", "6515553226");
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_NAME_REQUIRED),
+ comp.GetStringForMissingContactFields(p4));
+
+ // Generic error message if multiple fields missing.
+ AutofillProfile p5 =
+ CreateProfileWithContactInfo("", "homer@simpson.net", "");
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_MORE_INFORMATION_REQUIRED),
+ comp.GetStringForMissingContactFields(p5));
+}
+
+TEST(PaymentRequestProfileUtilTest, GetStringForMissingShippingFields) {
+ MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerPhone |
+ kRequestPayerEmail | kRequestShipping);
+ PaymentsProfileComparator comp("en-US", provider);
+
+ // No error message for complete profile.
+ AutofillProfile p1 = CreateProfileWithCompleteAddress("Homer", "6515553226");
+ EXPECT_TRUE(comp.GetStringForMissingShippingFields(p1).empty());
+
+ // Error message for shipping if shipping requested and not present.
+ AutofillProfile p2 =
+ CreateProfileWithContactInfo("Homer", "homer@simpson.net", "6515553226");
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_INVALID_ADDRESS),
+ comp.GetStringForMissingShippingFields(p2));
+
+ // Error message for shipping if shipping requested and only partially
+ // complete.
+ AutofillProfile p3 = CreateProfileWithPartialAddress("Homer", "6515553226");
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_INVALID_ADDRESS),
+ comp.GetStringForMissingShippingFields(p3));
+
+ // Error message for name if name requested and missing.
+ AutofillProfile p4 = CreateProfileWithCompleteAddress("", "6515553226");
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_NAME_REQUIRED),
+ comp.GetStringForMissingShippingFields(p4));
+
+ // Error message for phone if phone requested and missing.
+ AutofillProfile p5 = CreateProfileWithCompleteAddress("Homer", "");
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_PHONE_NUMBER_REQUIRED),
+ comp.GetStringForMissingShippingFields(p5));
+
+ // Generic error message if multiple fields missing.
+ AutofillProfile p6 = CreateProfileWithContactInfo("", "", "");
+ EXPECT_EQ(l10n_util::GetStringUTF16(IDS_PAYMENTS_MORE_INFORMATION_REQUIRED),
+ comp.GetStringForMissingShippingFields(p6));
+
+ MockPaymentOptionsProvider provider_no_shipping(
+ kRequestPayerName | kRequestPayerPhone | kRequestPayerEmail);
+ PaymentsProfileComparator comp_no_shipping("en-US", provider_no_shipping);
+
+ // No error message if everything is missing but shipping wasn't requested.
+ EXPECT_TRUE(comp_no_shipping.GetStringForMissingShippingFields(p6).empty());
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/core/profile_util.cc b/chromium/components/payments/core/profile_util.cc
deleted file mode 100644
index c1149c85ed8..00000000000
--- a/chromium/components/payments/core/profile_util.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2017 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.
-
-#include "components/payments/core/profile_util.h"
-
-#include <algorithm>
-
-#include "components/autofill/core/browser/autofill_profile.h"
-#include "components/autofill/core/browser/field_types.h"
-#include "components/payments/core/payment_options_provider.h"
-
-namespace payments {
-namespace profile_util {
-
-std::vector<autofill::AutofillProfile*> FilterProfilesForContact(
- const std::vector<autofill::AutofillProfile*>& profiles,
- const std::string& app_locale,
- const PaymentOptionsProvider& options) {
- // We will be removing profiles, so we operate on a copy.
- std::vector<autofill::AutofillProfile*> processed = profiles;
-
- PaymentsProfileComparator comparator(app_locale, options);
-
- // Stable sort, since profiles are expected to be passed in frecency order.
- std::stable_sort(
- processed.begin(), processed.end(),
- std::bind(&PaymentsProfileComparator::IsContactMoreComplete, &comparator,
- std::placeholders::_1, std::placeholders::_2));
-
- auto it = processed.begin();
- while (it != processed.end()) {
- if (comparator.GetContactCompletenessScore(*it) == 0) {
- // Since profiles are sorted by completeness, this and any further
- // profiles can be discarded.
- processed.erase(it, processed.end());
- break;
- }
-
- // Attempt to find a matching element in the vector before the current.
- // This is quadratic, but the number of elements is generally small
- // (< 10), so a more complicated algorithm would be overkill.
- if (std::find_if(processed.begin(), it,
- [&](autofill::AutofillProfile* prior) {
- return comparator.IsContactEqualOrSuperset(*prior, **it);
- }) != it) {
- // Remove the subset profile. |it| will point to the next element after
- // erasure.
- it = processed.erase(it);
- } else {
- it++;
- }
- }
-
- return processed;
-}
-
-PaymentsProfileComparator::PaymentsProfileComparator(
- const std::string& app_locale,
- const PaymentOptionsProvider& options)
- : autofill::AutofillProfileComparator(app_locale), options_(options) {}
-
-PaymentsProfileComparator::~PaymentsProfileComparator() {}
-
-bool PaymentsProfileComparator::IsContactEqualOrSuperset(
- const autofill::AutofillProfile& super,
- const autofill::AutofillProfile& sub) {
- if (options_.request_payer_name()) {
- if (sub.HasInfo(autofill::NAME_FULL) &&
- !super.HasInfo(autofill::NAME_FULL)) {
- return false;
- }
- if (!HaveMergeableNames(super, sub))
- return false;
- }
- if (options_.request_payer_phone()) {
- if (sub.HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER) &&
- !super.HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER)) {
- return false;
- }
- if (!HaveMergeablePhoneNumbers(super, sub))
- return false;
- }
- if (options_.request_payer_email()) {
- if (sub.HasInfo(autofill::EMAIL_ADDRESS) &&
- !super.HasInfo(autofill::EMAIL_ADDRESS)) {
- return false;
- }
- if (!HaveMergeableEmailAddresses(super, sub))
- return false;
- }
- return true;
-}
-
-int PaymentsProfileComparator::GetContactCompletenessScore(
- const autofill::AutofillProfile* profile) {
- if (!profile)
- return 0;
-
- return (options_.request_payer_name() &&
- profile->HasInfo(autofill::NAME_FULL)) +
- (options_.request_payer_phone() &&
- profile->HasInfo(autofill::PHONE_HOME_WHOLE_NUMBER)) +
- (options_.request_payer_email() &&
- profile->HasInfo(autofill::EMAIL_ADDRESS));
-}
-
-bool PaymentsProfileComparator::IsContactInfoComplete(
- const autofill::AutofillProfile* profile) {
- int desired_score = options_.request_payer_name() +
- options_.request_payer_phone() +
- options_.request_payer_email();
- return GetContactCompletenessScore(profile) == desired_score;
-}
-
-bool PaymentsProfileComparator::IsContactMoreComplete(
- const autofill::AutofillProfile* p1,
- const autofill::AutofillProfile* p2) {
- return GetContactCompletenessScore(p1) > GetContactCompletenessScore(p2);
-}
-
-} // namespace profile_util
-} // namespace payments
diff --git a/chromium/components/payments/core/profile_util.h b/chromium/components/payments/core/profile_util.h
deleted file mode 100644
index 6693fe854c8..00000000000
--- a/chromium/components/payments/core/profile_util.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2017 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.
-
-#ifndef COMPONENTS_PAYMENTS_CONTENT_PROFILE_UTIL_H_
-#define COMPONENTS_PAYMENTS_CONTENT_PROFILE_UTIL_H_
-
-#include <string>
-#include <vector>
-
-#include "components/autofill/core/browser/autofill_profile_comparator.h"
-
-// Utility functions used for processing and filtering address profiles
-// (AutofillProfile).
-
-namespace autofill {
-class AutofillProfile;
-} // namespace autofill
-
-namespace payments {
-
-class PaymentOptionsProvider;
-
-namespace profile_util {
-
-// Returns profiles for contact info, ordered by completeness and deduplicated.
-// |profiles| should be passed in order of frecency, and this order will be
-// preserved among equally-complete profiles. Deduplication here means that
-// profiles returned are excluded if they are a subset of a more complete or
-// more frecent profile. Completeness here refers only to the presence of the
-// fields requested per the request_payer_* fields in |options|.
-std::vector<autofill::AutofillProfile*> FilterProfilesForContact(
- const std::vector<autofill::AutofillProfile*>& profiles,
- const std::string& app_locale,
- const PaymentOptionsProvider& options);
-
-// Helper class which evaluates profiles for similarity and completeness.
-class PaymentsProfileComparator : public autofill::AutofillProfileComparator {
- public:
- PaymentsProfileComparator(const std::string& app_locale,
- const PaymentOptionsProvider& options);
- ~PaymentsProfileComparator();
-
- // Returns true iff all of the contact info in |sub| also appears in |super|.
- // Only operates on fields requested in |options|.
- bool IsContactEqualOrSuperset(const autofill::AutofillProfile& super,
- const autofill::AutofillProfile& sub);
-
- // Returns the number of contact fields requested in |options| which are
- // nonempty in |profile|.
- int GetContactCompletenessScore(const autofill::AutofillProfile* profile);
-
- // Returns true iff every contact field requested in |options| is nonempty in
- // |profile|.
- bool IsContactInfoComplete(const autofill::AutofillProfile* profile);
-
- // Comparison function suitable for sorting profiles by contact completeness
- // score with std::sort.
- bool IsContactMoreComplete(const autofill::AutofillProfile* p1,
- const autofill::AutofillProfile* p2);
-
- private:
- const PaymentOptionsProvider& options_;
-};
-
-} // namespace profile_util
-} // namespace payments
-
-#endif // COMPONENTS_PAYMENTS_CONTENT_PROFILE_UTIL_H_ \ No newline at end of file
diff --git a/chromium/components/payments/core/profile_util_unittest.cc b/chromium/components/payments/core/profile_util_unittest.cc
deleted file mode 100644
index 5ff1d7ec4e3..00000000000
--- a/chromium/components/payments/core/profile_util_unittest.cc
+++ /dev/null
@@ -1,231 +0,0 @@
-// Copyright 2017 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.
-
-#include "components/payments/core/profile_util.h"
-
-#include <memory>
-#include <vector>
-
-#include "base/guid.h"
-#include "base/memory/ptr_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "components/autofill/core/browser/autofill_profile.h"
-#include "components/autofill/core/browser/autofill_test_utils.h"
-#include "components/payments/core/payment_options_provider.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using autofill::AutofillProfile;
-
-namespace payments {
-namespace profile_util {
-
-constexpr uint32_t kRequestPayerName = 1 << 0;
-constexpr uint32_t kRequestPayerEmail = 1 << 1;
-constexpr uint32_t kRequestPayerPhone = 1 << 2;
-constexpr uint32_t kRequestShipping = 1 << 3;
-
-class MockPaymentOptionsProvider : public PaymentOptionsProvider {
- public:
- MockPaymentOptionsProvider(uint32_t options) : options_(options) {}
-
- ~MockPaymentOptionsProvider() override {}
- bool request_payer_name() const override {
- return options_ & kRequestPayerName;
- }
- bool request_payer_email() const override {
- return options_ & kRequestPayerEmail;
- }
- bool request_payer_phone() const override {
- return options_ & kRequestPayerPhone;
- }
- bool request_shipping() const override { return options_ & kRequestShipping; }
- PaymentShippingType shipping_type() const override {
- return PaymentShippingType::SHIPPING;
- }
-
- private:
- uint32_t options_;
-};
-
-AutofillProfile CreateProfileWithContactInfo(const char* name,
- const char* email,
- const char* phone) {
- AutofillProfile profile(base::GenerateGUID(), "http://www.example.com/");
- autofill::test::SetProfileInfo(&profile, name, "", "", email, "", "", "", "",
- "", "", "", phone);
- return profile;
-}
-
-TEST(PaymentRequestProfileUtilTest, FilterProfilesForContact) {
- // These profiles are subset/equal, so only the first complete one is
- // included.
- AutofillProfile exclude_1 =
- CreateProfileWithContactInfo("Homer", "", "5551234567");
-
- AutofillProfile exclude_2 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
-
- AutofillProfile include_1 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "5551234567");
-
- AutofillProfile exclude_3 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "5551234567");
-
- // This profile is different, so it should also be included. Since it is
- // less complete than |include_1|, it will appear after.
- AutofillProfile include_2 =
- CreateProfileWithContactInfo("Marge", "marge@simpson.net", "");
-
- // This profile is different, so it should also be included. Since it is
- // equally complete with |include_1|, it will appear before |include_2|, but
- // after |include_1| since order is preserved amongst profiles of equal
- // completeness.
- AutofillProfile include_3 = CreateProfileWithContactInfo(
- "Bart", "eatmyshorts@simpson.net", "5551234567");
-
- std::vector<AutofillProfile*> profiles = {&exclude_1, &exclude_2, &include_1,
- &exclude_3, &include_2, &include_3};
-
- MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerEmail |
- kRequestPayerPhone);
- std::vector<AutofillProfile*> filtered =
- FilterProfilesForContact(profiles, "en-US", provider);
-
- ASSERT_EQ(3u, filtered.size());
- EXPECT_EQ(&include_1, filtered[0]);
- EXPECT_EQ(&include_3, filtered[1]);
- EXPECT_EQ(&include_2, filtered[2]);
-
- // Repeat the filter using a provider set to only request phone numbers.
- // Under these rules, since all profiles have the same (or no) phone number,
- // we should only see the first profile with a phone number, |exclude_1|.
- MockPaymentOptionsProvider phone_only_provider(kRequestPayerPhone);
- std::vector<AutofillProfile*> filtered_phones =
- FilterProfilesForContact(profiles, "en-US", phone_only_provider);
- ASSERT_EQ(1u, filtered_phones.size());
- EXPECT_EQ(&exclude_1, filtered_phones[0]);
-}
-
-TEST(PaymentRequestProfileUtilTest, IsContactEqualOrSuperset) {
- MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerEmail |
- kRequestPayerPhone);
- PaymentsProfileComparator comp("en-US", provider);
-
- AutofillProfile p1 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "5551234567");
-
- // Candidate subset profile is equal.
- AutofillProfile p2 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "5551234567");
- EXPECT_TRUE(comp.IsContactEqualOrSuperset(p1, p2));
- EXPECT_TRUE(comp.IsContactEqualOrSuperset(p2, p1));
-
- // Candidate subset profile has non-matching fields.
- AutofillProfile p3 = CreateProfileWithContactInfo(
- "Homer", "homer@springfieldnuclear.gov", "5551234567");
- EXPECT_FALSE(comp.IsContactEqualOrSuperset(p1, p3));
- EXPECT_FALSE(comp.IsContactEqualOrSuperset(p3, p1));
-
- // Candidate subset profile is equal, except for missing fields.
- AutofillProfile p4 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
- EXPECT_TRUE(comp.IsContactEqualOrSuperset(p1, p4));
- EXPECT_FALSE(comp.IsContactEqualOrSuperset(p4, p1));
-
- // One field is common, but each has a field which the other is missing.
- AutofillProfile p5 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
- AutofillProfile p6 = CreateProfileWithContactInfo("Homer", "", "5551234567");
- EXPECT_FALSE(comp.IsContactEqualOrSuperset(p5, p6));
- EXPECT_FALSE(comp.IsContactEqualOrSuperset(p6, p5));
-}
-
-TEST(PaymentRequestProfileUtilTest, IsContactEqualOrSuperset_WithFieldIgnored) {
- // Discrepancies in email should be ignored throughout this test.
- MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerPhone);
- PaymentsProfileComparator comp("en-US", provider);
-
- AutofillProfile p1 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "5551234567");
-
- // Candidate subset profile is equal.
- AutofillProfile p2 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "5551234567");
- EXPECT_TRUE(comp.IsContactEqualOrSuperset(p1, p2));
- EXPECT_TRUE(comp.IsContactEqualOrSuperset(p2, p1));
-
- // Email fields don't match, but profiles are still equal.
- AutofillProfile p3 = CreateProfileWithContactInfo(
- "Homer", "homer@springfieldnuclear.gov", "5551234567");
- EXPECT_TRUE(comp.IsContactEqualOrSuperset(p1, p3));
- EXPECT_TRUE(comp.IsContactEqualOrSuperset(p3, p1));
-
- // Profile without an email is mutual subset of profile with an email.
- AutofillProfile p4 = CreateProfileWithContactInfo("Homer", "", "5551234567");
- EXPECT_TRUE(comp.IsContactEqualOrSuperset(p1, p4));
- EXPECT_TRUE(comp.IsContactEqualOrSuperset(p4, p1));
-}
-
-TEST(PaymentRequestProfileUtilTest, GetContactCompletenessScore) {
- MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerPhone);
- PaymentsProfileComparator comp("en-US", provider);
-
- // Two completeness points: One each for name and phone number, but not email
- // as it was not requested.
- AutofillProfile p1 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "5551234567");
- EXPECT_EQ(2, comp.GetContactCompletenessScore(&p1));
-
- // One completeness point for name, no points for phone number (missing) or
- // email (not requested).
- AutofillProfile p2 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
- EXPECT_EQ(1, comp.GetContactCompletenessScore(&p2));
-
- // No completeness points, as the only field present was not requested.
- AutofillProfile p3 =
- CreateProfileWithContactInfo("", "homer@simpson.net", "");
- EXPECT_EQ(0, comp.GetContactCompletenessScore(&p3));
-
- // Null profile returns 0.
- EXPECT_EQ(0, comp.GetContactCompletenessScore(nullptr));
-}
-
-TEST(PaymentRequestProfileUtilTest, IsContactInfoComplete) {
- MockPaymentOptionsProvider provider(kRequestPayerName | kRequestPayerEmail);
- PaymentsProfileComparator comp("en-US", provider);
-
- // If name and email are present, return true regardless of the (ignored)
- // phone value.
- AutofillProfile p1 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "5551234567");
- AutofillProfile p2 =
- CreateProfileWithContactInfo("Homer", "homer@simpson.net", "");
-
- EXPECT_TRUE(comp.IsContactInfoComplete(&p1));
- EXPECT_TRUE(comp.IsContactInfoComplete(&p2));
-
- // If name is not present, return false regardless of the (ignored)
- // phone value.
- AutofillProfile p3 =
- CreateProfileWithContactInfo("", "homer@simpson.net", "5551234567");
- AutofillProfile p4 =
- CreateProfileWithContactInfo("", "homer@simpson.net", "");
-
- EXPECT_FALSE(comp.IsContactInfoComplete(&p3));
- EXPECT_FALSE(comp.IsContactInfoComplete(&p4));
-
- // If no fields are requested, any profile (even empty or null) is complete.
- MockPaymentOptionsProvider empty_provider(0);
- PaymentsProfileComparator empty_comp("en-US", empty_provider);
-
- AutofillProfile p5 = CreateProfileWithContactInfo("", "", "");
-
- EXPECT_TRUE(empty_comp.IsContactInfoComplete(&p1));
- EXPECT_TRUE(empty_comp.IsContactInfoComplete(&p5));
- EXPECT_TRUE(empty_comp.IsContactInfoComplete(nullptr));
-}
-
-} // namespace profile_util
-} // namespace payments
diff --git a/chromium/components/payments/core/strings_util.cc b/chromium/components/payments/core/strings_util.cc
index 8c405e5ba83..165d2b225b7 100644
--- a/chromium/components/payments/core/strings_util.cc
+++ b/chromium/components/payments/core/strings_util.cc
@@ -4,12 +4,53 @@
#include "components/payments/core/strings_util.h"
+#include <vector>
+
#include "base/logging.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/field_types.h"
#include "components/strings/grit/components_strings.h"
#include "ui/base/l10n/l10n_util.h"
namespace payments {
+base::string16 GetShippingAddressLabelFormAutofillProfile(
+ const autofill::AutofillProfile& profile,
+ const std::string& locale) {
+ // Name, phone number, and country are not included in the shipping address
+ // label.
+ static const std::vector<autofill::ServerFieldType> label_fields{
+ autofill::COMPANY_NAME,
+ autofill::ADDRESS_HOME_STREET_ADDRESS,
+ autofill::ADDRESS_HOME_DEPENDENT_LOCALITY,
+ autofill::ADDRESS_HOME_CITY,
+ autofill::ADDRESS_HOME_STATE,
+ autofill::ADDRESS_HOME_ZIP,
+ autofill::ADDRESS_HOME_SORTING_CODE,
+ };
+
+ return profile.ConstructInferredLabel(label_fields, label_fields.size(),
+ locale);
+}
+
+base::string16 GetBillingAddressLabelFromAutofillProfile(
+ const autofill::AutofillProfile& profile,
+ const std::string& locale) {
+ // Name, company, phone number, and country are not included in the billing
+ // address label.
+ static const std::vector<autofill::ServerFieldType> label_fields{
+ autofill::ADDRESS_HOME_STREET_ADDRESS,
+ autofill::ADDRESS_HOME_DEPENDENT_LOCALITY,
+ autofill::ADDRESS_HOME_CITY,
+ autofill::ADDRESS_HOME_STATE,
+ autofill::ADDRESS_HOME_ZIP,
+ autofill::ADDRESS_HOME_SORTING_CODE,
+ };
+
+ return profile.ConstructInferredLabel(label_fields, label_fields.size(),
+ locale);
+}
+
base::string16 GetShippingAddressSelectorInfoMessage(
PaymentShippingType shipping_type) {
switch (shipping_type) {
diff --git a/chromium/components/payments/core/strings_util.h b/chromium/components/payments/core/strings_util.h
index 3d72567c44c..c693d29fe6d 100644
--- a/chromium/components/payments/core/strings_util.h
+++ b/chromium/components/payments/core/strings_util.h
@@ -5,11 +5,27 @@
#ifndef COMPONENTS_PAYMENTS_CORE_STRINGS_UTIL_H_
#define COMPONENTS_PAYMENTS_CORE_STRINGS_UTIL_H_
+#include <string>
+
#include "base/strings/string16.h"
#include "components/payments/core/payment_options_provider.h"
+namespace autofill {
+class AutofillProfile;
+}
+
namespace payments {
+// Helper function to create a shipping address label from an autofill profile.
+base::string16 GetShippingAddressLabelFormAutofillProfile(
+ const autofill::AutofillProfile& profile,
+ const std::string& locale);
+
+// Helper function to create a billing address label from an autofill profile.
+base::string16 GetBillingAddressLabelFromAutofillProfile(
+ const autofill::AutofillProfile& profile,
+ const std::string& locale);
+
// Gets the informational message to be displayed in the shipping address
// selector view when there are no valid shipping options.
base::string16 GetShippingAddressSelectorInfoMessage(
diff --git a/chromium/components/payments/core/subkey_requester.cc b/chromium/components/payments/core/subkey_requester.cc
new file mode 100644
index 00000000000..513fc54ffc4
--- /dev/null
+++ b/chromium/components/payments/core/subkey_requester.cc
@@ -0,0 +1,129 @@
+// Copyright 2017 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.
+
+#include "components/payments/core/subkey_requester.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/cancelable_callback.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "third_party/libaddressinput/chromium/chrome_address_validator.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h"
+namespace payments {
+
+namespace {
+
+using ::i18n::addressinput::Source;
+using ::i18n::addressinput::Storage;
+
+class SubKeyRequest : public SubKeyRequester::Request {
+ public:
+ // The |delegate| and |address_validator| need to outlive this Request.
+ SubKeyRequest(const std::string& region_code,
+ int timeout_seconds,
+ autofill::AddressValidator* address_validator,
+ SubKeyReceiverCallback on_subkeys_received)
+ : region_code_(region_code),
+ address_validator_(address_validator),
+ on_subkeys_received_(std::move(on_subkeys_received)),
+ has_responded_(false),
+ on_timeout_(base::Bind(&::payments::SubKeyRequest::OnRulesLoaded,
+ base::Unretained(this))) {
+ base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, on_timeout_.callback(),
+ base::TimeDelta::FromSeconds(timeout_seconds));
+ }
+
+ ~SubKeyRequest() override {}
+
+ void OnRulesLoaded() override {
+ on_timeout_.Cancel();
+ // Check if the timeout happened before the rules were loaded.
+ if (has_responded_)
+ return;
+ has_responded_ = true;
+
+ std::move(on_subkeys_received_)
+ .Run(address_validator_->GetRegionSubKeys(region_code_));
+ }
+
+ private:
+ std::string region_code_;
+ // Not owned. Never null. Outlive this object.
+ autofill::AddressValidator* address_validator_;
+
+ SubKeyReceiverCallback on_subkeys_received_;
+
+ bool has_responded_;
+ base::CancelableCallback<void()> on_timeout_;
+
+ DISALLOW_COPY_AND_ASSIGN(SubKeyRequest);
+};
+
+} // namespace
+
+SubKeyRequester::SubKeyRequester(std::unique_ptr<Source> source,
+ std::unique_ptr<Storage> storage)
+ : address_validator_(std::move(source), std::move(storage), this) {}
+
+SubKeyRequester::~SubKeyRequester() {}
+
+void SubKeyRequester::StartRegionSubKeysRequest(const std::string& region_code,
+ int timeout_seconds,
+ SubKeyReceiverCallback cb) {
+ DCHECK(timeout_seconds >= 0);
+
+ std::unique_ptr<SubKeyRequest> request(base::MakeUnique<SubKeyRequest>(
+ region_code, timeout_seconds, &address_validator_, std::move(cb)));
+
+ if (AreRulesLoadedForRegion(region_code)) {
+ request->OnRulesLoaded();
+ } else {
+ // Setup the variables so that the subkeys request is sent, when the rules
+ // are loaded.
+ pending_subkey_region_code_ = region_code;
+ pending_subkey_request_ = std::move(request);
+
+ // Start loading the rules for that region. If the rules were already in the
+ // process of being loaded, this call will do nothing.
+ LoadRulesForRegion(region_code);
+ }
+}
+
+bool SubKeyRequester::AreRulesLoadedForRegion(const std::string& region_code) {
+ return address_validator_.AreRulesLoadedForRegion(region_code);
+}
+
+void SubKeyRequester::LoadRulesForRegion(const std::string& region_code) {
+ address_validator_.LoadRules(region_code);
+}
+
+void SubKeyRequester::OnAddressValidationRulesLoaded(
+ const std::string& region_code,
+ bool success) {
+ // The case for |success| == false is already handled. if |success| == false,
+ // AddressValidator::GetRegionSubKeys will return an empty list of subkeys.
+ // Therefore, here, we can ignore the value of |success|.
+
+ // Check if there is any subkey request for that region code.
+ if (!pending_subkey_region_code_.compare(region_code))
+ pending_subkey_request_->OnRulesLoaded();
+ pending_subkey_region_code_.clear();
+ pending_subkey_request_.reset();
+}
+
+void SubKeyRequester::CancelPendingGetSubKeys() {
+ pending_subkey_region_code_.clear();
+ pending_subkey_request_.reset();
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/core/subkey_requester.h b/chromium/components/payments/core/subkey_requester.h
new file mode 100644
index 00000000000..1813781d5b4
--- /dev/null
+++ b/chromium/components/payments/core/subkey_requester.h
@@ -0,0 +1,73 @@
+// Copyright 2017 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.
+
+#ifndef COMPONENTS_PAYMENTS_CORE_SUBKEY_REQUESTER_H_
+#define COMPONENTS_PAYMENTS_CORE_SUBKEY_REQUESTER_H_
+
+#include "base/macros.h"
+#include "third_party/libaddressinput/chromium/chrome_address_validator.h"
+
+namespace payments {
+
+using SubKeyReceiverCallback =
+ base::OnceCallback<void(const std::vector<std::string>&)>;
+
+// SubKeyRequester Loads Rules from the server and extracts the subkeys.
+// For a given key (region code for a country, such as US), the list of its
+// corresponding subkeys is the list of that countries admin areas (states,
+// provinces, ..).
+class SubKeyRequester : public autofill::LoadRulesListener {
+ public:
+ // The interface for the subkey request.
+ class Request {
+ public:
+ virtual void OnRulesLoaded() = 0;
+ virtual ~Request() {}
+ };
+
+ SubKeyRequester(std::unique_ptr<i18n::addressinput::Source> source,
+ std::unique_ptr<i18n::addressinput::Storage> storage);
+ ~SubKeyRequester() override;
+
+ // If the rules for |region_code| are loaded, this gets the subkeys for the
+ // |region_code|, synchronously. If they are not loaded yet, it sets up a
+ // task to get the subkeys when the rules are loaded (asynchronous). If the
+ // loading has not yet started, it will also start loading the rules for the
+ // |region_code|. The received subkeys will be returned to the |requester|. If
+ // the subkeys are not received in |timeout_seconds|, then the requester will
+ // be informed and the request will be canceled. |requester| should never be
+ // null.
+ void StartRegionSubKeysRequest(const std::string& region_code,
+ int timeout_seconds,
+ SubKeyReceiverCallback cb);
+
+ // Returns whether the rules for the specified |region_code| have finished
+ // loading.
+ bool AreRulesLoadedForRegion(const std::string& region_code);
+
+ // Start loading the rules for the specified |region_code|.
+ virtual void LoadRulesForRegion(const std::string& region_code);
+
+ // Cancels the pending subkey request task.
+ void CancelPendingGetSubKeys();
+
+ private:
+ // Called when the address rules for the |region_code| have finished
+ // loading. Implementation of the LoadRulesListener interface.
+ void OnAddressValidationRulesLoaded(const std::string& region_code,
+ bool success) override;
+
+ // The region code and the request for the pending subkey request.
+ std::unique_ptr<Request> pending_subkey_request_;
+ std::string pending_subkey_region_code_;
+
+ // The address validator used to load subkeys.
+ autofill::AddressValidator address_validator_;
+
+ DISALLOW_COPY_AND_ASSIGN(SubKeyRequester);
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CORE_SUBKEY_REQUESTER_H_
diff --git a/chromium/components/payments/core/subkey_requester_unittest.cc b/chromium/components/payments/core/subkey_requester_unittest.cc
new file mode 100644
index 00000000000..f679c16a6b0
--- /dev/null
+++ b/chromium/components/payments/core/subkey_requester_unittest.cc
@@ -0,0 +1,194 @@
+// Copyright 2017 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.
+
+#include "components/payments/core/subkey_requester.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_task_scheduler.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/null_storage.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/source.h"
+#include "third_party/libaddressinput/src/cpp/include/libaddressinput/storage.h"
+#include "third_party/libaddressinput/src/cpp/test/testdata_source.h"
+
+namespace payments {
+namespace {
+
+using ::i18n::addressinput::NullStorage;
+using ::i18n::addressinput::Source;
+using ::i18n::addressinput::Storage;
+using ::i18n::addressinput::TestdataSource;
+
+const char kLocale[] = "OZ";
+const int kInvalidSize = -1;
+const int kCorrectSize = 2; // for subkeys = Do, Re
+const int kEmptySize = 0;
+
+class SubKeyReceiver : public base::RefCountedThreadSafe<SubKeyReceiver> {
+ public:
+ SubKeyReceiver() : subkeys_size_(kInvalidSize) {}
+
+ void OnSubKeysReceived(const std::vector<std::string>& subkeys) {
+ subkeys_size_ = subkeys.size();
+ }
+
+ int subkeys_size() const { return subkeys_size_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<SubKeyReceiver>;
+ ~SubKeyReceiver() {}
+
+ int subkeys_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(SubKeyReceiver);
+};
+
+// Used to load region rules for this test.
+class ChromiumTestdataSource : public TestdataSource {
+ public:
+ ChromiumTestdataSource() : TestdataSource(true) {}
+
+ ~ChromiumTestdataSource() override {}
+
+ // For this test, only load the rules for the kLocale.
+ void Get(const std::string& key, const Callback& data_ready) const override {
+ data_ready(
+ true, key,
+ new std::string(
+ "{\"data/OZ\": "
+ "{\"id\":\"data/OZ\",\"key\":\"OZ\",\"name\":\"Oz \", "
+ "\"lang\":\"en\",\"languages\":\"en\",\"sub_keys\":\"DO~Re\"}}"));
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ChromiumTestdataSource);
+};
+
+// A test subclass of the SubKeyRequesterImpl. Used to simulate rules not
+// being loaded.
+class TestSubKeyRequester : public SubKeyRequester {
+ public:
+ TestSubKeyRequester(std::unique_ptr<::i18n::addressinput::Source> source,
+ std::unique_ptr<::i18n::addressinput::Storage> storage)
+ : SubKeyRequester(std::move(source), std::move(storage)),
+ should_load_rules_(true) {}
+
+ ~TestSubKeyRequester() override {}
+
+ void ShouldLoadRules(bool should_load_rules) {
+ should_load_rules_ = should_load_rules;
+ }
+
+ void LoadRulesForRegion(const std::string& region_code) override {
+ if (should_load_rules_) {
+ SubKeyRequester::LoadRulesForRegion(region_code);
+ }
+ }
+
+ private:
+ bool should_load_rules_;
+ base::test::ScopedTaskScheduler scoped_task_scheduler_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestSubKeyRequester);
+};
+
+} // namespace
+
+class SubKeyRequesterTest : public testing::Test {
+ protected:
+ SubKeyRequesterTest()
+ : requester_(new TestSubKeyRequester(
+ std::unique_ptr<Source>(new ChromiumTestdataSource),
+ std::unique_ptr<Storage>(new NullStorage))) {}
+
+ ~SubKeyRequesterTest() override {}
+
+ const std::unique_ptr<TestSubKeyRequester> requester_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SubKeyRequesterTest);
+};
+
+// Tests that rules are not loaded by default.
+TEST_F(SubKeyRequesterTest, AreRulesLoadedForRegion_NotLoaded) {
+ EXPECT_FALSE(requester_->AreRulesLoadedForRegion(kLocale));
+}
+
+// Tests that the rules are loaded correctly.
+TEST_F(SubKeyRequesterTest, AreRulesLoadedForRegion_Loaded) {
+ requester_->LoadRulesForRegion(kLocale);
+ EXPECT_TRUE(requester_->AreRulesLoadedForRegion(kLocale));
+}
+
+// Tests that if the rules are loaded before the subkey request is started, the
+// received subkeys will be returned to the delegate synchronously.
+TEST_F(SubKeyRequesterTest, StartRequest_RulesLoaded) {
+ scoped_refptr<SubKeyReceiver> subkey_receiver_ = new SubKeyReceiver();
+
+ SubKeyReceiverCallback cb =
+ base::BindOnce(&SubKeyReceiver::OnSubKeysReceived, subkey_receiver_);
+
+ // Load the rules.
+ requester_->LoadRulesForRegion(kLocale);
+ EXPECT_TRUE(requester_->AreRulesLoadedForRegion(kLocale));
+
+ // Start the request.
+ requester_->StartRegionSubKeysRequest(kLocale, 0, std::move(cb));
+
+ // Since the rules are already loaded, the subkeys should be received
+ // synchronously.
+ EXPECT_EQ(subkey_receiver_->subkeys_size(), kCorrectSize);
+}
+
+// Tests that if the rules are not loaded before the request and cannot be
+// loaded after, the subkeys will not be received and the delegate will be
+// notified.
+TEST_F(SubKeyRequesterTest, StartRequest_RulesNotLoaded_WillNotLoad) {
+ scoped_refptr<SubKeyReceiver> subkey_receiver_ = new SubKeyReceiver();
+
+ SubKeyReceiverCallback cb =
+ base::BindOnce(&SubKeyReceiver::OnSubKeysReceived, subkey_receiver_);
+
+ // Make sure the rules will not be loaded in the StartRegionSubKeysRequest
+ // call.
+ requester_->ShouldLoadRules(false);
+
+ // Start the normalization.
+ requester_->StartRegionSubKeysRequest(kLocale, 0, std::move(cb));
+
+ // Let the timeout execute.
+ base::RunLoop().RunUntilIdle();
+
+ // Since the rules are never loaded and the timeout is 0, the delegate should
+ // get notified that the subkeys could not be received.
+ EXPECT_EQ(subkey_receiver_->subkeys_size(), kEmptySize);
+}
+
+// Tests that if the rules are not loaded before the call to
+// StartRegionSubKeysRequest, they will be loaded in the call.
+TEST_F(SubKeyRequesterTest, StartRequest_RulesNotLoaded_WillLoad) {
+ scoped_refptr<SubKeyReceiver> subkey_receiver_ = new SubKeyReceiver();
+
+ SubKeyReceiverCallback cb =
+ base::BindOnce(&SubKeyReceiver::OnSubKeysReceived, subkey_receiver_);
+
+ // Make sure the rules will not be loaded in the StartRegionSubKeysRequest
+ // call.
+ requester_->ShouldLoadRules(true);
+ // Start the request.
+ requester_->StartRegionSubKeysRequest(kLocale, 0, std::move(cb));
+
+ // Even if the rules are not loaded before the call to
+ // StartRegionSubKeysRequest, they should get loaded in the call. Since our
+ // test source is synchronous, the request will happen synchronously
+ // too.
+ EXPECT_TRUE(requester_->AreRulesLoadedForRegion(kLocale));
+ EXPECT_EQ(subkey_receiver_->subkeys_size(), kCorrectSize);
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/core/test_address_normalizer.cc b/chromium/components/payments/core/test_address_normalizer.cc
new file mode 100644
index 00000000000..e0b9fc868e8
--- /dev/null
+++ b/chromium/components/payments/core/test_address_normalizer.cc
@@ -0,0 +1,38 @@
+// Copyright 2017 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.
+
+#include "components/payments/core/test_address_normalizer.h"
+
+namespace payments {
+
+bool TestAddressNormalizer::AreRulesLoadedForRegion(
+ const std::string& region_code) {
+ return true;
+}
+
+void TestAddressNormalizer::StartAddressNormalization(
+ const autofill::AutofillProfile& profile,
+ const std::string& region_code,
+ int timeout_seconds,
+ AddressNormalizer::Delegate* requester) {
+ if (instantaneous_normalization_) {
+ requester->OnAddressNormalized(profile);
+ return;
+ }
+
+ // Setup the necessary variables for the delayed normalization.
+ profile_ = profile;
+ requester_ = requester;
+}
+
+void TestAddressNormalizer::DelayNormalization() {
+ instantaneous_normalization_ = false;
+}
+
+void TestAddressNormalizer::CompleteAddressNormalization() {
+ DCHECK(instantaneous_normalization_ == false);
+ requester_->OnAddressNormalized(profile_);
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/core/test_address_normalizer.h b/chromium/components/payments/core/test_address_normalizer.h
new file mode 100644
index 00000000000..56def89de36
--- /dev/null
+++ b/chromium/components/payments/core/test_address_normalizer.h
@@ -0,0 +1,46 @@
+// Copyright 2017 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.
+
+#ifndef COMPONENTS_PAYMENTS_CORE_TEST_ADDRESS_NORMALIZER_H_
+#define COMPONENTS_PAYMENTS_CORE_TEST_ADDRESS_NORMALIZER_H_
+
+#include "components/payments/core/address_normalizer.h"
+
+#include "components/autofill/core/browser/autofill_profile.h"
+
+namespace payments {
+
+// A simpler version of the address normalizer to be used in tests. Can be set
+// to normalize instantaneously or to wait for a call.
+class TestAddressNormalizer : public AddressNormalizer {
+ public:
+ TestAddressNormalizer() {}
+
+ void LoadRulesForRegion(const std::string& region_code) override {}
+
+ bool AreRulesLoadedForRegion(const std::string& region_code) override;
+
+ void StartAddressNormalization(
+ const autofill::AutofillProfile& profile,
+ const std::string& region_code,
+ int timeout_seconds,
+ AddressNormalizer::Delegate* requester) override;
+
+ void OnAddressValidationRulesLoaded(const std::string& region_code,
+ bool success) override {}
+
+ void DelayNormalization();
+
+ void CompleteAddressNormalization();
+
+ private:
+ autofill::AutofillProfile profile_;
+ AddressNormalizer::Delegate* requester_;
+
+ bool instantaneous_normalization_ = true;
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CORE_TEST_ADDRESS_NORMALIZER_H_ \ No newline at end of file
diff --git a/chromium/components/payments/core/test_payment_request_delegate.cc b/chromium/components/payments/core/test_payment_request_delegate.cc
new file mode 100644
index 00000000000..66794195362
--- /dev/null
+++ b/chromium/components/payments/core/test_payment_request_delegate.cc
@@ -0,0 +1,88 @@
+// Copyright 2017 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.
+
+#include "components/payments/core/test_payment_request_delegate.h"
+
+#include "base/strings/utf_string_conversions.h"
+
+namespace payments {
+
+TestPaymentRequestDelegate::TestPaymentRequestDelegate(
+ autofill::PersonalDataManager* personal_data_manager)
+ : personal_data_manager_(personal_data_manager),
+ locale_("en-US"),
+ last_committed_url_("https://shop.com") {}
+
+TestPaymentRequestDelegate::~TestPaymentRequestDelegate() {}
+
+autofill::PersonalDataManager*
+TestPaymentRequestDelegate::GetPersonalDataManager() {
+ return personal_data_manager_;
+}
+
+const std::string& TestPaymentRequestDelegate::GetApplicationLocale() const {
+ return locale_;
+}
+
+bool TestPaymentRequestDelegate::IsIncognito() const {
+ return false;
+}
+
+bool TestPaymentRequestDelegate::IsSslCertificateValid() {
+ return true;
+}
+
+const GURL& TestPaymentRequestDelegate::GetLastCommittedURL() const {
+ return last_committed_url_;
+}
+
+void TestPaymentRequestDelegate::DoFullCardRequest(
+ const autofill::CreditCard& credit_card,
+ base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
+ result_delegate) {
+ if (instantaneous_full_card_request_result_) {
+ result_delegate->OnFullCardRequestSucceeded(credit_card,
+ base::ASCIIToUTF16("123"));
+ return;
+ }
+
+ full_card_request_card_ = credit_card;
+ full_card_result_delegate_ = result_delegate;
+}
+
+AddressNormalizer* TestPaymentRequestDelegate::GetAddressNormalizer() {
+ return &address_normalizer_;
+}
+
+autofill::RegionDataLoader* TestPaymentRequestDelegate::GetRegionDataLoader() {
+ return nullptr;
+}
+
+ukm::UkmRecorder* TestPaymentRequestDelegate::GetUkmRecorder() {
+ return nullptr;
+}
+
+TestAddressNormalizer* TestPaymentRequestDelegate::test_address_normalizer() {
+ return &address_normalizer_;
+}
+
+void TestPaymentRequestDelegate::DelayFullCardRequestCompletion() {
+ instantaneous_full_card_request_result_ = false;
+}
+
+void TestPaymentRequestDelegate::CompleteFullCardRequest() {
+ DCHECK(instantaneous_full_card_request_result_ == false);
+ full_card_result_delegate_->OnFullCardRequestSucceeded(
+ full_card_request_card_, base::ASCIIToUTF16("123"));
+}
+
+std::string TestPaymentRequestDelegate::GetAuthenticatedEmail() const {
+ return "";
+}
+
+PrefService* TestPaymentRequestDelegate::GetPrefService() {
+ return nullptr;
+}
+
+} // namespace payments
diff --git a/chromium/components/payments/core/test_payment_request_delegate.h b/chromium/components/payments/core/test_payment_request_delegate.h
new file mode 100644
index 00000000000..e0697a7d326
--- /dev/null
+++ b/chromium/components/payments/core/test_payment_request_delegate.h
@@ -0,0 +1,59 @@
+// Copyright 2017 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.
+
+#ifndef COMPONENTS_PAYMENTS_CORE_TEST_PAYMENT_REQUEST_DELEGATE_H_
+#define COMPONENTS_PAYMENTS_CORE_TEST_PAYMENT_REQUEST_DELEGATE_H_
+
+#include <string>
+
+#include "components/payments/core/payment_request_delegate.h"
+#include "components/payments/core/test_address_normalizer.h"
+
+namespace payments {
+
+class TestPaymentRequestDelegate : public PaymentRequestDelegate {
+ public:
+ TestPaymentRequestDelegate(
+ autofill::PersonalDataManager* personal_data_manager);
+ ~TestPaymentRequestDelegate() override;
+
+ // PaymentRequestDelegate
+ void ShowDialog(PaymentRequest* request) override {}
+ void CloseDialog() override {}
+ void ShowErrorMessage() override {}
+ autofill::PersonalDataManager* GetPersonalDataManager() override;
+ const std::string& GetApplicationLocale() const override;
+ bool IsIncognito() const override;
+ bool IsSslCertificateValid() override;
+ const GURL& GetLastCommittedURL() const override;
+ void DoFullCardRequest(
+ const autofill::CreditCard& credit_card,
+ base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
+ result_delegate) override;
+ AddressNormalizer* GetAddressNormalizer() override;
+ autofill::RegionDataLoader* GetRegionDataLoader() override;
+ ukm::UkmRecorder* GetUkmRecorder() override;
+ std::string GetAuthenticatedEmail() const override;
+ PrefService* GetPrefService() override;
+
+ TestAddressNormalizer* test_address_normalizer();
+ void DelayFullCardRequestCompletion();
+ void CompleteFullCardRequest();
+
+ private:
+ autofill::PersonalDataManager* personal_data_manager_;
+ std::string locale_;
+ const GURL last_committed_url_;
+ TestAddressNormalizer address_normalizer_;
+
+ bool instantaneous_full_card_request_result_ = true;
+ autofill::CreditCard full_card_request_card_;
+ base::WeakPtr<autofill::payments::FullCardRequest::ResultDelegate>
+ full_card_result_delegate_;
+ DISALLOW_COPY_AND_ASSIGN(TestPaymentRequestDelegate);
+};
+
+} // namespace payments
+
+#endif // COMPONENTS_PAYMENTS_CORE_TEST_PAYMENT_REQUEST_DELEGATE_H_