| // 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/android_autofill/browser/android_autofill_manager.h" |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/check_op.h" |
| #include "base/containers/contains.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/notreached.h" |
| #include "components/android_autofill/browser/android_form_event_logger.h" |
| #include "components/android_autofill/browser/autofill_provider.h" |
| #include "components/android_autofill/browser/autofill_type_util.h" |
| #include "components/autofill/content/browser/content_autofill_driver.h" |
| #include "components/autofill/core/browser/field_types.h" |
| #include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h" |
| #include "components/autofill/core/common/unique_ids.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| |
| namespace autofill { |
| |
| using base::TimeTicks; |
| |
| AndroidAutofillManager::AndroidAutofillManager(AutofillDriver* driver) |
| : AutofillManager(driver) { |
| StartNewLoggingSession(); |
| autofill_manager_observation.Observe(this); |
| } |
| |
| AndroidAutofillManager::~AndroidAutofillManager() { |
| Reset(); |
| } |
| |
| base::WeakPtr<AutofillManager> AndroidAutofillManager::GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| bool AndroidAutofillManager::ShouldClearPreviewedForm() { |
| return false; |
| } |
| |
| void AndroidAutofillManager::OnFormSubmittedImpl( |
| const FormData& form, |
| mojom::SubmissionSource source) { |
| address_logger_->OnWillSubmitForm(); |
| loyalty_card_logger_->OnWillSubmitForm(); |
| payments_logger_->OnWillSubmitForm(); |
| password_logger_->OnWillSubmitForm(); |
| if (auto* provider = GetAutofillProvider()) |
| provider->OnFormSubmitted(this, form, source); |
| } |
| |
| void AndroidAutofillManager::OnTextFieldValueChangedImpl( |
| const FormData& form, |
| const FieldGlobalId& field_id, |
| const TimeTicks timestamp) { |
| auto* provider = GetAutofillProvider(); |
| if (!provider) { |
| return; |
| } |
| const FormFieldData* field = form.FindFieldByGlobalId(field_id); |
| if (!field) { |
| return; |
| } |
| |
| // We cannot use `field` is_autofilled state because it has already been |
| // cleared by blink. Check `provider` cache. |
| bool cached_is_autofilled = provider->GetCachedIsAutofilled(*field); |
| |
| provider->OnTextFieldValueChanged(this, form, *field, timestamp); |
| |
| if (auto* logger = GetEventFormLogger(form.global_id(), field_id)) { |
| if (cached_is_autofilled) { |
| logger->OnEditedAutofilledField(); |
| } else { |
| logger->OnTypedIntoNonFilledField(); |
| } |
| } |
| } |
| |
| void AndroidAutofillManager::OnTextFieldDidScrollImpl( |
| const FormData& form, |
| const FieldGlobalId& field_id) { |
| if (auto* provider = GetAutofillProvider()) |
| if (const FormFieldData* field = form.FindFieldByGlobalId(field_id)) { |
| provider->OnTextFieldDidScroll(this, form, *field); |
| } |
| } |
| |
| void AndroidAutofillManager::OnAskForValuesToFillImpl( |
| const FormData& form, |
| const FieldGlobalId& field_id, |
| const gfx::Rect& caret_bounds, |
| AutofillSuggestionTriggerSource trigger_source, |
| std::optional<PasswordSuggestionRequest> password_request) { |
| auto* provider = GetAutofillProvider(); |
| if (!provider) { |
| return; |
| } |
| const FormFieldData* field = form.FindFieldByGlobalId(field_id); |
| if (!field) { |
| return; |
| } |
| |
| provider->OnAskForValuesToFill(this, form, *field, trigger_source); |
| |
| if (auto* logger = GetEventFormLogger(form.global_id(), field_id)) { |
| logger->OnDidInteractWithAutofillableForm(); |
| } |
| } |
| |
| void AndroidAutofillManager::OnFocusOnFormFieldImpl( |
| const FormData& form, |
| const FieldGlobalId& field_id) { |
| if (auto* provider = GetAutofillProvider()) { |
| if (const FormFieldData* field = form.FindFieldByGlobalId(field_id)) { |
| provider->OnFocusOnFormField(this, form, *field); |
| } |
| } |
| } |
| |
| void AndroidAutofillManager::OnSelectControlSelectionChangedImpl( |
| const FormData& form, |
| const FieldGlobalId& field_id) { |
| if (auto* provider = GetAutofillProvider()) { |
| if (const FormFieldData* field = form.FindFieldByGlobalId(field_id)) { |
| provider->OnSelectControlSelectionChanged(this, form, *field); |
| } |
| } |
| } |
| |
| bool AndroidAutofillManager::ShouldParseForms() { |
| return true; |
| } |
| |
| void AndroidAutofillManager::OnFocusOnNonFormFieldImpl() { |
| if (auto* provider = GetAutofillProvider()) |
| provider->OnFocusOnNonFormField(this); |
| } |
| |
| void AndroidAutofillManager::OnDidFillAutofillFormDataImpl( |
| const FormData& form, |
| const base::TimeTicks timestamp) { |
| if (auto* provider = GetAutofillProvider()) |
| provider->OnDidFillAutofillFormData(this, form, timestamp); |
| } |
| |
| void AndroidAutofillManager::OnHidePopupImpl() { |
| if (auto* provider = GetAutofillProvider()) |
| provider->OnHidePopup(this); |
| } |
| |
| void AndroidAutofillManager::OnFormProcessed( |
| const FormData& form, |
| const FormStructure& form_structure) { |
| DenseSet<FormType> form_types = form_structure.GetFormTypes(); |
| for (FormType form_type : form_types) { |
| if (auto* logger = GetEventFormLogger(form_type)) { |
| logger->OnDidParseForm(); |
| } |
| } |
| } |
| |
| void AndroidAutofillManager::Reset() { |
| // Inform the provider before resetting state in case it needs to access it. |
| if (auto* rfh = |
| static_cast<ContentAutofillDriver&>(driver()).render_frame_host()) { |
| if (auto* web_contents = content::WebContents::FromRenderFrameHost(rfh)) { |
| if (auto* provider = AutofillProvider::FromWebContents(web_contents)) { |
| // Note that this doesn't use `GetAutofillProvider()` because we might |
| // need to reset even when `rfh` is pending deletion. |
| provider->OnManagerResetOrDestroyed(this); |
| } |
| } |
| } |
| AutofillManager::Reset(); |
| forms_with_server_predictions_.clear(); |
| StartNewLoggingSession(); |
| } |
| |
| void AndroidAutofillManager::OnFieldTypesDetermined(AutofillManager& manager, |
| FormGlobalId form, |
| FieldTypeSource source) { |
| CHECK_EQ(&manager, this); |
| switch (source) { |
| case FieldTypeSource::kAutofillAiModel: |
| case FieldTypeSource::kAutofillServer: |
| forms_with_server_predictions_.insert(form); |
| if (auto* provider = GetAutofillProvider()) { |
| provider->OnServerPredictionsAvailable(*this, form); |
| } |
| break; |
| case FieldTypeSource::kHeuristicsOrAutocomplete: |
| break; |
| } |
| } |
| |
| AutofillProvider* AndroidAutofillManager::GetAutofillProvider() { |
| if (auto* rfh = |
| static_cast<ContentAutofillDriver&>(driver()).render_frame_host()) { |
| if (rfh->IsActive()) { |
| if (auto* web_contents = content::WebContents::FromRenderFrameHost(rfh)) { |
| return AutofillProvider::FromWebContents(web_contents); |
| } |
| } |
| } |
| return nullptr; |
| } |
| |
| FieldTypeGroup AndroidAutofillManager::ComputeFieldTypeGroupForField( |
| const FormGlobalId& form_id, |
| const FieldGlobalId& field_id) { |
| FormStructure* form_structure = nullptr; |
| AutofillField* autofill_field = nullptr; |
| if (!GetCachedFormAndField(form_id, field_id, &form_structure, |
| &autofill_field)) { |
| return FieldTypeGroup::kNoGroup; |
| } |
| return GroupTypeOfFieldType(GetMostRelevantFieldType(autofill_field->Type())); |
| } |
| |
| void AndroidAutofillManager::FillOrPreviewForm( |
| mojom::ActionPersistence action_persistence, |
| FormData form, |
| FieldTypeGroup field_type_group, |
| const url::Origin& triggered_origin) { |
| DCHECK_EQ(action_persistence, mojom::ActionPersistence::kFill); |
| |
| std::vector<FormFieldData> fields = form.ExtractFields(); |
| std::erase_if(fields, [&](const FormFieldData& field) { |
| // The renderer doesn't fill such fields, and therefore they can be removed |
| // from here to reduce IPC traffic and avoid accidental filling. |
| return !field.is_autofilled() || field.value().empty(); |
| }); |
| |
| driver().ApplyFormAction(mojom::FormActionType::kFill, action_persistence, |
| fields, triggered_origin, /*field_type_map=*/{}, |
| /*section_for_clear_form_on_ios=*/Section()); |
| // We do not call OnAutofillProfileOrCreditCardFormFilled() because WebView |
| // doesn't have AutofillProfile or CreditCard. |
| if (auto* logger = GetEventFormLogger(field_type_group)) { |
| logger->OnDidFillSuggestion(); |
| } |
| } |
| |
| void AndroidAutofillManager::StartNewLoggingSession() { |
| address_logger_ = std::make_unique<AndroidFormEventLogger>("Address"); |
| loyalty_card_logger_ = |
| std::make_unique<AndroidFormEventLogger>("LoyaltyCard"); |
| payments_logger_ = std::make_unique<AndroidFormEventLogger>("CreditCard"); |
| password_logger_ = std::make_unique<AndroidFormEventLogger>("Password"); |
| } |
| |
| AndroidFormEventLogger* AndroidAutofillManager::GetEventFormLogger( |
| const FormGlobalId& form_id, |
| const FieldGlobalId& field_id) { |
| return GetEventFormLogger(ComputeFieldTypeGroupForField(form_id, field_id)); |
| } |
| |
| AndroidFormEventLogger* AndroidAutofillManager::GetEventFormLogger( |
| FieldTypeGroup group) { |
| return GetEventFormLogger(FieldTypeGroupToFormType(group)); |
| } |
| |
| AndroidFormEventLogger* AndroidAutofillManager::GetEventFormLogger( |
| FormType form_type) { |
| switch (form_type) { |
| case FormType::kAddressForm: |
| return address_logger_.get(); |
| case FormType::kLoyaltyCardForm: |
| return loyalty_card_logger_.get(); |
| case FormType::kCreditCardForm: |
| case FormType::kStandaloneCvcForm: |
| return payments_logger_.get(); |
| case FormType::kPasswordForm: |
| return password_logger_.get(); |
| case FormType::kUnknownFormType: |
| return nullptr; |
| } |
| NOTREACHED(); |
| } |
| |
| } // namespace autofill |