blob: 28fc860d07a94401f3fcb64ba3c6c7925fc4021b [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_AUTOFILL_CONTENT_RENDERER_AUTOFILL_AGENT_H_
#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_AUTOFILL_AGENT_H_
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <variant>
#include <vector>
#include "base/compiler_specific.h"
#include "base/feature_list.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/types/optional_ref.h"
#include "base/types/strong_alias.h"
#include "components/autofill/content/common/mojom/autofill_agent.mojom.h"
#include "components/autofill/content/common/mojom/autofill_driver.mojom.h"
#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/form_cache.h"
#include "components/autofill/content/renderer/form_tracker.h"
#include "components/autofill/content/renderer/timing.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/dense_set.h"
#include "components/autofill/core/common/field_data_manager.h"
#include "components/autofill/core/common/form_data_predictions.h"
#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
#include "components/autofill/core/common/unique_ids.h"
#include "content/public/renderer/render_frame_observer.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_registry.h"
#include "third_party/blink/public/web/web_autofill_client.h"
#include "third_party/blink/public/web/web_autofill_state.h"
#include "third_party/blink/public/web/web_element.h"
#include "third_party/blink/public/web/web_form_control_element.h"
#include "third_party/blink/public/web/web_form_element.h"
#include "third_party/blink/public/web/web_input_element.h"
#include "ui/accessibility/ax_mode.h"
namespace blink {
class WebFormControlElement;
class WebFormElement;
} // namespace blink
namespace autofill {
class PasswordAutofillAgent;
class PasswordGenerationAgent;
// AutofillAgent deals with Autofill related communications between Blink and
// the browser.
//
// Each AutofillAgent is associated with exactly one RenderFrame and
// communicates with exactly one ContentAutofillDriver throughout its entire
// lifetime.
//
// AutofillAgent is deleted asynchronously because it may itself take action
// that (via JavaScript) causes the associated RenderFrame's deletion.
// AutofillAgent is pending deletion between OnDestruct() and ~AutofillAgent().
// To handle this state, care must be taken to check for nullptrs:
// - `unsafe_autofill_driver()`
// - `unsafe_render_frame()`
// - `GetDocument()`
//
// This RenderFrame owns all forms and fields in the renderer-browser
// communication:
// - AutofillAgent may assume that forms and fields received in the
// mojom::AutofillAgent events are owned by that RenderFrame.
// - Conversely, the forms and fields which AutofillAgent passes to
// mojom::AutofillDriver events must be owned by that RenderFrame.
//
// Note that Autofill encompasses:
// - single text field suggestions, that we usually refer to as Autocomplete,
// - password form fill, referred to as Password Autofill, and
// - entire form fill based on one field entry, referred to as Form Autofill.
class AutofillAgent : public content::RenderFrameObserver,
public blink::WebAutofillClient,
public mojom::AutofillAgent {
public:
static constexpr base::TimeDelta kFormsSeenThrottle = base::Milliseconds(100);
using ExtractAllDatalists =
base::StrongAlias<class ExtractAllDatalistsTag, bool>;
using FocusRequiresScroll =
base::StrongAlias<class FocusRequiresScrollTag, bool>;
using QueryPasswordSuggestions =
base::StrongAlias<class QueryPasswordSuggestionsTag, bool>;
using SecureContextRequired =
base::StrongAlias<class SecureContextRequiredTag, bool>;
using UserGestureRequired = FormTracker::UserGestureRequired;
using UsesKeyboardAccessoryForSuggestions =
base::StrongAlias<class UsesKeyboardAccessoryForSuggestionsTag, bool>;
struct Config {
// Controls whether or not all datalists shall be extracted into
// FormFieldData. This feature is enabled when all datalists (instead of
// only the focused one) shall be extracted and sent to the Android Autofill
// service when the autofill session is created.
ExtractAllDatalists extract_all_datalists{false};
// Controls whether to delay focus handling until scrolling occurs.
FocusRequiresScroll focus_requires_scroll{true};
// Controls whether password suggestions are queried programmatically. This
// is required if the `PasswordAutofillAgent` does not handle password
// forms and `AutofillDriver` should be informed instead.
QueryPasswordSuggestions query_password_suggestions{false};
// Controls whether a secure context is required to query Autofill
// suggestions.
SecureContextRequired secure_context_required{false};
// Controls whether `FormTracker` requires a user gesture in order to pass
// on information about text field change events to `AutofillAgent`.
// Bypassing the user gesture check may be required when delegating to
// Android Autofill, which needs to be notified of every change to the
// field.
UserGestureRequired user_gesture_required{true};
// Is true iff the platform doesn't show any popups but renders the same
// information in or near the keyboard instead.
UsesKeyboardAccessoryForSuggestions uses_keyboard_accessory_for_suggestions{
BUILDFLAG(IS_ANDROID)};
};
// PasswordAutofillAgent is guaranteed to outlive AutofillAgent.
// PasswordGenerationAgent and AutofillAssistantAgent may be nullptr. If they
// are not, then they are also guaranteed to outlive AutofillAgent.
AutofillAgent(
content::RenderFrame* render_frame,
std::unique_ptr<PasswordAutofillAgent> password_autofill_agent,
std::unique_ptr<PasswordGenerationAgent> password_generation_agent,
blink::AssociatedInterfaceRegistry* registry);
AutofillAgent(const AutofillAgent&) = delete;
AutofillAgent& operator=(const AutofillAgent&) = delete;
~AutofillAgent() override;
void BindPendingReceiver(
mojo::PendingAssociatedReceiver<mojom::AutofillAgent> pending_receiver);
blink::WebDocument GetDocument() const;
// Callers must not store the returned value longer than a function scope.
// unsafe_autofill_driver() is nullptr if unsafe_render_frame() is nullptr and
// the `autofill_driver_` has not been bound yet.
mojom::AutofillDriver* unsafe_autofill_driver();
CallTimerState GetCallTimerState(CallTimerState::CallSite call_site) const;
// mojom::AutofillAgent:
void TriggerFormExtraction() override;
void TriggerFormExtractionWithResponse(
base::OnceCallback<void(bool)> callback) override;
void ApplyFieldsAction(
mojom::FormActionType action_type,
mojom::ActionPersistence action_persistence,
const std::vector<FormFieldData::FillData>& fields) override;
void ApplyFieldAction(mojom::FieldActionType action_type,
mojom::ActionPersistence action_persistence,
FieldRendererId field_id,
const std::u16string& value) override;
void ExtractForm(FormRendererId form,
base::OnceCallback<void(const std::optional<FormData>&)>
callback) override;
void ExtractLabeledTextNodeValue(
const std::u16string& value_regex,
const std::u16string& label_regex,
uint32_t number_of_ancestor_levels_to_search,
base::OnceCallback<void(const std::string&)> callback) override;
void ExposeDomNodeIds() override;
void FieldTypePredictionsAvailable(
const std::vector<FormDataPredictions>& forms) override;
// Besides cases that "actually" clear the form, this function needs to be
// called before all filling operations. This is because filled fields are no
// longer considered previewed - and any state tied to the preview needs to
// be reset.
void ClearPreviewedForm() override;
void TriggerSuggestions(
FieldRendererId field_id,
AutofillSuggestionTriggerSource trigger_source) override;
void SetSuggestionAvailability(
FieldRendererId field_id,
mojom::AutofillSuggestionAvailability suggestion_availability) override;
void AcceptDataListSuggestion(FieldRendererId field_id,
const std::u16string& suggested_value) override;
void PreviewPasswordSuggestion(const std::u16string& username,
const std::u16string& password) override;
void PreviewPasswordGenerationSuggestion(
const std::u16string& password) override;
void GetPotentialLastFourCombinationsForStandaloneCvc(
base::OnceCallback<void(const std::vector<std::string>&)>
potential_matches) override;
void DispatchEmailVerifiedEvent(
FieldRendererId field_id,
const std::string& presentation_token) override;
// Called after updating the last interacted element in FormTracker because of
// `reason`. It is always the case that `form` or `element` are non-null. If
// `form_element` is non-null, then `element` (if non-null) is owned by
// `form_element`, otherwise `element` is unowned and is surely non-null.
// TODO(crbug.com/40281981): Remove.
void OnProvisionallySaveForm(const blink::WebFormElement& form,
const blink::WebFormControlElement& element,
FormTracker::SaveFormReason reason);
void OnFormSubmission(
mojom::SubmissionSource source,
std::optional<blink::WebFormElement> submitted_form_element);
// Instructs `form_tracker_` to track the autofilled `element`.
void TrackAutofilledElement(const blink::WebFormControlElement& element);
// Function that should be called whenever the value of `element` changes due
// to user input. This is separate from OnTextFieldValueChanged() as that
// function may trigger UI and should only be called when other UI won't be
// shown. `form_cache` can be used to optimize form extractions occurring
// synchronously after this function call.
void UpdateStateForTextChange(const blink::WebFormControlElement& element,
FieldPropertiesFlags flag,
const SynchronousFormCache& form_cache);
bool IsPrerendering() const;
blink::WebFormControlElement last_queried_element() const {
return last_queried_element_.GetField();
}
FieldDataManager& field_data_manager() const {
return *field_data_manager_.get();
}
form_util::ButtonTitlesCache* button_titles_cache() {
return &button_titles_cache_;
}
protected:
// blink::WebAutofillClient:
// Signals from blink that a form related element changed dynamically,
// passing the changed element as well as the type of the change.
// TODO(crbug.com/40281981): Fire the signal for elements that become hidden.
void DidChangeFormRelatedElementDynamically(
const blink::WebElement&,
blink::WebFormRelatedChangeType) override;
// content::RenderFrameObserver:
void DidCommitProvisionalLoad(ui::PageTransition transition) override;
void DidCreateDocumentElement() override;
void DidDispatchDOMContentLoadedEvent() override;
void DidChangeScrollOffset() override;
void AccessibilityModeChanged(const ui::AXMode& mode) override;
void OnDestruct() override;
// This function fires `FocusOnFormField()` xor `FocusOnNonFormField()`:
// - It calls `FocusOnFormField()` iff the newly focused element is a non-null
// `WebFormControlElement` or a non-null contenteditable whose `FormData`
// can be extracted.
// - It calls `FocusOnNonFormField()` iff it does not call
// `FocusOnFormField()`.
// See crbug.com/337690061 for details.
void FocusedElementChanged(
const blink::WebElement& new_focused_element) override;
private:
class DeferringAutofillDriver;
friend class AutofillAgentTestApi;
// The RenderFrame* is nullptr while the AutofillAgent is pending deletion,
// between OnDestruct() and ~AutofillAgent().
content::RenderFrame* unsafe_render_frame() const {
return content::RenderFrameObserver::render_frame();
}
// Use unsafe_render_frame() instead.
template <typename T = int>
content::RenderFrame* render_frame(T* = 0) const {
static_assert(
std::is_void_v<T>,
"Beware that the RenderFrame may become nullptr by OnDestruct() "
"because AutofillAgent destructs itself asynchronously. Use "
"unsafe_render_frame() instead and make test that it is non-nullptr.");
}
// To be called when all forms are irretrievably gone, e.g., when a new
// document is loaded.
void Reset();
// Fires Mojo messages for a given form submission.
void FireHostSubmitEvents(const FormData& form_data,
mojom::SubmissionSource source);
// Tries to show the given `passwords_request` for the given fields and update
// `is_popup_possibly_visible` accordingly. Returns true if the password agent
// handles the request.
bool TryShowPasswordSuggestions(
const blink::WebInputElement& input,
IsPasswordRequestManuallyTriggered manually_triggered_password_request,
base::optional_ref<const PasswordSuggestionRequest> password_request);
// blink::WebAutofillClient:
void TextFieldCleared(const blink::WebFormControlElement&) override;
void TextFieldDidEndEditing(const blink::WebInputElement& element) override;
void TextFieldValueChanged(
const blink::WebFormControlElement& element) override;
void ContentEditableDidChange(const blink::WebElement& element) override;
void TextFieldDidReceiveKeyDown(
const blink::WebInputElement& element,
const blink::WebKeyboardEvent& event) override;
void OpenTextDataListChooser(const blink::WebInputElement& element) override;
void DataListOptionsChanged(const blink::WebInputElement& element) override;
void UserGestureObserved() override;
void AjaxSucceeded() override;
void JavaScriptChangedValue(blink::WebFormControlElement element,
const blink::WebString& old_value,
bool was_autofilled) override;
void DidCompleteFocusChangeInFrame() override;
void DidReceiveLeftMouseDownOrGestureTapInNode(
const blink::WebNode& node) override;
void SelectFieldOptionsChanged(
const blink::WebFormControlElement& element) override;
void SelectControlSelectionChanged(
const blink::WebFormControlElement& element) override;
void FormElementReset(const blink::WebFormElement& form) override;
void PasswordFieldReset(const blink::WebInputElement& element) override;
void EmitFormIssuesToDevtools() override;
// Starts observing the caret in the given element. Previous observers are
// implicitly deleted. The event handler is HandleCaretMovedInFormField().
void ObserveCaret(blink::WebElement element);
// Calls CaretMovedInFormField() in a throttled manner.
//
// If HandleCaretMovedInFormField() has been called in the past 100 ms,
// CaretMovedInFormField() is (re-)scheduled to be fired in 100 ms. Otherwise,
// it is fired synchronously.
//
// Thus, the first event is fired immediately, but fast successive events are
// throttled until no event has been fired for 200 ms.
void HandleCaretMovedInFormField(blink::WebElement element,
blink::WebDOMEvent event);
void HandleFocusChangeComplete(bool focused_node_was_last_clicked,
const SynchronousFormCache& form_cache);
// TODO(crbug.com/376628389): Remove.
void OnTextFieldValueChanged(const blink::WebFormControlElement& element,
const SynchronousFormCache& form_cache);
void OnSelectControlSelectionChanged(
const blink::WebFormControlElement& element,
const SynchronousFormCache& form_cache);
void DidChangeScrollOffsetImpl(FieldRendererId element_id);
// At least on Android, multiple AskForValuesToFill() events may be fired in
// short succession. Since getting the event handling right in AutofillAgent
// is difficult we ignore duplicate AskForValuesToFill() as a workaround.
// See crbug.com/40284788 for details.
bool ShouldThrottleAskForValuesToFill(FieldRendererId field);
// Shows Password Manager, password generation, or Autofill suggestions for
// `element`. This call is asynchronous and may or may not lead to the showing
// of a suggestion popup (no popup is shown if there are no available
// suggestions). `form_cache` can be used to optimize form extractions
// occurring synchronously after this function call.
void ShowSuggestions(
const blink::WebFormControlElement& element,
AutofillSuggestionTriggerSource trigger_source,
const SynchronousFormCache& form_cache,
const std::optional<PasswordSuggestionRequest>& password_request);
// Shows Autofill suggestions for `element` if `element` is a contenteditable.
void ShowSuggestionsForContentEditable(
const blink::WebElement& element,
AutofillSuggestionTriggerSource trigger_source);
// Set `element` to display the given `value`.
void DoFillFieldWithValue(std::u16string_view value,
blink::WebFormControlElement& element,
blink::WebAutofillState autofill_state);
// Notifies the AutofillDriver in the browser process of new and/or removed
// forms, modulo throttling.
//
// Throttling means that the actual work -- that is, extracting the forms and
// invoking AutofillDriver::FormsSeen() -- is delayed by (at least) 100 ms.
// All subsequent calls within the next (at least) 100 ms return early.
//
// Calls `callback(true)` asynchronously after the timer is completed.
// Otherwise, calls `callback(false)` immediately.
void ExtractForms(base::OneShotTimer& timer,
base::OnceCallback<void(bool)> callback);
// This function can be implemented through the one above, but it exists to
// avoid memory allocation for the OnceCallback state. Allocation and
// destruction of this callback in the hot path (when timer is already
// running) is expensive.
// Called when `element` is added/reassociated dynamically in the DOM.
void ExtractFormsAndNotifyPasswordAutofillAgent(
base::OneShotTimer& timer,
const blink::WebElement& element);
void ExtractFormsUnthrottled(base::OnceCallback<void(bool)> callback,
const CallTimerState& timer_state);
// Hides any currently showing Autofill popup.
void HidePopup();
// Returns an approximation of the submitted form. The candidates are:
// - `provisionally_saved_form_` , because it may be the last-known complete
// state of the form (i.e., the form or some fields in the form may have
// been removed afterwards).
// - `last_interacted_form_`'s current `FormData`, because this corresponds to
// the last form element the user interacted with.
// - `submitted_form_element`'s current `FormData`, because the caller
// specified that this is the form element that was submitted, regardless
// of autofill's tracking.
// When `submitted_form_element` is provided the function makes sure
// that the returned form corresponds to that DOM element.
// `source` is the type of submission requesting the submitted form.
std::optional<FormData> GetSubmittedForm(
mojom::SubmissionSource source,
std::optional<blink::WebFormElement> submitted_form_element);
void ResetLastInteractedElements();
// A form_id means that the user last interacted with a FormElement.
// A field_id means that the user last interacted with a formless control.
void UpdateLastInteractedElement(
std::variant<FormRendererId, FieldRendererId> element_id);
// Called when current form is no longer submittable, submitted_forms_ is
// cleared in this method.
void OnFormNoLongerSubmittable();
// Helpers for SelectFieldOptionsChanged() and
// DataListOptionsChanged(), which get called after a timer that is restarted
// when another event of the same type started.
void BatchSelectOptionChange(FieldRendererId element_id);
void BatchDataListOptionChange(FieldRendererId element_id);
FormRef last_interacted_form() const {
return form_tracker_->last_interacted_form();
}
// TODO(crbug.com/40281981): Remove.
std::optional<FormData>& provisionally_saved_form() {
return form_tracker_->provisionally_saved_form();
}
const std::optional<FormData>& provisionally_saved_form() const {
return form_tracker_->provisionally_saved_form();
}
// Stores immutable configuration this agent was created with. It contains
// features and settings that are specific to the client using this agent.
const Config config_;
// Contains the forms of the document.
FormCache form_cache_{this};
std::unique_ptr<PasswordAutofillAgent> password_autofill_agent_;
std::unique_ptr<PasswordGenerationAgent> password_generation_agent_;
// The element corresponding to the last request sent for form field Autofill.
FieldRef last_queried_element_;
// List of elements that are currently being previewed, along with their
// autofill state before the preview.
std::vector<std::pair<FieldRendererId, blink::WebAutofillState>>
previewed_elements_;
// When dealing with an unowned form, we keep track of the unowned fields
// the user has modified so we can determine when submission occurs.
// An additional sufficient condition for the form submission detection is
// that the form has been autofilled.
std::set<FieldRendererId> formless_elements_user_edited_;
bool formless_elements_were_autofilled_ = false;
// For each form, identified by its renderer ID, keeps track of the sources of
// observed submissions, so that we avoid firing duplicate submission signals
// to the driver. See `AutofillAgent::FireHostSubmitEvent` for more details.
base::flat_map<FormRendererId, DenseSet<mojom::SubmissionSource>>
submitted_forms_;
// Whether the Autofill popup is possibly visible. This is tracked as a
// performance improvement, so that the IPC channel isn't flooded with
// messages to close the Autofill popup when it can't possibly be showing.
bool is_popup_possibly_visible_ = false;
bool last_left_mouse_down_or_gesture_tap_in_node_caused_focus_ = false;
// This is never null, it is created at construction time and is not changed
// until destruction time.
std::unique_ptr<FormTracker> form_tracker_ =
std::make_unique<FormTracker>(unsafe_render_frame(), *this);
mojo::AssociatedReceiver<mojom::AutofillAgent> receiver_{this};
mojo::AssociatedRemote<mojom::AutofillDriver> autofill_driver_;
// For deferring messages to the browser process while prerendering.
std::unique_ptr<DeferringAutofillDriver> deferring_autofill_driver_;
bool was_last_action_fill_ = false;
// Timers for throttling handling of frequent events.
base::OneShotTimer select_option_change_batch_timer_;
base::OneShotTimer datalist_option_change_batch_timer_;
// TODO(crbug.com/40267764): Merge some or all of these timers?
base::OneShotTimer process_forms_after_dynamic_change_timer_;
base::OneShotTimer process_forms_form_extraction_timer_;
base::OneShotTimer process_forms_form_extraction_with_response_timer_;
// True iff DidDispatchDOMContentLoadedEvent() fired since the last
// navigation.
bool is_dom_content_loaded_ = false;
// Will be set when accessibility mode changes, depending on what the new mode
// is.
bool is_screen_reader_enabled_ = false;
// Map WebFormControlElement to the pair of:
// 1) The most recent text that user typed or autofilled in input elements.
// Used for storing credit card number/username/password before JavaScript
// changes them.
// 2) Field properties mask, i.e. whether the field was autofilled, modified
// by user, etc. (see FieldPropertiesMask).
scoped_refptr<FieldDataManager> field_data_manager_ =
base::MakeRefCounted<FieldDataManager>();
// Stores the mapping from a form element's ID to results of button titles
// heuristics for that form.
form_util::ButtonTitlesCache button_titles_cache_;
// State for, and only for, HandleFocusChangeComplete().
struct Caret {
private:
friend void AutofillAgent::ObserveCaret(blink::WebElement element);
friend void AutofillAgent::HandleCaretMovedInFormField(
blink::WebElement element,
blink::WebDOMEvent event);
// Removes the caret listener from the currently observed WebElement (if
// any).
base::ScopedClosureRunner remove_listener;
// The last time a CaretMovedInFormField().
base::Time time_of_last_event;
// The timer for the next CaretMovedInFormField().
base::OneShotTimer timer;
} caret_state_;
struct {
base::TimeTicks last_autofill_agent_reset = base::TimeTicks::Now();
base::TimeTicks last_dom_content_loaded;
} timing_;
struct {
base::TimeTicks time;
FieldRendererId field = {};
} last_ask_for_values_to_fill_;
const bool replace_form_element_observer_ = false;
base::WeakPtrFactory<AutofillAgent> weak_ptr_factory_{this};
};
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_AUTOFILL_AGENT_H_