| // 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/autofill_profile_validator.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/cancelable_callback.h" |
| #include "base/logging.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/sequenced_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "components/autofill/core/browser/autofill_profile.h" |
| #include "components/autofill/core/browser/autofill_profile_validation_util.h" |
| #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_data.h" |
| #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_validator.h" |
| |
| namespace autofill { |
| namespace { |
| |
| using ::i18n::addressinput::COUNTRY; |
| using ::i18n::addressinput::ADMIN_AREA; |
| using ::i18n::addressinput::LOCALITY; |
| using ::i18n::addressinput::DEPENDENT_LOCALITY; |
| using ::i18n::addressinput::SORTING_CODE; |
| using ::i18n::addressinput::POSTAL_CODE; |
| using ::i18n::addressinput::STREET_ADDRESS; |
| using ::i18n::addressinput::RECIPIENT; |
| |
| const int kRulesLoadingTimeoutSeconds = 5; |
| |
| } // namespace |
| |
| AutofillProfileValidator::ValidationRequest::ValidationRequest( |
| base::WeakPtr<AutofillProfile> profile, |
| autofill::AddressValidator* validator, |
| AutofillProfileValidatorCallback on_validated) |
| : profile_(profile), |
| validator_(validator), |
| on_validated_(std::move(on_validated)), |
| has_responded_(false), |
| on_timeout_(base::Bind(&ValidationRequest::OnRulesLoaded, |
| base::Unretained(this))) { |
| DCHECK(profile_); |
| base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, on_timeout_.callback(), |
| base::TimeDelta::FromSeconds(kRulesLoadingTimeoutSeconds)); |
| } |
| |
| AutofillProfileValidator::ValidationRequest::~ValidationRequest() { |
| on_timeout_.Cancel(); |
| } |
| |
| void AutofillProfileValidator::ValidationRequest::OnRulesLoaded() { |
| on_timeout_.Cancel(); |
| // Check if the timeout happened before the rules were loaded. |
| if (has_responded_) |
| return; |
| has_responded_ = true; |
| |
| if (!profile_) |
| return; |
| |
| profile_validation_util::ValidateProfile(profile_.get(), validator_); |
| |
| std::move(on_validated_).Run(profile_.get()); |
| } |
| |
| AutofillProfileValidator::AutofillProfileValidator( |
| std::unique_ptr<::i18n::addressinput::Source> source, |
| std::unique_ptr<::i18n::addressinput::Storage> storage) |
| : address_validator_(std::move(source), std::move(storage), this) {} |
| |
| AutofillProfileValidator::~AutofillProfileValidator() {} |
| |
| void AutofillProfileValidator::StartProfileValidation( |
| AutofillProfile* profile, |
| AutofillProfileValidatorCallback cb) { |
| DCHECK(profile); |
| if (!profile) |
| return; |
| std::unique_ptr<ValidationRequest> request( |
| std::make_unique<ValidationRequest>(profile->AsWeakPtr(), |
| &address_validator_, std::move(cb))); |
| |
| // If the |region_code| is not a valid code according to our source, calling |
| // LoadRules would result in calling OnAddressValidationRulesLoaded with |
| // success = false. Thus, we can handle illegitimate |region_code|'s as well. |
| std::string region_code = |
| base::UTF16ToUTF8(profile->GetRawInfo(ADDRESS_HOME_COUNTRY)); |
| if (address_validator_.AreRulesLoadedForRegion(region_code)) { |
| request->OnRulesLoaded(); |
| } else { |
| // Setup the variables to start validation when the rules are loaded. |
| pending_requests_[region_code].push_back(std::move(request)); |
| |
| // Start loading the rules for the region. If the rules were already in the |
| // process of being loaded, this call will do nothing. |
| address_validator_.LoadRules(region_code); |
| } |
| } |
| |
| void AutofillProfileValidator::OnAddressValidationRulesLoaded( |
| const std::string& region_code, |
| bool success) { |
| // Even if success = false, we can still validate address partially. We can |
| // check for missing fields or unexpected fields. We can also validate |
| // non-address fields. |
| |
| // Check if there is any request for that region code. |
| auto it = pending_requests_.find(region_code); |
| if (it != pending_requests_.end()) { |
| for (auto& request : it->second) { |
| request->OnRulesLoaded(); |
| } |
| pending_requests_.erase(it); |
| } |
| } |
| |
| bool AutofillProfileValidator::AreRulesLoadedForRegion( |
| const std::string& region_code) { |
| return address_validator_.AreRulesLoadedForRegion(region_code); |
| } |
| |
| void AutofillProfileValidator::LoadRulesForRegion( |
| const std::string& region_code) { |
| address_validator_.LoadRules(region_code); |
| } |
| } // namespace autofill |