blob: e763102a8f9faf3f6e061acb7658b2dcde70c745 [file] [log] [blame]
// Copyright 2021 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_BROWSER_CONTENT_AUTOFILL_ROUTER_H_
#define COMPONENTS_AUTOFILL_CONTENT_BROWSER_CONTENT_AUTOFILL_ROUTER_H_
#include <string>
#include <utility>
#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "components/autofill/content/browser/form_forest.h"
#include "components/autofill/core/browser/autofill_driver.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_data_predictions.h"
#include "components/autofill/core/common/form_field_data.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_widget_host.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/geometry/rect_f.h"
namespace autofill {
class ContentAutofillDriver;
// ContentAutofillRouter routes events between ContentAutofillDriver objects in
// order to handle frame-transcending forms.
//
// A *frame-transcending* form is a form whose fields live in different frames.
// For example, credit card forms often have the credit card number field in an
// iframe hosted by a payment service provider.
//
// A frame-transcending form therefore consists of multiple *renderer forms*.
// ContentAutofillRouter *flattens* these forms into a single *browser form*,
// and maps all events concerning the renderer forms to that browser form, and
// vice versa.
//
// That way, the collection of renderer forms appears as one ordinary form to
// the browser.
//
// For example, consider the following pseudo HTML code:
// <html>
// <form id="Form-1">
// <input id="Field-1">
// <iframe id="Frame-1">
// <input id="Field-2">
// </iframe>
// <iframe id="Frame-2">
// <iframe id="Frame-3">
// <form id="Form-2">
// <input id="Field-3">
// </form>
// <form id="Form-3">
// <input id="Field-4">
// </form>
// </iframe>
// </iframe>
// <input id="Field-5">
// </form>
//
// Forms can be actual <form> elements or synthetic forms: <input>, <select>,
// and <iframe> elements that are not in the scope of any <form> belong to the
// enclosing frame's synthetic form.
//
// The five renderer forms are therefore, in pseudo C++ code:
// FormData{
// .host_frame = "Frame-0", // The main frame.
// .name = "Form-1",
// .fields = { "Field-1", "Field-5" },
// .child_frames = { "Frame-1", "Frame-2" }
// }
// FormData{
// .host_frame = "Frame-1",
// .name = "synthetic",
// .fields = { "Field-2" },
// .child_frames = { }
// }
// FormData{
// .host_frame = "Frame-2",
// .name = "synthetic",
// .fields = { },
// .child_frames = { "Frame-3" }
// }
// FormData{
// .host_frame = "Frame-3",
// .name = "Form-2",
// .fields = { "Field-3" },
// .child_frames = { }
// }
// FormData{
// .host_frame = "Frame-3",
// .name = "Form-3",
// .fields = { "Field-4" },
// .child_frames = { }
// }
//
// The browser form of these renderer forms is obtained by flattening the fields
// into the root form:
// FormData{
// .name = "Form-1",
// .fields = { "Field-1", "Field-2", "Field-3", "Field-4", "Field-5" }
// }
//
// Let AutofillAgent-N, ContentAutofillRouter-N, and AutofillManager-N
// correspond to the Frame-N. ContentAutofillRouter would route an event
// concerning any of the forms in Frame-3 from ContentAutofillDriver-3 to
// ContentAutofillDriver-0:
//
// +---Tab---+ +---Tab----+ +----Tab----+
// | Agent-0 | +---> | Driver-0 | ---------> | Manager-0 |
// | | | | | | |
// | Agent-1 | | | Driver-1 | | Manager-1 |
// | | | | | | |
// | Agent-2 | | | Driver-2 | | Manager-2 |
// | | | | | | |
// | Agent-3 | -----|---> | Driver-3 | -----+ | Manager-3 |
// +---------+ | +----------+ | +-----------+
// | |
// | +--Tab---+ |
// +----- | Router | <-----+
// +--------+
//
// If the event name is `f`, the control flow is as follows:
// Driver-3's ContentAutofillDriver::f(args...) calls
// Router's ContentAutofillRouter::f(this, args..., callback) calls
// Driver-0's ContentAutofillDriver::callback(args...).
//
// Every function in ContentAutofillRouter takes a |source| parameter, which
// points to the ContentAutofillDriver that triggered the event. In events
// triggered by the renderer, the source driver is the driver the associated
// renderer form originates from.
//
// See ContentAutofillDriver for details on the naming pattern and an example.
//
// See FormForest for details on (un)flattening.
class ContentAutofillRouter {
public:
ContentAutofillRouter();
ContentAutofillRouter(const ContentAutofillRouter&) = delete;
ContentAutofillRouter& operator=(const ContentAutofillRouter&) = delete;
~ContentAutofillRouter();
// Deletes all forms and fields related to |driver| (and this driver only).
// Must be called whenever |driver| is destroyed.
// As a simple performance optimization, if |driver| is a main frame, the
// whole router is reset to the initial state.
void UnregisterDriver(ContentAutofillDriver* driver);
// Returns the ContentAutofillDriver* for which AskForValuesToFill() was
// called last.
// TODO(crbug.com/1224846) QueryFormFieldAutofill() was renamed to
// AskForValuesToFill(), so we should rename last_queried_source() and
// last_queried_source_ as well.
ContentAutofillDriver* last_queried_source() const {
return last_queried_source_;
}
// Registers the key-press handler with the driver that last called
// AskForValuesToFill(), that is, |last_queried_source_|.
void SetKeyPressHandler(
ContentAutofillDriver* source,
const content::RenderWidgetHost::KeyPressEventCallback& handler,
void (*callback)(
ContentAutofillDriver* target,
const content::RenderWidgetHost::KeyPressEventCallback& handler));
void UnsetKeyPressHandler(ContentAutofillDriver* source,
void (*callback)(ContentAutofillDriver* target));
// Sets the suppress state in the driver that last called
// AskForValuesToFill(), that is, |last_queried_source_|.
void SetShouldSuppressKeyboard(ContentAutofillDriver* source,
bool suppress,
void (*callback)(ContentAutofillDriver* target,
bool suppress));
// Routing of events called by the renderer:
void SetFormToBeProbablySubmitted(
ContentAutofillDriver* source,
absl::optional<FormData> form,
void (*callback)(ContentAutofillDriver* target,
const FormData* optional_form));
void FormsSeen(
ContentAutofillDriver* source,
std::vector<FormData> updated_forms,
const std::vector<FormGlobalId>& removed_forms,
void (*callback)(ContentAutofillDriver* target,
const std::vector<FormData>& updated_forms,
const std::vector<FormGlobalId>& removed_forms));
void FormSubmitted(
ContentAutofillDriver* source,
FormData form,
bool known_success,
mojom::SubmissionSource submission_source,
void (*callback)(ContentAutofillDriver* target,
const FormData& form,
bool known_success,
mojom::SubmissionSource submission_source));
void TextFieldDidChange(ContentAutofillDriver* source,
FormData form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
base::TimeTicks timestamp,
void (*callback)(ContentAutofillDriver* target,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
base::TimeTicks timestamp));
void TextFieldDidScroll(ContentAutofillDriver* source,
FormData form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
void (*callback)(ContentAutofillDriver* target,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box));
void SelectControlDidChange(ContentAutofillDriver* source,
FormData form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
void (*callback)(ContentAutofillDriver* target,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box));
void AskForValuesToFill(
ContentAutofillDriver* source,
FormData form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
AutoselectFirstSuggestion autoselect_first_suggestion,
FormElementWasClicked form_element_was_clicked,
void (*callback)(ContentAutofillDriver* target,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
AutoselectFirstSuggestion autoselect_first_suggestion,
FormElementWasClicked form_element_was_clicked));
void HidePopup(ContentAutofillDriver* source,
void (*callback)(ContentAutofillDriver* target));
void FocusNoLongerOnForm(ContentAutofillDriver* source,
bool had_interacted_form,
void (*callback)(ContentAutofillDriver* target,
bool had_interacted_form));
void FocusOnFormField(ContentAutofillDriver* source,
FormData form,
const FormFieldData& field,
const gfx::RectF& bounding_box,
void (*callback)(ContentAutofillDriver* target,
const FormData& form,
const FormFieldData& field,
const gfx::RectF& bounding_box));
void DidFillAutofillFormData(ContentAutofillDriver* source,
FormData form,
base::TimeTicks timestamp,
void (*callback)(ContentAutofillDriver* target,
const FormData& form,
base::TimeTicks timestamp));
void DidPreviewAutofillFormData(
ContentAutofillDriver* source,
void (*callback)(ContentAutofillDriver* target));
void DidEndTextFieldEditing(ContentAutofillDriver* source,
void (*callback)(ContentAutofillDriver* target));
void SelectFieldOptionsDidChange(
ContentAutofillDriver* source,
FormData form,
void (*callback)(ContentAutofillDriver* target, const FormData& form));
void JavaScriptChangedAutofilledValue(
ContentAutofillDriver* source,
FormData form,
const FormFieldData& field,
const std::u16string& old_value,
void (*callback)(ContentAutofillDriver* target,
const FormData& form,
const FormFieldData& field,
const std::u16string& old_value));
// Event called when the context menu is opened on a field.
void OnContextMenuShownInField(
ContentAutofillDriver* source,
const FormGlobalId& form_global_id,
const FieldGlobalId& field_global_id,
void (*callback)(ContentAutofillDriver* target,
const FormGlobalId& form_global_id,
const FieldGlobalId& field_global_id));
// Routing of events called by the browser:
std::vector<FieldGlobalId> FillOrPreviewForm(
ContentAutofillDriver* source,
mojom::RendererFormDataAction action,
const FormData& data,
const url::Origin& triggered_origin,
const base::flat_map<FieldGlobalId, ServerFieldType>& field_type_map,
void (*callback)(ContentAutofillDriver* target,
mojom::RendererFormDataAction action,
const FormData& form));
void SendAutofillTypePredictionsToRenderer(
ContentAutofillDriver* source,
const std::vector<FormDataPredictions>& type_predictions,
void (*callback)(ContentAutofillDriver* target,
const std::vector<FormDataPredictions>& predictions));
void SendFieldsEligibleForManualFillingToRenderer(
ContentAutofillDriver* source,
const std::vector<FieldGlobalId>& fields,
void (*callback)(ContentAutofillDriver* target,
const std::vector<FieldRendererId>& fields));
void RendererShouldAcceptDataListSuggestion(
ContentAutofillDriver* source,
const FieldGlobalId& field,
const std::u16string& value,
void (*callback)(ContentAutofillDriver* target,
const FieldRendererId& field,
const std::u16string& value));
void RendererShouldClearFilledSection(
ContentAutofillDriver* source,
void (*callback)(ContentAutofillDriver* target));
void RendererShouldClearPreviewedForm(
ContentAutofillDriver* source,
void (*callback)(ContentAutofillDriver* target));
void RendererShouldFillFieldWithValue(
ContentAutofillDriver* source,
const FieldGlobalId& field,
const std::u16string& value,
void (*callback)(ContentAutofillDriver* target,
const FieldRendererId& field,
const std::u16string& value));
void RendererShouldPreviewFieldWithValue(
ContentAutofillDriver* source,
const FieldGlobalId& field,
const std::u16string& value,
void (*callback)(ContentAutofillDriver* target,
const FieldRendererId& field,
const std::u16string& value));
void RendererShouldSetSuggestionAvailability(
ContentAutofillDriver* source,
const FieldGlobalId& field,
const mojom::AutofillState state,
void (*callback)(ContentAutofillDriver* target,
const FieldRendererId& field,
const mojom::AutofillState state));
private:
friend class ContentAutofillRouterTestApi;
// Returns the driver of |frame| stored in |form_forest_|.
ContentAutofillDriver* DriverOfFrame(LocalFrameToken frame);
// Calls ContentAutofillDriver::TriggerReparse() for all drivers in
// |form_forest_| except for |exception|.
void TriggerReparseExcept(ContentAutofillDriver* exception);
// Update the last queried and source and do cleanup work.
void SetLastQueriedSource(ContentAutofillDriver* source);
void SetLastQueriedTarget(ContentAutofillDriver* target);
// The URL of a main frame managed by the ContentAutofillRouter.
// TODO(crbug.com/1240247): Remove.
std::string MainUrlForDebugging() const;
// The frame managed by the ContentAutofillRouter that was last passed to
// an event.
// TODO(crbug.com/1240247): Remove.
content::GlobalRenderFrameHostId some_rfh_for_debugging_;
// The forest of forms. See its documentation for the usage protocol.
internal::FormForest form_forest_;
// The driver that triggered the last AskForValuesToFill() call.
// Update with SetLastQueriedSource().
raw_ptr<ContentAutofillDriver> last_queried_source_ = nullptr;
// The driver to which the last AskForValuesToFill() call was routed.
// Update with SetLastQueriedTarget().
raw_ptr<ContentAutofillDriver> last_queried_target_ = nullptr;
// When the focus moves to a different frame, the order of the events
// FocusNoLongerOnForm() and FocusOnFormField() may be reversed due to race
// conditions. We use these members to correct the order of the events.
LocalFrameToken focused_frame_;
bool focus_no_longer_on_form_has_fired_ = true;
};
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CONTENT_BROWSER_CONTENT_AUTOFILL_ROUTER_H_