blob: e42390565323264797e995470de272d7ab722a51 [file] [log] [blame]
// 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/autofill/core/browser/address_normalizer_impl.h"
#include <stddef.h>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/cancelable_callback.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task_scheduler/post_task.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/address_i18n.h"
#include "components/autofill/core/browser/autofill_data_util.h"
#include "components/autofill/core/browser/autofill_profile.h"
#include "components/autofill/core/browser/phone_number_i18n.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 autofill {
namespace {
using ::i18n::addressinput::Source;
using ::i18n::addressinput::Storage;
using DeleteOnTaskRunnerStorageUniquePtr =
std::unique_ptr<Storage, base::OnTaskRunnerDeleter>;
bool NormalizeProfileWithValidator(AutofillProfile* profile,
const std::string& app_locale,
AddressValidator* address_validator) {
DCHECK(address_validator);
// Create the AddressData from the profile.
::i18n::addressinput::AddressData address_data =
*autofill::i18n::CreateAddressDataFromAutofillProfile(*profile,
app_locale);
// Normalize the address.
if (!address_validator->NormalizeAddress(&address_data))
return false;
profile->SetRawInfo(ADDRESS_HOME_STATE,
base::UTF8ToUTF16(address_data.administrative_area));
profile->SetRawInfo(ADDRESS_HOME_CITY,
base::UTF8ToUTF16(address_data.locality));
profile->SetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY,
base::UTF8ToUTF16(address_data.dependent_locality));
return true;
}
// Formats the phone number in |profile| to E.164 format. Leaves the original
// phone number if formatting was not possible (or already optimal).
void FormatPhoneNumberToE164(AutofillProfile* profile,
const std::string& region_code,
const std::string& app_locale) {
const std::string formatted_number = autofill::i18n::FormatPhoneForResponse(
base::UTF16ToUTF8(
profile->GetInfo(AutofillType(PHONE_HOME_WHOLE_NUMBER), app_locale)),
region_code);
profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER,
base::UTF8ToUTF16(formatted_number));
}
std::unique_ptr<AddressValidator> CreateAddressValidator(
std::unique_ptr<Source> source,
DeleteOnTaskRunnerStorageUniquePtr storage,
LoadRulesListener* load_rules_listener) {
return std::make_unique<AddressValidator>(std::move(source),
base::WrapUnique(storage.release()),
load_rules_listener);
}
} // namespace
class AddressNormalizerImpl::NormalizationRequest {
public:
NormalizationRequest(const AutofillProfile& profile,
const std::string& app_locale,
int timeout_seconds,
AddressNormalizer::NormalizationCallback callback)
: profile_(profile),
app_locale_(app_locale),
callback_(std::move(callback)),
weak_ptr_factory_(this) {
// OnRulesLoaded will be called in |timeout_seconds| if the rules are not
// loaded in time.
base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&NormalizationRequest::OnRulesLoaded,
weak_ptr_factory_.GetWeakPtr(),
/*success=*/false,
/*address_validator=*/nullptr),
base::TimeDelta::FromSeconds(timeout_seconds));
}
~NormalizationRequest() {}
void OnRulesLoaded(bool success, AddressValidator* address_validator) {
// Check if the timeout happened before the rules were loaded.
if (has_responded_)
return;
has_responded_ = true;
// The phone number formatting doesn't require the rules to have been
// successfully loaded. Therefore it is done for every request regardless of
// |success|.
const std::string region_code =
data_util::GetCountryCodeWithFallback(profile_, app_locale_);
FormatPhoneNumberToE164(&profile_, region_code, app_locale_);
if (!success) {
std::move(callback_).Run(/*success=*/false, profile_);
return;
}
// The rules should be loaded.
DCHECK(address_validator->AreRulesLoadedForRegion(region_code));
bool normalization_success = NormalizeProfileWithValidator(
&profile_, app_locale_, address_validator);
std::move(callback_).Run(/*success=*/normalization_success, profile_);
}
private:
AutofillProfile profile_;
const std::string app_locale_;
AddressNormalizer::NormalizationCallback callback_;
bool has_responded_ = false;
base::WeakPtrFactory<NormalizationRequest> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(NormalizationRequest);
};
AddressNormalizerImpl::AddressNormalizerImpl(std::unique_ptr<Source> source,
std::unique_ptr<Storage> storage,
const std::string& app_locale)
: app_locale_(app_locale), weak_ptr_factory_(this) {
// |address_validator_| is created in the background. Once initialized, it
// will run any pending normalization.
//
// |storage| is wrapped in a DeleteOnTaskRunnerStorageUniquePtr to ensure that
// it is deleted on the current sequence even if the task is skipped on
// shutdown. This is important to prevent an access race when the destructor
// of |storage| accesses an ObserverList that lives on the current sequence.
// https://crbug.com/829122
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
base::BindOnce(
&CreateAddressValidator, std::move(source),
DeleteOnTaskRunnerStorageUniquePtr(
storage.release(), base::OnTaskRunnerDeleter(
base::SequencedTaskRunnerHandle::Get())),
this),
base::BindOnce(&AddressNormalizerImpl::OnAddressValidatorCreated,
weak_ptr_factory_.GetWeakPtr()));
}
AddressNormalizerImpl::~AddressNormalizerImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void AddressNormalizerImpl::LoadRulesForRegion(const std::string& region_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// The |address_validator_| is initialized in a background task. It may not
// be available yet.
if (!address_validator_)
return;
address_validator_->LoadRules(region_code);
}
void AddressNormalizerImpl::NormalizeAddressAsync(
const AutofillProfile& profile,
int timeout_seconds,
AddressNormalizer::NormalizationCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GE(timeout_seconds, 0);
std::unique_ptr<NormalizationRequest> request =
std::make_unique<NormalizationRequest>(
profile, app_locale_, timeout_seconds, std::move(callback));
// If the rules are already loaded for |region_code| and the validator is
// initialized, the |request| will callback synchronously.
const std::string region_code =
data_util::GetCountryCodeWithFallback(profile, app_locale_);
if (address_validator_ && AreRulesLoadedForRegion(region_code)) {
request->OnRulesLoaded(/*success=*/true, address_validator_.get());
return;
}
// Otherwise, the request is added to the queue for |region_code|.
AddNormalizationRequestForRegion(std::move(request), region_code);
// Start loading the rules for that region. If the rules were already in the
// process of being loaded or |address_validator_| is not yet initialized,
// this call will do nothing.
LoadRulesForRegion(region_code);
}
bool AddressNormalizerImpl::NormalizeAddressSync(AutofillProfile* profile) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Phone number is always formatted, regardless of whether the address rules
// are loaded for |region_code|.
const std::string region_code =
data_util::GetCountryCodeWithFallback(*profile, app_locale_);
FormatPhoneNumberToE164(profile, region_code, app_locale_);
if (!address_validator_) {
// Can't normalize the address synchronously. Add an empty
// NormalizationRequest so that the rules are loaded when the validator is
// initialized (See OnAddressValidatorCreated).
AddNormalizationRequestForRegion(nullptr, region_code);
return false;
}
if (!AreRulesLoadedForRegion(region_code)) {
// Ensure that the rules are being loaded for that region.
LoadRulesForRegion(region_code);
return false;
}
return NormalizeProfileWithValidator(profile, app_locale_,
address_validator_.get());
}
bool AddressNormalizerImpl::AreRulesLoadedForRegion(
const std::string& region_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return address_validator_->AreRulesLoadedForRegion(region_code);
}
void AddressNormalizerImpl::OnAddressValidationRulesLoaded(
const std::string& region_code,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// |address_validator_| is logically loaded at this point, but DCHECK anyway.
DCHECK(address_validator_);
// Check if an address normalization is pending for |region_code|.
auto it = pending_normalization_.find(region_code);
if (it != pending_normalization_.end()) {
for (size_t i = 0; i < it->second.size(); ++i) {
// Some NormalizationRequest are null, and served only to load the rules.
if (it->second[i]) {
// TODO(crbug.com/777417): |success| appears to be true even when the
// key was not actually found.
it->second[i]->OnRulesLoaded(AreRulesLoadedForRegion(region_code),
address_validator_.get());
}
}
pending_normalization_.erase(it);
}
}
void AddressNormalizerImpl::OnAddressValidatorCreated(
std::unique_ptr<AddressValidator> validator) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!address_validator_);
address_validator_ = std::move(validator);
// Make a copy of region keys before calling LoadRulesForRegion on them,
// because LoadRulesForRegion may synchronously modify
// |pending_normalization_|.
std::vector<std::string> region_keys;
region_keys.reserve(pending_normalization_.size());
for (const auto& entry : pending_normalization_)
region_keys.push_back(entry.first);
// Load rules for regions with pending normalization requests.
for (const std::string& region : region_keys)
LoadRulesForRegion(region);
}
void AddressNormalizerImpl::AddNormalizationRequestForRegion(
std::unique_ptr<NormalizationRequest> request,
const std::string& region_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Setup the variables so the profile gets normalized when the rules have
// finished loading.
pending_normalization_[region_code].push_back(std::move(request));
}
} // namespace autofill