blob: 0bb513b5cabfd44ce1d24d272117b5c770162eeb [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// 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_manager.h"
#include "base/check_deref.h"
#include "base/command_line.h"
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/thread_pool.h"
#include "components/autofill/core/browser/country_type.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/logging/log_manager.h"
#include "components/autofill/core/browser/metrics/quality_metrics.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_data_validation.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_internals/log_message.h"
#include "components/autofill/core/common/autofill_internals/logging_scope.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/autofill_tick_clock.h"
#include "components/optimization_guide/machine_learning_tflite_buildflags.h"
#include "components/translate/core/common/language_detection_details.h"
#include "components/translate/core/common/translate_constants.h"
#include "ui/gfx/geometry/rect_f.h"
#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
#include "components/autofill/core/browser/ml_model/autofill_ml_prediction_model_handler.h"
#endif
namespace autofill {
namespace {
// ParsingCallback(), NotifyObserversCallback(), and NotifyNoObserversCallback()
// assemble the reply callback for ParseFormAsync().
//
// An event
// AutofillManager::OnFoo(const FormData& form, args...)
// is handled by
// asynchronously parsing the form and then calling
// AutofillManager::OnFooImpl(const FormData& form, args...)
// unless the AutofillManager has been destructed or reset in the meantime.
//
// For some events, AutofillManager::Observer::On{Before,After}Foo() must be
// called before/after AutofillManager::OnFooImpl().
//
// The corresponding callback for ParseFormAsync() is assembled by
// ParsingCallback(&AutofillManager::OnFooImpl, ...)
// .Then(NotifyNoObserversCallback())
// or
// ParsingCallback(&AutofillManager::OnFooImpl, ...)
// .Then(NotifyObserversCallback(&Observer::OnAfterFoo, ...))
//
// `.Then(NotifyNoObserversCallback())` is needed in the first case to discard
// the return type of ParsingCallback().
template <typename Functor, typename... Args>
base::OnceCallback<AutofillManager&(AutofillManager&, const FormData&)>
ParsingCallback(Functor&& functor, Args&&... args) {
return base::BindOnce(
[](Functor&& functor, std::remove_reference_t<Args&&>... args,
AutofillManager& self, const FormData& form) -> AutofillManager& {
base::invoke(std::forward<Functor>(functor), self, form,
std::forward<Args>(args)...);
return self;
},
std::forward<Functor>(functor), std::forward<Args>(args)...);
}
// See ParsingCallback().
template <typename Functor, typename... Args>
[[nodiscard]] base::OnceCallback<void(AutofillManager&)>
NotifyObserversCallback(Functor&& functor, Args&&... args) {
return base::BindOnce(
[](Functor&& functor, std::remove_reference_t<Args&&>... args,
AutofillManager& self) {
self.NotifyObservers(std::forward<Functor>(functor),
std::forward<Args>(args)...);
},
std::forward<Functor>(functor), std::forward<Args>(args)...);
}
// See ParsingCallback().
base::OnceCallback<void(AutofillManager&)> NotifyNoObserversCallback() {
return base::DoNothingAs<void(AutofillManager&)>();
}
// Collects the FormGlobalIds of `forms`.
std::vector<FormGlobalId> GetFormGlobalIds(base::span<const FormData> forms) {
std::vector<FormGlobalId> form_ids;
form_ids.reserve(forms.size());
for (const FormData& form : forms) {
form_ids.push_back(form.global_id());
}
return form_ids;
}
// Returns the AutofillField* corresponding to |field| in |form| or nullptr,
// if not found.
AutofillField* FindAutofillFillField(const FormStructure& form,
const FormFieldData& field) {
for (const auto& f : form) {
if (field.global_id() == f->global_id())
return f.get();
}
for (const auto& cur_field : form) {
if (cur_field->SameFieldAs(field)) {
return cur_field.get();
}
}
return nullptr;
}
// Returns true if |live_form| does not match |cached_form|.
// TODO(crbug.com/1211834): This should be some form of FormData::DeepEqual().
bool CachedFormNeedsUpdate(const FormData& live_form,
const FormStructure& cached_form) {
if (cached_form.version() > live_form.version)
return false;
if (live_form.fields.size() != cached_form.field_count())
return true;
for (size_t i = 0; i < cached_form.field_count(); ++i) {
if (!cached_form.field(i)->SameFieldAs(live_form.fields[i]))
return true;
}
return false;
}
} // namespace
// static
void AutofillManager::LogAutofillTypePredictionsAvailable(
LogManager* log_manager,
const std::vector<FormStructure*>& forms) {
LogBuffer buffer(IsLoggingActive(log_manager));
for (FormStructure* form : forms)
LOG_AF(buffer) << *form;
LOG_AF(log_manager) << LoggingScope::kParsing << LogMessage::kParsedForms
<< std::move(buffer);
}
AutofillManager::AutofillManager(AutofillDriver* driver, AutofillClient* client)
: driver_(CHECK_DEREF(driver)),
client_(CHECK_DEREF(client)),
log_manager_(client->GetLogManager()),
form_interactions_ukm_logger_(CreateFormInteractionsUkmLogger()) {
translate::TranslateDriver* translate_driver = client->GetTranslateDriver();
if (translate_driver) {
translate_observation_.Observe(translate_driver);
}
}
AutofillManager::~AutofillManager() {
NotifyObservers(&Observer::OnAutofillManagerDestroyed);
translate_observation_.Reset();
}
// TODO(crbug.com/1309848): Unify form parsing logic.
// TODO(crbug.com/1465926): ML predictions are not computed here since
// `kAutofillPageLanguageDetection` is disabled by default. Once the form
// parsing logic is unified with `ParseFormsAsync()`, this won't be necessary
// anymore.
void AutofillManager::OnLanguageDetermined(
const translate::LanguageDetectionDetails& details) {
if (!base::FeatureList::IsEnabled(features::kAutofillPageLanguageDetection))
return;
if (details.adopted_language == translate::kUnknownLanguageCode ||
!driver_->IsInActiveFrame()) {
return;
}
NotifyObservers(&Observer::OnBeforeLanguageDetermined);
LanguageCode lang(details.adopted_language);
for (auto& [form_id, form_structure] : form_structures_)
form_structure->set_current_page_language(lang);
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
for (auto& [form_id, form_structure] : form_structures_) {
form_structure->DetermineHeuristicTypes(
client().GetVariationConfigCountryCode(),
form_interactions_ukm_logger(), log_manager_);
NotifyObservers(&Observer::OnFieldTypesDetermined, form_id,
Observer::FieldTypeSource::kHeuristicsOrAutocomplete);
}
NotifyObservers(&Observer::OnAfterLanguageDetermined);
return;
}
struct AsyncContext {
AsyncContext(
std::map<FormGlobalId, std::unique_ptr<FormStructure>> form_structures,
GeoIpCountryCode country_code,
LogManager* log_manager)
: form_structures(std::move(form_structures)),
country_code(std::move(country_code)),
log_manager(IsLoggingActive(log_manager)
? LogManager::CreateBuffering()
: nullptr) {}
std::map<FormGlobalId, std::unique_ptr<FormStructure>> form_structures;
GeoIpCountryCode country_code;
std::unique_ptr<BufferingLogManager> log_manager;
};
// To be run on a different task (must not access global or member
// variables).
// TODO(crbug.com/1309848): We can't pass a UKM logger because it's a member
// variable. To be fixed.
auto RunHeuristics = [](AsyncContext context) {
SCOPED_UMA_HISTOGRAM_TIMER(
"Autofill.Timing.OnLanguageDetermined.RunHeuristics");
for (auto& [id, form_structure] : context.form_structures) {
form_structure->DetermineHeuristicTypes(
context.country_code,
/*form_interactions_ukm_logger=*/nullptr, context.log_manager.get());
}
return context;
};
// To be run on the main thread (accesses member variables).
auto UpdateCache = [](base::WeakPtr<AutofillManager> self,
AsyncContext context) {
SCOPED_UMA_HISTOGRAM_TIMER(
"Autofill.Timing.OnLanguageDetermined.UpdateCache");
if (!self) {
return;
}
if (context.log_manager && self->log_manager_) {
context.log_manager->Flush(*self->log_manager_);
}
for (auto& [id, form_structure] : context.form_structures) {
self->form_structures_[id] = std::move(form_structure);
self->NotifyObservers(
&Observer::OnFieldTypesDetermined, id,
Observer::FieldTypeSource::kHeuristicsOrAutocomplete);
}
self->NotifyObservers(&Observer::OnAfterLanguageDetermined);
};
// Transfers ownership of the cached `form_structures_` to the worker task,
// which will eventually move them back into `form_structures_`. This means
// `AutofillManager::form_structures_` is empty for a brief period of time.
auto form_structures = std::exchange(form_structures_, {});
parsing_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
RunHeuristics,
AsyncContext(std::move(form_structures),
client().GetVariationConfigCountryCode(), log_manager_)),
base::BindOnce(UpdateCache, parsing_weak_ptr_factory_.GetWeakPtr()));
}
void AutofillManager::OnTranslateDriverDestroyed(
translate::TranslateDriver* translate_driver) {
translate_observation_.Reset();
}
LanguageCode AutofillManager::GetCurrentPageLanguage() {
const translate::LanguageState* language_state = client().GetLanguageState();
if (!language_state)
return LanguageCode();
return LanguageCode(language_state->current_language());
}
void AutofillManager::FillCreditCardForm(
const FormData& form,
const FormFieldData& field,
const CreditCard& credit_card,
const std::u16string& cvc,
const AutofillTriggerDetails& trigger_details) {
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
FillCreditCardFormImpl(form, field, credit_card, cvc, trigger_details);
return;
}
ParseFormAsync(form, ParsingCallback(&AutofillManager::FillCreditCardFormImpl,
field, credit_card, cvc, trigger_details)
.Then(NotifyNoObserversCallback()));
}
void AutofillManager::FillProfileForm(
const AutofillProfile& profile,
const FormData& form,
const FormFieldData& field,
const AutofillTriggerDetails& trigger_details) {
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
FillProfileFormImpl(form, field, profile, trigger_details);
return;
}
ParseFormAsync(form, ParsingCallback(&AutofillManager::FillProfileFormImpl,
field, profile, trigger_details)
.Then(NotifyNoObserversCallback()));
}
void AutofillManager::OnDidFillAutofillFormData(
const FormData& form,
const base::TimeTicks timestamp) {
if (!IsValidFormData(form))
return;
NotifyObservers(&Observer::OnBeforeDidFillAutofillFormData, form.global_id());
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
OnDidFillAutofillFormDataImpl(form, timestamp);
NotifyObservers(&Observer::OnAfterDidFillAutofillFormData,
form.global_id());
return;
}
ParseFormAsync(
form,
ParsingCallback(&AutofillManager::OnDidFillAutofillFormDataImpl,
timestamp)
.Then(NotifyObserversCallback(
&Observer::OnAfterDidFillAutofillFormData, form.global_id())));
}
void AutofillManager::OnFormSubmitted(const FormData& form,
const bool known_success,
const mojom::SubmissionSource source) {
if (!IsValidFormData(form)) {
return;
}
NotifyObservers(&Observer::OnFormSubmitted, form.global_id());
OnFormSubmittedImpl(form, known_success, source);
}
void AutofillManager::OnFormsSeen(
const std::vector<FormData>& updated_forms,
const std::vector<FormGlobalId>& removed_forms) {
// Erase forms that have been removed from the DOM. This prevents
// |form_structures_| from growing up its upper bound
// kAutofillManagerMaxFormCacheSize.
for (FormGlobalId removed_form : removed_forms)
form_structures_.erase(removed_form);
if (!IsValidFormDataVector(updated_forms) || !driver_->RendererIsAvailable())
return;
if (!ShouldParseForms()) {
return;
}
NotifyObservers(&Observer::OnBeforeFormsSeen,
GetFormGlobalIds(updated_forms));
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
std::vector<FormData> parsed_forms;
for (const FormData& form : updated_forms) {
const auto parse_form_start_time = AutofillTickClock::NowTicks();
FormStructure* cached_form_structure =
FindCachedFormById(form.global_id());
// Not updating signatures of credit card forms is legacy behaviour. We
// believe that the signatures are kept stable for voting purposes.
bool update_form_signature = false;
if (cached_form_structure) {
const DenseSet<FormType>& form_types =
cached_form_structure->GetFormTypes();
update_form_signature =
form_types.size() > form_types.count(FormType::kCreditCardForm);
}
FormStructure* form_structure = ParseForm(form, cached_form_structure);
if (!form_structure)
continue;
DCHECK(form_structure);
if (update_form_signature) {
form_structure->set_form_signature(CalculateFormSignature(form));
form_structure->set_alternative_form_signature(
CalculateAlternativeFormSignature(form));
}
parsed_forms.push_back(form);
AutofillMetrics::LogParseFormTiming(AutofillTickClock::NowTicks() -
parse_form_start_time);
}
if (!parsed_forms.empty())
OnFormsParsed(parsed_forms);
NotifyObservers(&Observer::OnAfterFormsSeen,
GetFormGlobalIds(parsed_forms));
return;
}
DCHECK(base::FeatureList::IsEnabled(features::kAutofillParseAsync));
auto ProcessParsedForms = [](AutofillManager& self,
const std::vector<FormData>& parsed_forms) {
if (!parsed_forms.empty())
self.OnFormsParsed(parsed_forms);
self.NotifyObservers(&Observer::OnAfterFormsSeen,
GetFormGlobalIds(parsed_forms));
};
ParseFormsAsync(updated_forms, base::BindOnce(ProcessParsedForms));
}
void AutofillManager::OnFormsParsed(const std::vector<FormData>& forms) {
DCHECK(!forms.empty());
OnBeforeProcessParsedForms();
driver().HandleParsedForms(forms);
std::vector<FormStructure*> non_queryable_forms;
std::vector<FormStructure*> queryable_forms;
DenseSet<FormType> form_types;
for (const FormData& form : forms) {
FormStructure* form_structure = FindCachedFormById(form.global_id());
if (!form_structure) {
NOTREACHED();
continue;
}
form_types.insert_all(form_structure->GetFormTypes());
// Configure the query encoding for this form and add it to the appropriate
// collection of forms: queryable vs non-queryable.
if (form_structure->ShouldBeQueried()) {
queryable_forms.push_back(form_structure);
} else {
non_queryable_forms.push_back(form_structure);
}
OnFormProcessed(form, *form_structure);
}
if (!queryable_forms.empty() || !non_queryable_forms.empty()) {
OnAfterProcessParsedForms(form_types);
}
// Send the current type predictions to the renderer. For non-queryable forms
// this is all the information about them that will ever be available. The
// queryable forms will be updated once the field type query is complete.
driver().SendAutofillTypePredictionsToRenderer(non_queryable_forms);
driver().SendAutofillTypePredictionsToRenderer(queryable_forms);
// Send the fields that are eligible for manual filling to the renderer. If
// server predictions are not yet available for these forms, the eligible
// fields would be updated again once they are available.
driver().SendFieldsEligibleForManualFillingToRenderer(
FormStructure::FindFieldsEligibleForManualFilling(non_queryable_forms));
driver().SendFieldsEligibleForManualFillingToRenderer(
FormStructure::FindFieldsEligibleForManualFilling(queryable_forms));
LogAutofillTypePredictionsAvailable(log_manager_, non_queryable_forms);
LogAutofillTypePredictionsAvailable(log_manager_, queryable_forms);
// Query the server if at least one of the forms was parsed.
if (!queryable_forms.empty() && download_manager()) {
NotifyObservers(&Observer::OnBeforeLoadedServerPredictions);
if (!download_manager()->StartQueryRequest(
queryable_forms, driver().IsolationInfo(), GetWeakPtr())) {
NotifyObservers(&Observer::OnAfterLoadedServerPredictions);
}
}
}
void AutofillManager::OnTextFieldDidChange(const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
const base::TimeTicks timestamp) {
if (!IsValidFormData(form) || !IsValidFormFieldData(field))
return;
NotifyObservers(&Observer::OnBeforeTextFieldDidChange, form.global_id(),
field.global_id());
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
OnTextFieldDidChangeImpl(form, field, bounding_box, timestamp);
NotifyObservers(&Observer::OnAfterTextFieldDidChange, form.global_id(),
field.global_id());
return;
}
ParseFormAsync(
form,
ParsingCallback(&AutofillManager::OnTextFieldDidChangeImpl, field,
bounding_box, timestamp)
.Then(NotifyObserversCallback(&Observer::OnAfterTextFieldDidChange,
form.global_id(), field.global_id())));
}
void AutofillManager::OnTextFieldDidScroll(const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) {
if (!IsValidFormData(form) || !IsValidFormFieldData(field))
return;
NotifyObservers(&Observer::OnBeforeTextFieldDidScroll, form.global_id(),
field.global_id());
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
OnTextFieldDidScrollImpl(form, field, bounding_box);
NotifyObservers(&Observer::OnAfterTextFieldDidScroll, form.global_id(),
field.global_id());
return;
}
ParseFormAsync(
form,
ParsingCallback(&AutofillManager::OnTextFieldDidScrollImpl, field,
bounding_box)
.Then(NotifyObserversCallback(&Observer::OnAfterTextFieldDidScroll,
form.global_id(), field.global_id())));
}
void AutofillManager::OnSelectControlDidChange(const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) {
if (!IsValidFormData(form) || !IsValidFormFieldData(field))
return;
NotifyObservers(&Observer::OnBeforeSelectControlDidChange, form.global_id(),
field.global_id());
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
OnSelectControlDidChangeImpl(form, field, bounding_box);
NotifyObservers(&Observer::OnAfterSelectControlDidChange, form.global_id(),
field.global_id());
return;
}
ParseFormAsync(
form, ParsingCallback(&AutofillManager::OnSelectControlDidChangeImpl,
field, bounding_box)
.Then(NotifyObserversCallback(
&Observer::OnAfterSelectControlDidChange, form.global_id(),
field.global_id())));
}
void AutofillManager::OnAskForValuesToFill(
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
AutofillSuggestionTriggerSource trigger_source) {
if (!IsValidFormData(form) || !IsValidFormFieldData(field))
return;
NotifyObservers(&Observer::OnBeforeAskForValuesToFill, form.global_id(),
field.global_id());
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
OnAskForValuesToFillImpl(form, field, bounding_box, trigger_source);
NotifyObservers(&Observer::OnAfterAskForValuesToFill, form.global_id(),
field.global_id());
return;
}
ParseFormAsync(
form,
ParsingCallback(&AutofillManager::OnAskForValuesToFillImpl, field,
bounding_box, trigger_source)
.Then(NotifyObserversCallback(&Observer::OnAfterAskForValuesToFill,
form.global_id(), field.global_id())));
}
void AutofillManager::OnFocusOnFormField(const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box) {
if (!IsValidFormData(form) || !IsValidFormFieldData(field))
return;
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
OnFocusOnFormFieldImpl(form, field, bounding_box);
return;
}
ParseFormAsync(form, ParsingCallback(&AutofillManager::OnFocusOnFormFieldImpl,
field, bounding_box)
.Then(NotifyNoObserversCallback()));
}
void AutofillManager::OnFocusNoLongerOnForm(bool had_interacted_form) {
OnFocusNoLongerOnFormImpl(had_interacted_form);
}
void AutofillManager::OnDidEndTextFieldEditing() {
OnDidEndTextFieldEditingImpl();
}
void AutofillManager::OnHidePopup() {
OnHidePopupImpl();
}
void AutofillManager::OnPopupHidden() {
driver().PopupHidden();
NotifyObservers(&Observer::OnSuggestionsHidden);
}
void AutofillManager::OnSelectOrSelectListFieldOptionsDidChange(
const FormData& form) {
if (!IsValidFormData(form))
return;
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
OnSelectOrSelectListFieldOptionsDidChangeImpl(form);
return;
}
ParseFormAsync(
form, ParsingCallback(
&AutofillManager::OnSelectOrSelectListFieldOptionsDidChangeImpl)
.Then(NotifyNoObserversCallback()));
}
void AutofillManager::OnJavaScriptChangedAutofilledValue(
const FormData& form,
const FormFieldData& field,
const std::u16string& old_value) {
if (!IsValidFormData(form))
return;
NotifyObservers(&Observer::OnBeforeJavaScriptChangedAutofilledValue,
form.global_id(), field.global_id());
if (!base::FeatureList::IsEnabled(features::kAutofillParseAsync)) {
OnJavaScriptChangedAutofilledValueImpl(form, field, old_value);
NotifyObservers(&Observer::OnAfterJavaScriptChangedAutofilledValue,
form.global_id(), field.global_id());
return;
}
ParseFormAsync(
form,
ParsingCallback(&AutofillManager::OnJavaScriptChangedAutofilledValueImpl,
field, old_value)
.Then(NotifyObserversCallback(
&Observer::OnAfterJavaScriptChangedAutofilledValue,
form.global_id(), field.global_id())));
}
// Returns true if |live_form| does not match |cached_form|.
bool AutofillManager::GetCachedFormAndField(const FormData& form,
const FormFieldData& field,
FormStructure** form_structure,
AutofillField** autofill_field) {
// Maybe find an existing FormStructure that corresponds to |form|.
FormStructure* cached_form = FindCachedFormById(form.global_id());
if (cached_form) {
if (base::FeatureList::IsEnabled(features::kAutofillParseAsync) ||
!CachedFormNeedsUpdate(form, *cached_form)) {
// There is no data to return if there are no auto-fillable fields.
if (!cached_form->autofill_count())
return false;
// Return the cached form and matching field, if any.
*form_structure = cached_form;
*autofill_field = FindAutofillFillField(**form_structure, field);
return *autofill_field != nullptr;
}
}
if (base::FeatureList::IsEnabled(features::kAutofillParseAsync))
return false;
// The form is new or updated, parse it and discard |cached_form|.
// i.e., |cached_form| is no longer valid after this call.
*form_structure = ParseForm(form, cached_form);
if (!*form_structure)
return false;
// Annotate the updated form with its predicted types.
driver().SendAutofillTypePredictionsToRenderer({*form_structure});
// Update the renderer with the latest set of fields eligible for manual
// filling.
driver().SendFieldsEligibleForManualFillingToRenderer(
FormStructure::FindFieldsEligibleForManualFilling({*form_structure}));
// There is no data to return if there are no auto-fillable fields.
if (!(*form_structure)->autofill_count())
return false;
// Find the AutofillField that corresponds to |field|.
*autofill_field = FindAutofillFillField(**form_structure, field);
return *autofill_field != nullptr;
}
std::unique_ptr<AutofillMetrics::FormInteractionsUkmLogger>
AutofillManager::CreateFormInteractionsUkmLogger() {
return std::make_unique<AutofillMetrics::FormInteractionsUkmLogger>(
&unsafe_client(), unsafe_client().GetUkmRecorder());
}
size_t AutofillManager::FindCachedFormsBySignature(
FormSignature form_signature,
std::vector<FormStructure*>* form_structures) const {
size_t hits_num = 0;
for (const auto& [form_id, form_structure] : form_structures_) {
if (form_structure->form_signature() == form_signature) {
++hits_num;
if (form_structures)
form_structures->push_back(form_structure.get());
}
}
return hits_num;
}
FormStructure* AutofillManager::FindCachedFormById(FormGlobalId form_id) const {
auto it = form_structures_.find(form_id);
return it != form_structures_.end() ? it->second.get() : nullptr;
}
bool AutofillManager::CanShowAutofillUi() const {
return driver_->CanShowAutofillUi();
}
void AutofillManager::TriggerFormExtractionInAllFrames(
base::OnceCallback<void(bool success)> form_extraction_finished_callback) {
driver_->TriggerFormExtractionInAllFrames(
std::move(form_extraction_finished_callback));
}
void AutofillManager::ParseFormsAsync(
const std::vector<FormData>& forms,
base::OnceCallback<void(AutofillManager&, const std::vector<FormData>&)>
callback) {
SCOPED_UMA_HISTOGRAM_TIMER("Autofill.Timing.ParseFormsAsync");
DCHECK(base::FeatureList::IsEnabled(features::kAutofillParseAsync));
// `num_managed_forms` is the number of forms that will be managed by this
// AutofillManager after ParseFormsAsync() and its asynchronous callees have
// finished.
size_t num_managed_forms = form_structures_.size();
// To be run on the main thread (accesses member variables).
std::vector<FormData> parsed_forms;
std::vector<std::unique_ptr<FormStructure>> form_structures;
for (const FormData& form_data : forms) {
bool is_new_form = !base::Contains(form_structures_, form_data.global_id());
if (num_managed_forms + is_new_form > kAutofillManagerMaxFormCacheSize) {
LOG_AF(log_manager_) << LoggingScope::kAbortParsing
<< LogMessage::kAbortParsingTooManyForms
<< form_data;
continue;
}
auto form_structure = std::make_unique<FormStructure>(form_data);
if (!form_structure->ShouldBeParsed(log_manager_))
continue;
num_managed_forms += is_new_form;
DCHECK_LE(num_managed_forms, kAutofillManagerMaxFormCacheSize);
if (FormStructure* cached_form_structure =
FindCachedFormById(form_data.global_id())) {
// We need to keep the server data if available. We need to use them while
// determining the heuristics.
form_structure->RetrieveFromCache(
*cached_form_structure,
FormStructure::RetrieveFromCacheReason::kFormParsing);
// Not updating signatures of credit card forms is legacy behaviour. We
// believe that the signatures are kept stable for voting purposes.
DenseSet<FormType> form_types = cached_form_structure->GetFormTypes();
if (form_types.size() > form_types.count(FormType::kCreditCardForm)) {
form_structure->set_form_signature(CalculateFormSignature(form_data));
form_structure->set_alternative_form_signature(
CalculateAlternativeFormSignature(form_data));
}
}
form_structure->set_current_page_language(GetCurrentPageLanguage());
form_structures.push_back(std::move(form_structure));
parsed_forms.push_back(form_data);
}
// Remove duplicates by their FormGlobalId. Otherwise, after moving the forms
// into `form_structures_`, duplicates may be destroyed and we'd end up with
// dangling pointers.
base::ranges::sort(form_structures, {}, &FormStructure::global_id);
form_structures.erase(
base::ranges::unique(form_structures, {}, &FormStructure::global_id),
form_structures.end());
struct AsyncContext {
AsyncContext(std::vector<std::unique_ptr<FormStructure>> form_structures,
GeoIpCountryCode country_code,
LogManager* log_manager)
: form_structures(std::move(form_structures)),
country_code(std::move(country_code)),
log_manager(IsLoggingActive(log_manager)
? LogManager::CreateBuffering()
: nullptr) {}
std::vector<std::unique_ptr<FormStructure>> form_structures;
GeoIpCountryCode country_code;
std::unique_ptr<BufferingLogManager> log_manager;
};
// To be run on a different task (must not access global or member
// variables).
// TODO(crbug.com/1309848): We can't pass a UKM logger because it's a member
// variable. To be fixed.
auto run_heuristics = [](AsyncContext context) {
SCOPED_UMA_HISTOGRAM_TIMER("Autofill.Timing.ParseFormsAsync.RunHeuristics");
for (auto& form_structure : context.form_structures) {
form_structure->DetermineHeuristicTypes(
context.country_code,
/*form_interactions_ukm_logger=*/nullptr, context.log_manager.get());
}
return context;
};
// To be run on the main thread (accesses member variables).
auto update_cache = base::BindOnce(
[](base::WeakPtr<AutofillManager> self,
base::OnceCallback<void(AutofillManager&,
const std::vector<FormData>&)> callback,
const std::vector<FormData>& parsed_forms, AsyncContext context) {
SCOPED_UMA_HISTOGRAM_TIMER(
"Autofill.Timing.ParseFormsAsync.UpdateCache");
if (!self) {
return;
}
if (context.log_manager && self->log_manager_) {
context.log_manager->Flush(*self->log_manager_);
}
for (auto& form_structure : context.form_structures) {
FormGlobalId id = form_structure->global_id();
self->form_structures_[id] = std::move(form_structure);
self->NotifyObservers(
&Observer::OnFieldTypesDetermined, id,
Observer::FieldTypeSource::kHeuristicsOrAutocomplete);
}
std::move(callback).Run(*self, parsed_forms);
},
parsing_weak_ptr_factory_.GetWeakPtr(), std::move(callback),
parsed_forms);
// To be run on the main thread (accesses member variables).
auto run_heuristics_and_update_cache = base::BindOnce(
[](base::WeakPtr<AutofillManager> self,
AsyncContext (*run_heuristics)(AsyncContext),
base::OnceCallback<void(AsyncContext)> update_cache,
std::vector<std::unique_ptr<FormStructure>> forms) {
if (!self) {
return;
}
self->parsing_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
run_heuristics,
AsyncContext(std::move(forms),
self->client().GetVariationConfigCountryCode(),
self->log_manager_)),
std::move(update_cache));
},
parsing_weak_ptr_factory_.GetWeakPtr(), run_heuristics,
std::move(update_cache));
#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
// Run ML Model before running heuristics to ensure that
// rationalization and sectioning are done.
if (auto* ml_handler = client().GetAutofillMlPredictionModelHandler()) {
ml_handler->GetModelPredictionsForForms(
std::move(form_structures), std::move(run_heuristics_and_update_cache));
return;
}
#endif
std::move(run_heuristics_and_update_cache).Run(std::move(form_structures));
}
void AutofillManager::ParseFormAsync(
const FormData& form_data,
base::OnceCallback<void(AutofillManager&, const FormData&)> callback) {
SCOPED_UMA_HISTOGRAM_TIMER("Autofill.Timing.ParseFormAsync");
DCHECK(base::FeatureList::IsEnabled(features::kAutofillParseAsync));
bool is_new_form = !base::Contains(form_structures_, form_data.global_id());
if (form_structures_.size() + is_new_form >
kAutofillManagerMaxFormCacheSize) {
LOG_AF(log_manager_) << LoggingScope::kAbortParsing
<< LogMessage::kAbortParsingTooManyForms << form_data;
return;
}
auto form_structure = std::make_unique<FormStructure>(form_data);
if (!form_structure->ShouldBeParsed(log_manager_)) {
// For Autocomplete, events need to be handled even for forms that cannot be
// parsed.
std::move(callback).Run(*this, form_data);
return;
}
if (FormStructure* cached_form_structure =
FindCachedFormById(form_data.global_id())) {
if (!CachedFormNeedsUpdate(form_data, *cached_form_structure)) {
std::move(callback).Run(*this, form_data);
return;
}
// We need to keep the server data if available. We need to use them while
// determining the heuristics.
form_structure->RetrieveFromCache(
*cached_form_structure,
FormStructure::RetrieveFromCacheReason::kFormParsing);
}
form_structure->set_current_page_language(GetCurrentPageLanguage());
struct AsyncContext {
AsyncContext(std::unique_ptr<FormStructure> form_structure,
GeoIpCountryCode country_code,
LogManager* log_manager)
: form_structure(std::move(form_structure)),
country_code(std::move(country_code)),
log_manager(IsLoggingActive(log_manager)
? LogManager::CreateBuffering()
: nullptr) {}
std::unique_ptr<FormStructure> form_structure;
GeoIpCountryCode country_code;
std::unique_ptr<BufferingLogManager> log_manager;
};
// To be run on a different task (must not access global or member
// variables).
// TODO(crbug.com/1309848): We can't pass a UKM logger because it's a member
// variable. To be fixed.
auto run_heuristics = [](AsyncContext context) {
SCOPED_UMA_HISTOGRAM_TIMER("Autofill.Timing.ParseFormAsync.RunHeuristics");
context.form_structure->DetermineHeuristicTypes(
context.country_code,
/*form_interactions_ukm_logger=*/nullptr, context.log_manager.get());
return context;
};
// To be run on the main thread (accesses member variables).
// The reason this takes both `form_data` and `form_structure` is that they
// may disagree on the form's values: if the form is seen for the second time,
// RetrieveFromCache() resets the `form_structure`'s fields.
// TODO(crbug/1345089): Make FormStructure's and FormData's fields correspond,
// migrate all event handlers in BrowserAutofillManager take a FormStructure,
// and drop the FormData from UpdateCache().
auto update_cache = base::BindOnce(
[](base::WeakPtr<AutofillManager> self,
base::OnceCallback<void(AutofillManager&, const FormData&)> callback,
const FormData& form_data, AsyncContext context) {
SCOPED_UMA_HISTOGRAM_TIMER(
"Autofill.Timing.ParseFormAsync.UpdateCache");
if (!self) {
return;
}
if (context.log_manager && self->log_manager_) {
context.log_manager->Flush(*self->log_manager_);
}
FormGlobalId id = context.form_structure->global_id();
self->form_structures_[id] = std::move(context.form_structure);
self->NotifyObservers(
&Observer::OnFieldTypesDetermined, id,
Observer::FieldTypeSource::kHeuristicsOrAutocomplete);
std::move(callback).Run(*self, form_data);
},
parsing_weak_ptr_factory_.GetWeakPtr(), std::move(callback), form_data);
// To be run on the main thread (accesses member variables).
auto run_heuristics_and_update_cache = base::BindOnce(
[](base::WeakPtr<AutofillManager> self,
AsyncContext (*run_heuristics)(AsyncContext),
base::OnceCallback<void(AsyncContext)> update_cache,
std::unique_ptr<FormStructure> form) {
if (!self) {
return;
}
self->parsing_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(
run_heuristics,
AsyncContext(std::move(form),
self->client().GetVariationConfigCountryCode(),
self->log_manager_)),
std::move(update_cache));
},
parsing_weak_ptr_factory_.GetWeakPtr(), run_heuristics,
std::move(update_cache));
#if BUILDFLAG(BUILD_WITH_TFLITE_LIB)
// Run ML Model before running heuristics to ensure that
// rationalization and sectioning are done.
if (auto* ml_handler = client().GetAutofillMlPredictionModelHandler()) {
ml_handler->GetModelPredictionsForForm(
std::move(form_structure), std::move(run_heuristics_and_update_cache));
return;
}
#endif
std::move(run_heuristics_and_update_cache).Run(std::move(form_structure));
}
FormStructure* AutofillManager::ParseForm(const FormData& form,
const FormStructure* cached_form) {
DCHECK(!base::FeatureList::IsEnabled(features::kAutofillParseAsync));
if (form_structures_.size() >= kAutofillManagerMaxFormCacheSize) {
LOG_AF(log_manager_) << LoggingScope::kAbortParsing
<< LogMessage::kAbortParsingTooManyForms << form;
return nullptr;
}
auto form_structure = std::make_unique<FormStructure>(form);
if (!form_structure->ShouldBeParsed(log_manager_))
return nullptr;
if (cached_form) {
// We need to keep the server data if available. We need to use them while
// determining the heuristics.
form_structure->RetrieveFromCache(
*cached_form, FormStructure::RetrieveFromCacheReason::kFormParsing);
}
form_structure->set_current_page_language(GetCurrentPageLanguage());
form_structure->DetermineHeuristicTypes(
client().GetVariationConfigCountryCode(), form_interactions_ukm_logger(),
log_manager_);
// Hold the parsed_form_structure we intend to return. We can use this to
// reference the form_signature when transferring ownership below.
FormStructure* parsed_form_structure = form_structure.get();
// Ownership is transferred to |form_structures_| which maintains it until
// the form is parsed again or the AutofillManager is destroyed.
//
// Note that this insert/update takes ownership of the new form structure
// and also destroys the previously cached form structure.
form_structures_[parsed_form_structure->global_id()] =
std::move(form_structure);
NotifyObservers(&Observer::OnFieldTypesDetermined,
parsed_form_structure->global_id(),
Observer::FieldTypeSource::kHeuristicsOrAutocomplete);
return parsed_form_structure;
}
void AutofillManager::Reset() {
parsing_weak_ptr_factory_.InvalidateWeakPtrs();
NotifyObservers(&Observer::OnAutofillManagerReset);
form_structures_.clear();
form_interactions_ukm_logger_ = CreateFormInteractionsUkmLogger();
}
void AutofillManager::OnLoadedServerPredictions(
std::string response,
const std::vector<FormSignature>& queried_form_signatures) {
// Get the current valid FormStructures represented by
// |queried_form_signatures|.
std::vector<FormStructure*> queried_forms;
queried_forms.reserve(queried_form_signatures.size());
for (const auto& form_signature : queried_form_signatures) {
FindCachedFormsBySignature(form_signature, &queried_forms);
}
// Each form signature in |queried_form_signatures| is supposed to be unique,
// and therefore appear only once. This ensures that
// FindCachedFormsBySignature() produces an output without duplicates in the
// forms.
// TODO(crbug/1064709): |queried_forms| could be a set data structure; their
// order should be irrelevant.
DCHECK_EQ(queried_forms.size(),
std::set<FormStructure*>(queried_forms.begin(), queried_forms.end())
.size());
// If there are no current forms corresponding to the queried signatures, drop
// the query response.
if (queried_forms.empty()) {
NotifyObservers(&Observer::OnAfterLoadedServerPredictions);
return;
}
// Parse and store the server predictions.
FormStructure::ParseApiQueryResponse(
std::move(response), queried_forms, queried_form_signatures,
form_interactions_ukm_logger(), log_manager_);
// Will log quality metrics for each FormStructure based on the presence of
// autocomplete attributes, if available.
if (auto* logger = form_interactions_ukm_logger()) {
for (FormStructure* cur_form : queried_forms) {
autofill_metrics::LogQualityMetricsBasedOnAutocomplete(*cur_form, logger);
}
}
// Send field type predictions to the renderer so that it can possibly
// annotate forms with the predicted types or add console warnings.
driver().SendAutofillTypePredictionsToRenderer(queried_forms);
driver().SendFieldsEligibleForManualFillingToRenderer(
FormStructure::FindFieldsEligibleForManualFilling(queried_forms));
LogAutofillTypePredictionsAvailable(log_manager_, queried_forms);
for (const FormStructure* form : queried_forms) {
NotifyObservers(&Observer::OnFieldTypesDetermined, form->global_id(),
Observer::FieldTypeSource::kAutofillServer);
}
NotifyObservers(&Observer::OnAfterLoadedServerPredictions);
}
} // namespace autofill