blob: f2bed71aa34b09264d0dd8cf17448f380131404e [file] [log] [blame]
// Copyright 2014 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/content/browser/content_autofill_driver.h"
#include <concepts>
#include <functional>
#include <type_traits>
#include <utility>
#include <variant>
#include "base/barrier_callback.h"
#include "base/functional/callback.h"
#include "components/autofill/content/browser/bad_message.h"
#include "components/autofill/content/browser/content_autofill_driver_factory.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/foundations/autofill_client.h"
#include "components/autofill/core/browser/foundations/autofill_driver_router.h"
#include "components/autofill/core/common/aliases.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_data_predictions.h"
#include "components/autofill/core/common/signatures.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "mojo/public/cpp/bindings/message.h"
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "url/origin.h"
namespace autofill {
namespace {
template <typename T, typename... Ts>
concept AnyOf =
(std::same_as<std::remove_cvref_t<T>, std::remove_cvref_t<Ts>> || ...);
// Lift() elevates data from the a renderer process to the browser process.
// Every data received from a renderer should immediately be lifted.
//
// Lifting includes, for example, setting security-critical data like frame
// tokens and origins of Form[Field]Data. Another example is transforming
// coordinates from the originating frame's space to the top-level frame.
// No-op: add types to the `requires` clause below as necessary.
template <typename T>
requires(std::is_scalar_v<std::remove_cvref_t<T>> ||
AnyOf<T,
bool,
AutofillDriverRouter::RoutedCallback<>,
base::TimeTicks,
std::u16string>)
T&& Lift(ContentAutofillDriver& source, T&& x) {
return std::forward<T>(x);
}
gfx::Rect Lift(ContentAutofillDriver& source, gfx::Rect r) {
if (content::RenderWidgetHostView* view =
source.render_frame_host()->GetView()) {
r.set_origin(view->TransformPointToRootCoordSpace(r.origin()));
}
return r;
}
gfx::RectF Lift(ContentAutofillDriver& source, gfx::RectF r) {
if (content::RenderWidgetHostView* view =
source.render_frame_host()->GetView()) {
r.set_origin(view->TransformPointToRootCoordSpaceF(r.origin()));
}
return r;
}
FormData Lift(ContentAutofillDriver& source, FormData form) {
content::RenderFrameHost& rfh = *source.render_frame_host();
form.set_host_frame(source.GetFrameToken());
form.set_main_frame_origin(rfh.GetMainFrame()->GetLastCommittedOrigin());
// GetLastCommittedURL() doesn't include URL updates due to
// document.open() and so it might be about:blank or about:srcdoc. In this
// case fallback to GetLastCommittedOrigin(). See crbug.com/1209270.
GURL unstripped_url = rfh.GetLastCommittedURL();
if (unstripped_url.SchemeIs(url::kAboutScheme)) {
unstripped_url = rfh.GetLastCommittedOrigin().GetURL();
}
form.set_url(StripAuthAndParams(unstripped_url));
form.set_full_url(StripAuth(unstripped_url));
// The form signature must be calculated after setting FormData::url.
FormSignature signature = CalculateFormSignature(form);
std::vector<FormFieldData> fields = form.ExtractFields();
for (FormFieldData& field : fields) {
field.set_host_frame(form.host_frame());
field.set_host_form_id(form.renderer_id());
field.set_host_form_signature(signature);
field.set_origin(rfh.GetLastCommittedOrigin());
field.set_bounds(Lift(source, field.bounds()));
}
form.set_fields(std::move(fields));
return form;
}
FormGlobalId Lift(ContentAutofillDriver& source, FormRendererId id) {
return FormGlobalId(source.GetFrameToken(), id);
}
FieldGlobalId Lift(ContentAutofillDriver& source, FieldRendererId id) {
return FieldGlobalId(source.GetFrameToken(), id);
}
PasswordSuggestionRequest Lift(ContentAutofillDriver& source,
PasswordSuggestionRequest request) {
// These indices are equal to fields().size() for manual requests.
if (request.username_field_index > request.form_data.fields().size() ||
request.password_field_index > request.form_data.fields().size()) {
mojo::ReportBadMessage(
"username_field_index or password_field_index cannot be greater than "
"form.fields.size()!");
}
request.form_data = Lift(source, std::move(request.form_data));
request.field.bounds = Lift(source, std::move(request.field.bounds));
return request;
}
template <typename T>
std::optional<T> Lift(ContentAutofillDriver& source, std::optional<T> x) {
std::optional<T> y;
if (x) {
y.emplace(Lift(source, *std::move(x)));
}
return y;
}
template <typename T>
auto Lift(ContentAutofillDriver& source, const std::vector<T>& xs) {
std::vector<std::remove_cvref_t<decltype(Lift(source, xs.front()))>> ys;
ys.reserve(xs.size());
for (const auto& x : xs) {
ys.push_back(Lift(source, x));
}
return ys;
}
template <typename... Args>
base::OnceCallback<void(Args...)> Lift(ContentAutofillDriver& source,
base::OnceCallback<void(Args...)> cb) {
return base::BindOnce(
[](raw_ref<ContentAutofillDriver> source,
base::OnceCallback<void(Args...)> cb, Args... args) {
std::move(cb).Run(Lift(*source, std::forward<Args>(args))...);
},
raw_ref(source), std::move(cb));
}
// WithNewVersion() bumps the FormData::version of each form. This should be
// called for every browser form before it enters AutofillManager so that
// AutofillManager can distinguish newer and older forms.
//
// TODO(crbug.com/40144964): Remove once FormData objects aren't stored
// globally anymore.
// No-op: add types to the `requires` clause below as necessary.
template <typename T>
requires(std::is_scalar_v<std::remove_cvref_t<T>> ||
AnyOf<T,
bool,
FieldGlobalId,
base::TimeTicks,
gfx::Rect,
std::u16string,
std::vector<FormGlobalId>>)
T&& WithNewVersion(T&& x) {
return std::forward<T>(x);
}
auto& WithNewVersion(const FormData& browser_form) {
static FormVersion version_counter;
++*version_counter;
// This const_cast is a hack to avoid additional copies. It's OK because the
// FormData is owned by AutofillDriverRouter, FormData::version is written
// only here and read only in AutofillManager.
const_cast<FormData&>(browser_form).set_version(version_counter);
return browser_form;
}
auto& WithNewVersion(const std::optional<FormData>& browser_form) {
if (browser_form) {
WithNewVersion(*browser_form);
}
return browser_form;
}
auto& WithNewVersion(const std::vector<FormData>& browser_forms) {
for (const FormData& form : browser_forms) {
WithNewVersion(form);
}
return browser_forms;
}
std::optional<PasswordSuggestionRequest> WithNewVersion(
std::optional<PasswordSuggestionRequest> password_request) {
if (password_request) {
WithNewVersion(password_request->form_data);
}
return password_request;
}
template <typename... Args>
base::OnceCallback<void(Args...)> WithNewVersion(
base::OnceCallback<void(Args...)> cb) {
return base::BindOnce(
[](base::OnceCallback<void(Args...)> cb, Args... args) {
std::move(cb).Run(WithNewVersion(std::forward<Args>(args))...);
},
std::move(cb));
}
// Routes an event from the browser to one or multiple AutofillAgents.
//
// Routing converts values and types: for example, browser forms become renderer
// forms, and FieldGlobalIds become FieldRendererIds.
template <typename R,
typename... RouterArgs,
typename... AgentArgs,
typename... ActualArgs>
R RouteToAgent(AutofillDriverRouter& router,
R (AutofillDriverRouter::*router_fun)(
AutofillDriverRouter::RoutedCallback<AgentArgs...>,
RouterArgs...),
void (mojom::AutofillAgent::*agent_fun)(AgentArgs...),
ActualArgs&&... args) {
return (router.*router_fun)(
[&agent_fun](autofill::AutofillDriver& target, AgentArgs... args) {
if (!target.IsActive()) {
// We early-return rather than crashing or killing the renderer
// because Autofill might want to communicate with a frame that just
// became inactive due to race conditions. See crbug.com/345195973.
LOG(WARNING) << "Skipped Autofill message for inactive frame";
return;
}
mojom::AutofillAgent& agent =
*static_cast<ContentAutofillDriver&>(target).GetAutofillAgent();
(agent.*agent_fun)(std::forward<AgentArgs>(args)...);
},
std::forward<ActualArgs>(args)...);
}
// Routes an event from the renderer to one or multiple AutofillManagers.
//
// Routing converts values and types: for example, renderer forms become browser
// forms, and FieldRendererIds become FieldGlobalIds. Additionally, this
// function takes care of some necessary pre- and postprocessing: for example,
// it sets FormData::frame_token and transforms graphical coordinates, and bumps
// the browser form's FormData::version.
template <typename... RouterArgs,
typename... ManagerArgs,
typename... ActualArgs>
void RouteToManager(ContentAutofillDriver& source,
AutofillDriverRouter& router,
void (AutofillDriverRouter::*router_fun)(
AutofillDriverRouter::RoutedCallback<ManagerArgs...>,
autofill::AutofillDriver& source,
RouterArgs...),
void (AutofillManager::*manager_fun)(ManagerArgs...),
ActualArgs&&... args) {
if (!bad_message::CheckArgs(args...) ||
!bad_message::CheckFrameNotPrerendering(source.render_frame_host())) {
return;
}
return (router.*router_fun)(
[&manager_fun](autofill::AutofillDriver& target, ManagerArgs... args) {
AutofillManager& manager = target.GetAutofillManager();
(manager.*
manager_fun)(WithNewVersion(std::forward<ManagerArgs>(args))...);
},
source, Lift(source, std::forward<ActualArgs>(args))...);
}
} // namespace
ContentAutofillDriver::ContentAutofillDriver(
content::RenderFrameHost* render_frame_host,
ContentAutofillDriverFactory* owner)
: render_frame_host_(*render_frame_host), owner_(*owner) {
autofill_manager_ = GetAutofillClient().CreateManager(/*pass_key=*/{}, *this);
}
ContentAutofillDriver::~ContentAutofillDriver() {
owner_->router().UnregisterDriver(*this, /*driver_is_dying=*/true);
}
void ContentAutofillDriver::Reset(ContentAutofillDriverFactoryPassKey) {
owner_->router().UnregisterDriver(*this, /*driver_is_dying=*/false);
}
void ContentAutofillDriver::TriggerFormExtractionInDriverFrame(
AutofillDriverRouterAndFormForestPassKey pass_key) {
if (!IsActive()) {
LOG(WARNING) << "Skipped Autofill message for inactive frame";
return;
}
GetAutofillAgent()->TriggerFormExtraction();
}
void ContentAutofillDriver::TriggerFormExtractionInAllFrames(
base::OnceCallback<void(bool success)> form_extraction_finished_callback) {
std::vector<ContentAutofillDriver*> drivers;
render_frame_host()->GetMainFrame()->ForEachRenderFrameHost(
[&drivers](content::RenderFrameHost* rfh) {
if (rfh->IsActive()) {
if (auto* driver = GetForRenderFrameHost(rfh)) {
drivers.push_back(driver);
}
}
});
auto barrier_callback = base::BarrierCallback<bool>(
drivers.size(),
base::BindOnce(
[](base::OnceCallback<void(bool success)>
form_extraction_finished_callback,
const std::vector<bool>& successes) {
std::move(form_extraction_finished_callback)
.Run(std::ranges::all_of(successes, std::identity()));
},
std::move(form_extraction_finished_callback)));
for (ContentAutofillDriver* driver : drivers) {
driver->GetAutofillAgent()->TriggerFormExtractionWithResponse(
barrier_callback);
}
}
void ContentAutofillDriver::GetFourDigitCombinationsFromDom(
base::OnceCallback<void(const std::vector<std::string>&)>
potential_matches) {
if (!IsActive()) {
LOG(WARNING) << "Skipped Autofill message for inactive frame";
std::move(potential_matches).Run({});
return;
}
content::RenderFrameHost* main_rfh = render_frame_host_->GetMainFrame();
if (auto* main_driver = GetForRenderFrameHost(main_rfh)) {
main_driver->GetAutofillAgent()
->GetPotentialLastFourCombinationsForStandaloneCvc(
std::move(potential_matches));
}
}
void ContentAutofillDriver::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& amount)> response_callback) {
if (!IsActive()) {
LOG(WARNING) << "Skipped Autofill message for inactive frame";
std::move(response_callback).Run(std::string());
return;
}
content::RenderFrameHost* main_rfh = render_frame_host_->GetMainFrame();
if (auto* main_driver = GetForRenderFrameHost(main_rfh)) {
main_driver->GetAutofillAgent()->ExtractLabeledTextNodeValue(
value_regex, label_regex, number_of_ancestor_levels_to_search,
std::move(response_callback));
}
}
// static
ContentAutofillDriver* ContentAutofillDriver::GetForRenderFrameHost(
content::RenderFrameHost* render_frame_host) {
ContentAutofillDriverFactory* factory =
ContentAutofillDriverFactory::FromWebContents(
content::WebContents::FromRenderFrameHost(render_frame_host));
return factory
? factory->DriverForFrame(render_frame_host,
base::PassKey<ContentAutofillDriver>())
: nullptr;
}
void ContentAutofillDriver::BindPendingReceiver(
mojo::PendingAssociatedReceiver<mojom::AutofillDriver> pending_receiver) {
receiver_.Bind(std::move(pending_receiver));
}
LocalFrameToken ContentAutofillDriver::GetFrameToken() const {
return LocalFrameToken(render_frame_host_->GetFrameToken().value());
}
ContentAutofillDriver* ContentAutofillDriver::GetParent() {
content::RenderFrameHost* parent_rfh = render_frame_host_->GetParent();
if (!parent_rfh) {
return nullptr;
}
return GetForRenderFrameHost(parent_rfh);
}
ContentAutofillClient& ContentAutofillDriver::GetAutofillClient() {
return owner_->client();
}
AutofillManager& ContentAutofillDriver::GetAutofillManager() {
return *autofill_manager_;
}
std::optional<LocalFrameToken> ContentAutofillDriver::Resolve(
FrameToken query) {
if (std::holds_alternative<LocalFrameToken>(query)) {
return std::get<LocalFrameToken>(query);
}
DCHECK(std::holds_alternative<RemoteFrameToken>(query));
content::RenderProcessHost* rph = render_frame_host_->GetProcess();
blink::RemoteFrameToken blink_remote_token(
std::get<RemoteFrameToken>(query).value());
content::RenderFrameHost* remote_rfh =
content::RenderFrameHost::FromPlaceholderToken(rph->GetDeprecatedID(),
blink_remote_token);
if (!remote_rfh) {
return std::nullopt;
}
return LocalFrameToken(remote_rfh->GetFrameToken().value());
}
ukm::SourceId ContentAutofillDriver::GetPageUkmSourceId() const {
CHECK(!render_frame_host_->IsInLifecycleState(
content::RenderFrameHost::LifecycleState::kPrerendering));
return render_frame_host_->GetPageUkmSourceId();
}
bool ContentAutofillDriver::IsActive() const {
return render_frame_host_->IsActive();
}
bool ContentAutofillDriver::HasSharedAutofillPermission() const {
return render_frame_host_->IsFeatureEnabled(
network::mojom::PermissionsPolicyFeature::kSharedAutofill);
}
bool ContentAutofillDriver::CanShowAutofillUi() const {
// Don't show AutofillUi for inactive RenderFrameHost. Here it is safe to
// ignore the calls from inactive RFH as the renderer is not expecting a reply
// and it doesn't lead to browser-renderer consistency issues.
return render_frame_host_->IsActive();
}
std::optional<net::IsolationInfo> ContentAutofillDriver::GetIsolationInfo() {
return render_frame_host_->GetIsolationInfoForSubresources();
}
base::flat_set<FieldGlobalId> ContentAutofillDriver::ApplyFormAction(
mojom::FormActionType action_type,
mojom::ActionPersistence action_persistence,
base::span<const FormFieldData> data,
const url::Origin& triggered_origin,
const base::flat_map<FieldGlobalId, FieldType>& field_type_map,
const Section& section_for_clear_form_on_ios) {
// If this driver is active, then its main frame is identical to the main
// frame at the time the form was received from a renderer and their origins
// are the same.
url::Origin main_origin = [&] {
if (auto* main_rfh = render_frame_host_->GetMainFrame();
main_rfh->IsInPrimaryMainFrame()) {
return main_rfh->GetLastCommittedOrigin();
} else {
return url::Origin();
}
}();
return RouteToAgent(router(), &AutofillDriverRouter::ApplyFormAction,
&mojom::AutofillAgent::ApplyFieldsAction, action_type,
action_persistence, data, main_origin, triggered_origin,
field_type_map);
}
void ContentAutofillDriver::ApplyFieldAction(
mojom::FieldActionType action_type,
mojom::ActionPersistence action_persistence,
const FieldGlobalId& field_id,
const std::u16string& value) {
RouteToAgent(router(), &AutofillDriverRouter::ApplyFieldAction,
&mojom::AutofillAgent::ApplyFieldAction, action_type,
action_persistence, field_id, value);
}
void ContentAutofillDriver::ExtractForm(FormGlobalId form_id,
BrowserFormHandler final_handler) {
if (!IsActive()) {
LOG(WARNING) << "Skipped Autofill message for inactive frame";
std::move(final_handler).Run(nullptr, std::nullopt);
return;
}
router().ExtractForm(
[](autofill::AutofillDriver& request_target, FormRendererId form_id,
AutofillDriverRouter::RendererFormHandler route_response) {
auto& source = static_cast<ContentAutofillDriver&>(request_target);
source.GetAutofillAgent()->ExtractForm(
form_id, Lift(source, std::move(route_response)));
},
form_id, WithNewVersion(std::move(final_handler)));
}
void ContentAutofillDriver::ExposeDomNodeIdsInAllFrames() {
RouteToAgent(router(), &AutofillDriverRouter::ExposeDomNodeIdsInAllFrames,
&mojom::AutofillAgent::ExposeDomNodeIds);
}
void ContentAutofillDriver::SendTypePredictionsToRenderer(
const FormStructure& form) {
CHECK(base::FeatureList::IsEnabled(
features::test::kAutofillShowTypePredictions));
RouteToAgent(router(), &AutofillDriverRouter::SendTypePredictionsToRenderer,
&mojom::AutofillAgent::FieldTypePredictionsAvailable,
form.GetFieldTypePredictions());
}
void ContentAutofillDriver::RendererShouldAcceptDataListSuggestion(
const FieldGlobalId& field_id,
const std::u16string& value) {
RouteToAgent(
router(), &AutofillDriverRouter::RendererShouldAcceptDataListSuggestion,
&mojom::AutofillAgent::AcceptDataListSuggestion, field_id, value);
}
void ContentAutofillDriver::RendererShouldClearPreviewedForm() {
RouteToAgent(router(),
&AutofillDriverRouter::RendererShouldClearPreviewedForm,
&mojom::AutofillAgent::ClearPreviewedForm);
}
void ContentAutofillDriver::RendererShouldTriggerSuggestions(
const FieldGlobalId& field_id,
AutofillSuggestionTriggerSource trigger_source) {
RouteToAgent(
router(), &AutofillDriverRouter::RendererShouldTriggerSuggestions,
&mojom::AutofillAgent::TriggerSuggestions, field_id, trigger_source);
}
void ContentAutofillDriver::RendererShouldSetSuggestionAvailability(
const FieldGlobalId& field_id,
mojom::AutofillSuggestionAvailability suggestion_availability) {
RouteToAgent(router(),
&AutofillDriverRouter::RendererShouldSetSuggestionAvailability,
&mojom::AutofillAgent::SetSuggestionAvailability, field_id,
suggestion_availability);
}
void ContentAutofillDriver::DispatchEmailVerifiedEvent(
FieldGlobalId field_id,
const std::string& presentation_token) {
RouteToAgent(router(), &AutofillDriverRouter::DispatchEmailVerifiedEvent,
&mojom::AutofillAgent::DispatchEmailVerifiedEvent, field_id,
presentation_token);
}
void ContentAutofillDriver::FormsSeen(
const std::vector<FormData>& updated_forms,
const std::vector<FormRendererId>& removed_forms) {
RouteToManager(*this, router(), &AutofillDriverRouter::FormsSeen,
&AutofillManager::OnFormsSeen, updated_forms, removed_forms);
}
void ContentAutofillDriver::FormSubmitted(
const FormData& form,
mojom::SubmissionSource submission_source) {
RouteToManager(*this, router(), &AutofillDriverRouter::FormSubmitted,
&AutofillManager::OnFormSubmitted, form, submission_source);
}
void ContentAutofillDriver::CaretMovedInFormField(
const FormData& form,
FieldRendererId field_id,
const gfx::Rect& caret_bounds) {
RouteToManager(*this, router(), &AutofillDriverRouter::CaretMovedInFormField,
&AutofillManager::OnCaretMovedInFormField, form, field_id,
caret_bounds);
}
void ContentAutofillDriver::TextFieldValueChanged(const FormData& form,
FieldRendererId field_id,
base::TimeTicks timestamp) {
RouteToManager(*this, router(), &AutofillDriverRouter::TextFieldValueChanged,
&AutofillManager::OnTextFieldValueChanged, form, field_id,
timestamp);
}
void ContentAutofillDriver::TextFieldDidScroll(const FormData& form,
FieldRendererId field_id) {
RouteToManager(*this, router(), &AutofillDriverRouter::TextFieldDidScroll,
&AutofillManager::OnTextFieldDidScroll, form, field_id);
}
void ContentAutofillDriver::SelectControlSelectionChanged(
const FormData& form,
FieldRendererId field_id) {
RouteToManager(
*this, router(), &AutofillDriverRouter::SelectControlSelectionChanged,
&AutofillManager::OnSelectControlSelectionChanged, form, field_id);
}
void ContentAutofillDriver::AskForValuesToFill(
const FormData& form,
FieldRendererId field_id,
const gfx::Rect& caret_bounds,
AutofillSuggestionTriggerSource trigger_source,
const std::optional<PasswordSuggestionRequest>& password_request) {
RouteToManager(*this, router(), &AutofillDriverRouter::AskForValuesToFill,
&AutofillManager::OnAskForValuesToFill, form, field_id,
caret_bounds, trigger_source, password_request);
}
void ContentAutofillDriver::HidePopup() {
RouteToManager(*this, router(), &AutofillDriverRouter::HidePopup,
&AutofillManager::OnHidePopup);
}
void ContentAutofillDriver::FocusOnNonFormField() {
RouteToManager(*this, router(), &AutofillDriverRouter::FocusOnNonFormField,
&AutofillManager::OnFocusOnNonFormField);
}
void ContentAutofillDriver::FocusOnFormField(const FormData& form,
FieldRendererId field_id) {
auto focus_no_longer_on_form = [](autofill::AutofillDriver& target) {
target.GetAutofillManager().OnFocusOnNonFormField();
};
RouteToManager(
*this, router(), &AutofillDriverRouter::FocusOnFormField,
&AutofillManager::OnFocusOnFormField, form, field_id,
AutofillDriverRouter::RoutedCallback<>(focus_no_longer_on_form));
}
void ContentAutofillDriver::DidAutofillForm(const FormData& form,
base::TimeTicks timestamp) {
RouteToManager(*this, router(), &AutofillDriverRouter::DidAutofillForm,
&AutofillManager::OnDidAutofillForm, form, timestamp);
}
void ContentAutofillDriver::DidEndTextFieldEditing() {
RouteToManager(*this, router(), &AutofillDriverRouter::DidEndTextFieldEditing,
&AutofillManager::OnDidEndTextFieldEditing);
}
void ContentAutofillDriver::SelectFieldOptionsDidChange(const FormData& form) {
RouteToManager(*this, router(),
&AutofillDriverRouter::SelectFieldOptionsDidChange,
&AutofillManager::OnSelectFieldOptionsDidChange, form);
}
void ContentAutofillDriver::JavaScriptChangedAutofilledValue(
const FormData& form,
FieldRendererId field_id,
const std::u16string& old_value) {
RouteToManager(*this, router(),
&AutofillDriverRouter::JavaScriptChangedAutofilledValue,
&AutofillManager::OnJavaScriptChangedAutofilledValue, form,
field_id, old_value);
}
const mojo::AssociatedRemote<mojom::AutofillAgent>&
ContentAutofillDriver::GetAutofillAgent() {
// Here is a lazy binding, and will not reconnect after connection error.
if (!autofill_agent_) {
render_frame_host_->GetRemoteAssociatedInterfaces()->GetInterface(
&autofill_agent_);
}
return autofill_agent_;
}
void ContentAutofillDriver::LiftForTest(FormData& form) {
form = Lift(*this, form);
}
AutofillDriverRouter& ContentAutofillDriver::router() {
return owner_->router();
}
} // namespace autofill