Commit b09a328a authored by Christoph Schwering's avatar Christoph Schwering Committed by Commit Bot

[Autofill] Move probable-form-submission detection to the browser.

This CL moves the probable-form-submission detection from the renderer
process to the browser process. The new behaviour is active iff
kAutofillEnableProbableFormSubmissionInBrowser is enabled.

The current state is that probable form submission is detected in
FormTracker::DidStartNavigation(), which forwards the event to
AutofillAgent::OnProbablyFormSubmitted(), which then notifies
ContentAutofillDriver::FormSubmitted(). The 'submitted form' is the
one returned by AutofillAgent::GetSubmittedForm().

This CL moves the event detection to the newly added
ContentAutofillDriverFactory::DidStartNavigation(), which calls the
newly added ContentAutofillDriver::ProbablyFormSubmitted(). The
'submitted form' should again be the one returned by
AutofillAgont::GetSubmittedForm(). For the browser process to have
access to that form, AutofillAgent now proactively sends it to the
browser process by calling SendPotentiallySubmittedFormToBrowser().

See the referenced bugs for further details.

Bug: 1113740, 1117451
Change-Id: I46be9e8b7058f4a309e3e9c64f34ca4d8852e641
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2332622
Commit-Queue: Christoph Schwering <schwering@google.com>
Reviewed-by: default avatarMatthias Körber <koerber@google.com>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800092}
parent 56c4b4d2
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include "components/autofill/core/browser/personal_data_manager_observer.h" #include "components/autofill/core/browser/personal_data_manager_observer.h"
#include "components/autofill/core/browser/validation.h" #include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/mojom/autofill_types.mojom-shared.h"
#include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h" #include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
#include "content/public/browser/browser_accessibility_state.h" #include "content/public/browser/browser_accessibility_state.h"
#include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_controller.h"
...@@ -50,6 +51,8 @@ ...@@ -50,6 +51,8 @@
#include "content/public/test/test_renderer_host.h" #include "content/public/test/test_renderer_host.h"
#include "content/public/test/test_utils.h" #include "content/public/test/test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/http_request.h"
#include "net/test/embedded_test_server/http_response.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_enums.mojom.h"
...@@ -57,6 +60,7 @@ ...@@ -57,6 +60,7 @@
using base::ASCIIToUTF16; using base::ASCIIToUTF16;
using base::UTF16ToASCII; using base::UTF16ToASCII;
using testing::_;
namespace autofill { namespace autofill {
...@@ -780,4 +784,177 @@ IN_PROC_BROWSER_TEST_F(AutofillAccessibilityTest, TestAutocompleteState) { ...@@ -780,4 +784,177 @@ IN_PROC_BROWSER_TEST_F(AutofillAccessibilityTest, TestAutocompleteState) {
ASSERT_TRUE(AutocompleteIsAvailable(node_data)); ASSERT_TRUE(AutocompleteIsAvailable(node_data));
} }
// Test fixture for testing that that appropriate form submission events are
// fired in AutofillManager.
class FormSubmissionDetectionTest
: public InProcessBrowserTest,
public testing::WithParamInterface<std::tuple<bool, bool>> {
protected:
class MockAutofillManager : public AutofillManager {
public:
MockAutofillManager(AutofillDriver* driver, AutofillClient* client)
: AutofillManager(driver,
client,
"en-US",
AutofillManager::DISABLE_AUTOFILL_DOWNLOAD_MANAGER) {}
MOCK_METHOD3(OnFormSubmittedImpl,
void(const FormData&, bool, mojom::SubmissionSource));
};
FormSubmissionDetectionTest() { InitializeFeatures(); }
void SetUpOnMainThread() override {
SetUpServer();
NavigateToPage("/form.html");
Mock();
}
void TearDownOnMainThread() override {}
void ExecuteScript(const std::string& js) {
// Simulate a mouse click to submit the form because form submissions not
// triggered by user gestures are ignored.
std::string onclick_js = "document.onclick = function() { " + js + "; };";
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(content::ExecuteScript(web_contents, onclick_js));
content::SimulateMouseClick(
browser()->tab_strip_model()->GetActiveWebContents(), 0,
blink::WebMouseEvent::Button::kLeft);
}
MockAutofillManager* autofill_manager_ = nullptr;
private:
void InitializeFeatures() {
std::vector<base::Feature> enabled;
std::vector<base::Feature> disabled;
if (std::get<0>(GetParam())) {
enabled.push_back(features::kAutofillAllowDuplicateFormSubmissions);
} else {
disabled.push_back(features::kAutofillAllowDuplicateFormSubmissions);
}
if (std::get<1>(GetParam())) {
enabled.push_back(features::kAutofillProbableFormSubmissionInBrowser);
} else {
disabled.push_back(features::kAutofillProbableFormSubmissionInBrowser);
}
feature_list_.InitWithFeatures(enabled, disabled);
}
void SetUpServer() {
embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
&FormSubmissionDetectionTest::HandleRequest, base::Unretained(this)));
ASSERT_TRUE(embedded_test_server()->Start());
}
std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
const net::test_server::HttpRequest& request) {
GURL absolute_url = embedded_test_server()->GetURL(request.relative_url);
std::string content;
if (absolute_url.path() == "/form.html") {
content = get_form_html();
} else if (absolute_url.path() == "/success.html") {
content = "<html><body>Happy times!";
} else {
return nullptr;
}
auto http_response =
std::make_unique<net::test_server::BasicHttpResponse>();
http_response->set_code(net::HTTP_OK);
http_response->set_content_type("text/html");
http_response->set_content(content);
return http_response;
}
virtual std::string get_form_html() const {
return "<html><body>"
"<form id='form' method='POST' action='/success.html'>"
"Name: <input type='text' id='name'><br>"
"Address: <input type='text' id='address'><br>"
"City: <input type='text' id='city'><br>"
"ZIP: <input type='text' id='zip'><br>"
"State: <select id='state'>"
" <option value='CA'>CA</option>"
" <option value='WA'>WA</option>"
"</select><br>"
"</form>";
}
void NavigateToPage(const std::string& filename) {
GURL url = embedded_test_server()->GetURL(filename);
NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
ui_test_utils::NavigateToURL(&params);
}
// TODO(crbug/1119526) This dependency injection is wonky because it only
// mocks the current ContentAutofillDriver's AutofillManager, not the future
// ones' AutofillManagers.
void Mock() {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ContentAutofillDriverFactory* driver_factory =
ContentAutofillDriverFactory::FromWebContents(web_contents);
AutofillClient* client = driver_factory->client();
ContentAutofillDriver* driver =
driver_factory->DriverForFrame(web_contents->GetMainFrame());
std::unique_ptr<MockAutofillManager> mock_autofill_manager =
std::make_unique<MockAutofillManager>(driver, client);
autofill_manager_ = mock_autofill_manager.get();
driver->SetAutofillManager(std::move(mock_autofill_manager));
}
base::test::ScopedFeatureList feature_list_;
};
// Tests that user-triggered submission triggers a submission event in
// AutofillManager.
IN_PROC_BROWSER_TEST_P(FormSubmissionDetectionTest, Submission) {
base::RunLoop run_loop;
EXPECT_CALL(
*autofill_manager_,
OnFormSubmittedImpl(_, _, mojom::SubmissionSource::FORM_SUBMISSION))
.Times(1)
.WillRepeatedly(
testing::InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
ExecuteScript(
"document.getElementById('name').value = 'Sarah';"
"document.getElementById('name').select();"
"document.getElementById('form').submit();");
run_loop.Run();
}
// Tests that non-link-click, renderer-inititiated navigation triggers a
// submission event in AutofillManager.
IN_PROC_BROWSER_TEST_P(FormSubmissionDetectionTest, ProbableSubmission) {
base::RunLoop run_loop;
EXPECT_CALL(*autofill_manager_,
OnFormSubmittedImpl(
_, _, mojom::SubmissionSource::PROBABLY_FORM_SUBMITTED))
.Times(1)
.WillRepeatedly(
testing::InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
// Add a delay before navigating away to avoid race conditions. This is
// appropriate since we're faking user interaction here.
ExecuteScript(
"document.getElementById('name').focus();"
"document.getElementById('name').value = 'Sarah';"
"document.getElementById('name').select();"
"document.getElementById('state').selectedIndex = 1;"
"document.getElementById('zip').focus();"
"document.getElementById('name').select();"
"setTimeout(function() { window.location.assign('/success.html'); }, "
"50);");
run_loop.Run();
}
INSTANTIATE_TEST_SUITE_P(All,
FormSubmissionDetectionTest,
testing::Combine(testing::Bool(), testing::Bool()));
} // namespace autofill } // namespace autofill
...@@ -70,6 +70,9 @@ class FakeContentAutofillDriver : public mojom::AutofillDriver { ...@@ -70,6 +70,9 @@ class FakeContentAutofillDriver : public mojom::AutofillDriver {
private: private:
// mojom::AutofillDriver: // mojom::AutofillDriver:
void SetFormToBeProbablySubmitted(
const base::Optional<FormData>& form) override {}
void FormsSeen(const std::vector<FormData>& forms, void FormsSeen(const std::vector<FormData>& forms,
base::TimeTicks timestamp) override { base::TimeTicks timestamp) override {
// FormsSeen() could be called multiple times and sometimes even with empty // FormsSeen() could be called multiple times and sometimes even with empty
......
...@@ -7,11 +7,13 @@ ...@@ -7,11 +7,13 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/test/base/chrome_render_view_test.h" #include "chrome/test/base/chrome_render_view_test.h"
#include "components/autofill/content/renderer/autofill_agent.h" #include "components/autofill/content/renderer/autofill_agent.h"
#include "components/autofill/content/renderer/focus_test_utils.h" #include "components/autofill/content/renderer/focus_test_utils.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/form_data.h" #include "components/autofill/core/common/form_data.h"
#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h" #include "content/public/renderer/render_view.h"
...@@ -62,6 +64,9 @@ class FakeContentAutofillDriver : public mojom::AutofillDriver { ...@@ -62,6 +64,9 @@ class FakeContentAutofillDriver : public mojom::AutofillDriver {
private: private:
// mojom::AutofillDriver: // mojom::AutofillDriver:
void SetFormToBeProbablySubmitted(
const base::Optional<FormData>& form) override {}
void FormsSeen(const std::vector<FormData>& forms, void FormsSeen(const std::vector<FormData>& forms,
base::TimeTicks timestamp) override {} base::TimeTicks timestamp) override {}
...@@ -973,6 +978,10 @@ TEST_F(FormAutocompleteTest, FormSubmittedBySameDocumentNavigation) { ...@@ -973,6 +978,10 @@ TEST_F(FormAutocompleteTest, FormSubmittedBySameDocumentNavigation) {
} }
TEST_F(FormAutocompleteTest, FormSubmittedByProbablyFormSubmitted) { TEST_F(FormAutocompleteTest, FormSubmittedByProbablyFormSubmitted) {
base::test::ScopedFeatureList scoped_features;
scoped_features.InitAndDisableFeature(
features::kAutofillProbableFormSubmissionInBrowser);
LoadHTML( LoadHTML(
"<html>" "<html>"
"<input type='text' id='address_field' name='address' autocomplete='on'>" "<input type='text' id='address_field' name='address' autocomplete='on'>"
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "components/autofill/content/browser/content_autofill_driver_factory.h" #include "components/autofill/content/browser/content_autofill_driver_factory.h"
...@@ -16,6 +17,7 @@ ...@@ -16,6 +17,7 @@
#include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/payments/payments_service_url.h" #include "components/autofill/core/browser/payments/payments_service_url.h"
#include "components/autofill/core/common/autofill_features.h"
#include "content/public/browser/back_forward_cache.h" #include "content/public/browser/back_forward_cache.h"
#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_controller.h"
...@@ -51,12 +53,8 @@ ContentAutofillDriver::ContentAutofillDriver( ...@@ -51,12 +53,8 @@ ContentAutofillDriver::ContentAutofillDriver(
if (provider) { if (provider) {
SetAutofillProvider(provider); SetAutofillProvider(provider);
} else { } else {
autofill_handler_ = std::make_unique<AutofillManager>( SetAutofillManager(std::make_unique<AutofillManager>(
this, client, app_locale, enable_download_manager); this, client, app_locale, enable_download_manager));
autofill_manager_ = static_cast<AutofillManager*>(autofill_handler_.get());
autofill_external_delegate_ =
std::make_unique<AutofillExternalDelegate>(autofill_manager_, this);
autofill_manager_->SetExternalDelegate(autofill_external_delegate_.get());
} }
} }
...@@ -226,9 +224,32 @@ void ContentAutofillDriver::FormsSeen(const std::vector<FormData>& forms, ...@@ -226,9 +224,32 @@ void ContentAutofillDriver::FormsSeen(const std::vector<FormData>& forms,
autofill_handler_->OnFormsSeen(forms, timestamp); autofill_handler_->OnFormsSeen(forms, timestamp);
} }
void ContentAutofillDriver::SetFormToBeProbablySubmitted(
const base::Optional<FormData>& form) {
potentially_submitted_form_ = form;
}
void ContentAutofillDriver::ProbablyFormSubmitted() {
if (potentially_submitted_form_.has_value()) {
FormSubmitted(potentially_submitted_form_.value(), false,
mojom::SubmissionSource::PROBABLY_FORM_SUBMITTED);
}
}
void ContentAutofillDriver::FormSubmitted(const FormData& form, void ContentAutofillDriver::FormSubmitted(const FormData& form,
bool known_success, bool known_success,
mojom::SubmissionSource source) { mojom::SubmissionSource source) {
// Omit duplicate form submissions. It may be reasonable to take |source|
// into account here as well.
// TODO(crbug/1117451): Clean up experiment code.
if (base::FeatureList::IsEnabled(
features::kAutofillProbableFormSubmissionInBrowser) &&
!base::FeatureList::IsEnabled(
features::kAutofillAllowDuplicateFormSubmissions) &&
!submitted_forms_.insert(form.unique_renderer_id).second) {
return;
}
autofill_handler_->OnFormSubmitted(form, known_success, source); autofill_handler_->OnFormSubmitted(form, known_success, source);
} }
...@@ -305,6 +326,7 @@ void ContentAutofillDriver::DidNavigateFrame( ...@@ -305,6 +326,7 @@ void ContentAutofillDriver::DidNavigateFrame(
return; return;
} }
submitted_forms_.clear();
autofill_handler_->Reset(); autofill_handler_->Reset();
} }
...@@ -312,6 +334,8 @@ void ContentAutofillDriver::SetAutofillManager( ...@@ -312,6 +334,8 @@ void ContentAutofillDriver::SetAutofillManager(
std::unique_ptr<AutofillManager> manager) { std::unique_ptr<AutofillManager> manager) {
autofill_handler_ = std::move(manager); autofill_handler_ = std::move(manager);
autofill_manager_ = static_cast<AutofillManager*>(autofill_handler_.get()); autofill_manager_ = static_cast<AutofillManager*>(autofill_handler_.get());
autofill_external_delegate_ =
std::make_unique<AutofillExternalDelegate>(autofill_manager_, this);
autofill_manager_->SetExternalDelegate(autofill_external_delegate_.get()); autofill_manager_->SetExternalDelegate(autofill_external_delegate_.get());
} }
......
...@@ -105,6 +105,8 @@ class ContentAutofillDriver : public AutofillDriver, ...@@ -105,6 +105,8 @@ class ContentAutofillDriver : public AutofillDriver,
net::IsolationInfo IsolationInfo() override; net::IsolationInfo IsolationInfo() override;
// mojom::AutofillDriver: // mojom::AutofillDriver:
void SetFormToBeProbablySubmitted(
const base::Optional<FormData>& form) override;
void FormsSeen(const std::vector<FormData>& forms, void FormsSeen(const std::vector<FormData>& forms,
base::TimeTicks timestamp) override; base::TimeTicks timestamp) override;
void FormSubmitted(const FormData& form, void FormSubmitted(const FormData& form,
...@@ -136,6 +138,8 @@ class ContentAutofillDriver : public AutofillDriver, ...@@ -136,6 +138,8 @@ class ContentAutofillDriver : public AutofillDriver,
void DidEndTextFieldEditing() override; void DidEndTextFieldEditing() override;
void SelectFieldOptionsDidChange(const FormData& form) override; void SelectFieldOptionsDidChange(const FormData& form) override;
void ProbablyFormSubmitted();
// DidNavigateFrame() is called on the frame's driver, respectively, when a // DidNavigateFrame() is called on the frame's driver, respectively, when a
// navigation occurs in that specific frame. // navigation occurs in that specific frame.
void DidNavigateFrame(content::NavigationHandle* navigation_handle); void DidNavigateFrame(content::NavigationHandle* navigation_handle);
...@@ -188,6 +192,15 @@ class ContentAutofillDriver : public AutofillDriver, ...@@ -188,6 +192,15 @@ class ContentAutofillDriver : public AutofillDriver,
// always be non-NULL and valid for lifetime of |this|. // always be non-NULL and valid for lifetime of |this|.
content::RenderFrameHost* const render_frame_host_; content::RenderFrameHost* const render_frame_host_;
// The form pushed from the AutofillAgent to the AutofillDriver. When the
// ProbablyFormSubmitted() event is fired, this form is considered the
// submitted one.
base::Optional<FormData> potentially_submitted_form_;
// Keeps track of the forms for which FormSubmitted() event has been triggered
// to avoid duplicates fired by AutofillAgent.
std::set<FormRendererId> submitted_forms_;
// AutofillHandler instance via which this object drives the shared Autofill // AutofillHandler instance via which this object drives the shared Autofill
// code. // code.
std::unique_ptr<AutofillHandler> autofill_handler_; std::unique_ptr<AutofillHandler> autofill_handler_;
......
...@@ -9,9 +9,11 @@ ...@@ -9,9 +9,11 @@
#include <vector> #include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/feature_list.h"
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "components/autofill/content/browser/content_autofill_driver.h" #include "components/autofill/content/browser/content_autofill_driver.h"
#include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/autofill_manager.h"
#include "components/autofill/core/common/autofill_features.h"
#include "content/public/browser/navigation_handle.h" #include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_process_host.h"
...@@ -136,6 +138,24 @@ void ContentAutofillDriverFactory::RenderFrameDeleted( ...@@ -136,6 +138,24 @@ void ContentAutofillDriverFactory::RenderFrameDeleted(
DeleteForKey(render_frame_host); DeleteForKey(render_frame_host);
} }
void ContentAutofillDriverFactory::DidStartNavigation(
content::NavigationHandle* navigation_handle) {
// TODO(crbug/1117451): Clean up experiment code.
if (base::FeatureList::IsEnabled(
features::kAutofillProbableFormSubmissionInBrowser) &&
navigation_handle->IsRendererInitiated() &&
!navigation_handle->WasInitiatedByLinkClick() &&
navigation_handle->IsInMainFrame()) {
content::GlobalFrameRoutingId id =
navigation_handle->GetPreviousRenderFrameHostId();
content::RenderFrameHost* render_frame_host =
content::RenderFrameHost::FromID(id);
if (render_frame_host) {
DriverForFrame(render_frame_host)->ProbablyFormSubmitted();
}
}
}
void ContentAutofillDriverFactory::DidFinishNavigation( void ContentAutofillDriverFactory::DidFinishNavigation(
content::NavigationHandle* navigation_handle) { content::NavigationHandle* navigation_handle) {
if (navigation_handle->HasCommitted() && if (navigation_handle->HasCommitted() &&
......
...@@ -64,6 +64,8 @@ class ContentAutofillDriverFactory : public AutofillDriverFactory, ...@@ -64,6 +64,8 @@ class ContentAutofillDriverFactory : public AutofillDriverFactory,
// content::WebContentsObserver: // content::WebContentsObserver:
void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override;
void DidFinishNavigation( void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override; content::NavigationHandle* navigation_handle) override;
void OnVisibilityChanged(content::Visibility visibility) override; void OnVisibilityChanged(content::Visibility visibility) override;
......
...@@ -14,6 +14,12 @@ import "url/mojom/url.mojom"; ...@@ -14,6 +14,12 @@ import "url/mojom/url.mojom";
// There is one instance of this interface per render frame host in the browser // There is one instance of this interface per render frame host in the browser
// process. All methods are called by renderer on browser. // process. All methods are called by renderer on browser.
interface AutofillDriver { interface AutofillDriver {
// Pushes a form the user interacted with from the AutofillAgent to the
// AutofillDriver, which may consider this form as a probably-submitted form
// once the page is navigated away from. If |form| is not set, it resets the
// last-interacted form.
SetFormToBeProbablySubmitted(FormData? form);
// Notification that forms have been seen that are candidates for // Notification that forms have been seen that are candidates for
// filling/submitting by the AutofillManager. // filling/submitting by the AutofillManager.
FormsSeen(array<FormData> forms, mojo_base.mojom.TimeTicks timestamp); FormsSeen(array<FormData> forms, mojo_base.mojom.TimeTicks timestamp);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "base/i18n/case_conversion.h" #include "base/i18n/case_conversion.h"
#include "base/location.h" #include "base/location.h"
#include "base/metrics/field_trial.h" #include "base/metrics/field_trial.h"
#include "base/optional.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
...@@ -167,6 +168,7 @@ void AutofillAgent::DidCommitProvisionalLoad(ui::PageTransition transition) { ...@@ -167,6 +168,7 @@ void AutofillAgent::DidCommitProvisionalLoad(ui::PageTransition transition) {
form_cache_.Reset(); form_cache_.Reset();
ResetLastInteractedElements(); ResetLastInteractedElements();
OnFormNoLongerSubmittable(); OnFormNoLongerSubmittable();
SendPotentiallySubmittedFormToBrowser();
} }
void AutofillAgent::DidFinishDocumentLoad() { void AutofillAgent::DidFinishDocumentLoad() {
...@@ -290,8 +292,11 @@ void AutofillAgent::FireHostSubmitEvents(const FormData& form_data, ...@@ -290,8 +292,11 @@ void AutofillAgent::FireHostSubmitEvents(const FormData& form_data,
bool known_success, bool known_success,
SubmissionSource source) { SubmissionSource source) {
// We don't want to fire duplicate submission event. // We don't want to fire duplicate submission event.
if (!submitted_forms_.insert(form_data.unique_renderer_id).second) if (!base::FeatureList::IsEnabled(
features::kAutofillAllowDuplicateFormSubmissions) &&
!submitted_forms_.insert(form_data.unique_renderer_id).second) {
return; return;
}
GetAutofillDriver()->FormSubmitted(form_data, known_success, source); GetAutofillDriver()->FormSubmitted(form_data, known_success, source);
} }
...@@ -312,6 +317,8 @@ void AutofillAgent::TextFieldDidEndEditing(const WebInputElement& element) { ...@@ -312,6 +317,8 @@ void AutofillAgent::TextFieldDidEndEditing(const WebInputElement& element) {
password_autofill_agent_->DidEndTextFieldEditing(); password_autofill_agent_->DidEndTextFieldEditing();
if (password_generation_agent_) if (password_generation_agent_)
password_generation_agent_->DidEndTextFieldEditing(element); password_generation_agent_->DidEndTextFieldEditing(element);
SendPotentiallySubmittedFormToBrowser();
} }
void AutofillAgent::SetUserGestureRequired(bool required) { void AutofillAgent::SetUserGestureRequired(bool required) {
...@@ -468,6 +475,7 @@ void AutofillAgent::FillForm(int32_t id, const FormData& form) { ...@@ -468,6 +475,7 @@ void AutofillAgent::FillForm(int32_t id, const FormData& form) {
AutofillTickClock::NowTicks()); AutofillTickClock::NowTicks());
TriggerRefillIfNeeded(form); TriggerRefillIfNeeded(form);
SendPotentiallySubmittedFormToBrowser();
} }
void AutofillAgent::PreviewForm(int32_t id, const FormData& form) { void AutofillAgent::PreviewForm(int32_t id, const FormData& form) {
...@@ -586,7 +594,7 @@ void AutofillAgent::PreviewPasswordSuggestion(const base::string16& username, ...@@ -586,7 +594,7 @@ void AutofillAgent::PreviewPasswordSuggestion(const base::string16& username,
DCHECK(handled); DCHECK(handled);
} }
bool AutofillAgent::CollectFormlessElements(FormData* output) { bool AutofillAgent::CollectFormlessElements(FormData* output) const {
if (render_frame() == nullptr || render_frame()->GetWebFrame() == nullptr) if (render_frame() == nullptr || render_frame()->GetWebFrame() == nullptr)
return false; return false;
...@@ -878,6 +886,8 @@ void AutofillAgent::DidCompleteFocusChangeInFrame() { ...@@ -878,6 +886,8 @@ void AutofillAgent::DidCompleteFocusChangeInFrame() {
if (!IsKeyboardAccessoryEnabled() && focus_requires_scroll_) if (!IsKeyboardAccessoryEnabled() && focus_requires_scroll_)
HandleFocusChangeComplete(); HandleFocusChangeComplete();
SendPotentiallySubmittedFormToBrowser();
} }
void AutofillAgent::DidReceiveLeftMouseDownOrGestureTapInNode( void AutofillAgent::DidReceiveLeftMouseDownOrGestureTapInNode(
...@@ -957,6 +967,8 @@ void AutofillAgent::FormControlElementClicked( ...@@ -957,6 +967,8 @@ void AutofillAgent::FormControlElementClicked(
options.show_full_suggestion_list = element.IsAutofilled() || was_focused; options.show_full_suggestion_list = element.IsAutofilled() || was_focused;
ShowSuggestions(element, options); ShowSuggestions(element, options);
SendPotentiallySubmittedFormToBrowser();
} }
void AutofillAgent::HandleFocusChangeComplete() { void AutofillAgent::HandleFocusChangeComplete() {
...@@ -976,10 +988,14 @@ void AutofillAgent::HandleFocusChangeComplete() { ...@@ -976,10 +988,14 @@ void AutofillAgent::HandleFocusChangeComplete() {
was_focused_before_now_ = true; was_focused_before_now_ = true;
focused_node_was_last_clicked_ = false; focused_node_was_last_clicked_ = false;
SendPotentiallySubmittedFormToBrowser();
} }
void AutofillAgent::AjaxSucceeded() { void AutofillAgent::AjaxSucceeded() {
form_tracker_.AjaxSucceeded(); form_tracker_.AjaxSucceeded();
SendPotentiallySubmittedFormToBrowser();
} }
void AutofillAgent::OnProvisionallySaveForm( void AutofillAgent::OnProvisionallySaveForm(
...@@ -995,7 +1011,6 @@ void AutofillAgent::OnProvisionallySaveForm( ...@@ -995,7 +1011,6 @@ void AutofillAgent::OnProvisionallySaveForm(
FireHostSubmitEvents(form, /*known_success=*/false, FireHostSubmitEvents(form, /*known_success=*/false,
SubmissionSource::FORM_SUBMISSION); SubmissionSource::FORM_SUBMISSION);
ResetLastInteractedElements(); ResetLastInteractedElements();
return;
} else if (source == ElementChangeSource::TEXTFIELD_CHANGED || } else if (source == ElementChangeSource::TEXTFIELD_CHANGED ||
source == ElementChangeSource::SELECT_CHANGED) { source == ElementChangeSource::SELECT_CHANGED) {
// Remember the last form the user interacted with. // Remember the last form the user interacted with.
...@@ -1034,16 +1049,18 @@ void AutofillAgent::OnProvisionallySaveForm( ...@@ -1034,16 +1049,18 @@ void AutofillAgent::OnProvisionallySaveForm(
} }
} }
} }
SendPotentiallySubmittedFormToBrowser();
} }
void AutofillAgent::OnProbablyFormSubmitted() { void AutofillAgent::OnProbablyFormSubmitted() {
FormData form_data; base::Optional<FormData> form_data = GetSubmittedForm();
if (GetSubmittedForm(&form_data)) { if (form_data.has_value()) {
FireHostSubmitEvents(form_data, /*known_success=*/false, FireHostSubmitEvents(form_data.value(), /*known_success=*/false,
SubmissionSource::PROBABLY_FORM_SUBMITTED); SubmissionSource::PROBABLY_FORM_SUBMITTED);
} }
ResetLastInteractedElements(); ResetLastInteractedElements();
OnFormNoLongerSubmittable(); OnFormNoLongerSubmittable();
SendPotentiallySubmittedFormToBrowser();
} }
void AutofillAgent::OnFormSubmitted(const WebFormElement& form) { void AutofillAgent::OnFormSubmitted(const WebFormElement& form) {
...@@ -1053,6 +1070,7 @@ void AutofillAgent::OnFormSubmitted(const WebFormElement& form) { ...@@ -1053,6 +1070,7 @@ void AutofillAgent::OnFormSubmitted(const WebFormElement& form) {
SubmissionSource::FORM_SUBMISSION); SubmissionSource::FORM_SUBMISSION);
ResetLastInteractedElements(); ResetLastInteractedElements();
OnFormNoLongerSubmittable(); OnFormNoLongerSubmittable();
SendPotentiallySubmittedFormToBrowser();
} }
void AutofillAgent::OnInferredFormSubmission(SubmissionSource source) { void AutofillAgent::OnInferredFormSubmission(SubmissionSource source) {
...@@ -1064,6 +1082,7 @@ void AutofillAgent::OnInferredFormSubmission(SubmissionSource source) { ...@@ -1064,6 +1082,7 @@ void AutofillAgent::OnInferredFormSubmission(SubmissionSource source) {
render_frame()->GetWebFrame()->Parent())) { render_frame()->GetWebFrame()->Parent())) {
ResetLastInteractedElements(); ResetLastInteractedElements();
OnFormNoLongerSubmittable(); OnFormNoLongerSubmittable();
SendPotentiallySubmittedFormToBrowser();
return; return;
} }
...@@ -1074,12 +1093,13 @@ void AutofillAgent::OnInferredFormSubmission(SubmissionSource source) { ...@@ -1074,12 +1093,13 @@ void AutofillAgent::OnInferredFormSubmission(SubmissionSource source) {
FireHostSubmitEvents(provisionally_saved_form_.value(), FireHostSubmitEvents(provisionally_saved_form_.value(),
/*known_success=*/true, source); /*known_success=*/true, source);
} else { } else {
FormData form_data; base::Optional<FormData> form_data = GetSubmittedForm();
if (GetSubmittedForm(&form_data)) if (form_data.has_value())
FireHostSubmitEvents(form_data, /*known_success=*/true, source); FireHostSubmitEvents(form_data.value(), /*known_success=*/true, source);
} }
ResetLastInteractedElements(); ResetLastInteractedElements();
OnFormNoLongerSubmittable(); OnFormNoLongerSubmittable();
SendPotentiallySubmittedFormToBrowser();
} }
void AutofillAgent::AddFormObserver(Observer* observer) { void AutofillAgent::AddFormObserver(Observer* observer) {
...@@ -1090,14 +1110,14 @@ void AutofillAgent::RemoveFormObserver(Observer* observer) { ...@@ -1090,14 +1110,14 @@ void AutofillAgent::RemoveFormObserver(Observer* observer) {
form_tracker_.RemoveObserver(observer); form_tracker_.RemoveObserver(observer);
} }
bool AutofillAgent::GetSubmittedForm(FormData* form) { base::Optional<FormData> AutofillAgent::GetSubmittedForm() const {
if (!last_interacted_form_.IsNull()) { if (!last_interacted_form_.IsNull()) {
FormData form;
if (form_util::ExtractFormData(last_interacted_form_, if (form_util::ExtractFormData(last_interacted_form_,
*field_data_manager_.get(), form)) { *field_data_manager_.get(), &form)) {
return true; return base::make_optional(form);
} else if (provisionally_saved_form_.has_value()) { } else if (provisionally_saved_form_.has_value()) {
*form = provisionally_saved_form_.value(); return base::make_optional(provisionally_saved_form_.value());
return true;
} }
} else if (formless_elements_user_edited_.size() != 0 && } else if (formless_elements_user_edited_.size() != 0 &&
!form_util::IsSomeControlElementVisible( !form_util::IsSomeControlElementVisible(
...@@ -1106,14 +1126,18 @@ bool AutofillAgent::GetSubmittedForm(FormData* form) { ...@@ -1106,14 +1126,18 @@ bool AutofillAgent::GetSubmittedForm(FormData* form) {
// to decide if submission has occurred, and use the // to decide if submission has occurred, and use the
// provisionally_saved_form_ saved in OnProvisionallySaveForm() if fail to // provisionally_saved_form_ saved in OnProvisionallySaveForm() if fail to
// construct form. // construct form.
if (CollectFormlessElements(form)) { FormData form;
return true; if (CollectFormlessElements(&form)) {
return base::make_optional(form);
} else if (provisionally_saved_form_.has_value()) { } else if (provisionally_saved_form_.has_value()) {
*form = provisionally_saved_form_.value(); return base::make_optional(provisionally_saved_form_.value());
return true;
} }
} }
return false; return base::nullopt;
}
void AutofillAgent::SendPotentiallySubmittedFormToBrowser() {
GetAutofillDriver()->SetFormToBeProbablySubmitted(GetSubmittedForm());
} }
void AutofillAgent::ResetLastInteractedElements() { void AutofillAgent::ResetLastInteractedElements() {
......
...@@ -206,7 +206,7 @@ class AutofillAgent : public content::RenderFrameObserver, ...@@ -206,7 +206,7 @@ class AutofillAgent : public content::RenderFrameObserver,
// Helper method which collects unowned elements (i.e., those not inside a // Helper method which collects unowned elements (i.e., those not inside a
// form tag) and writes them into |output|. Returns true if the process is // form tag) and writes them into |output|. Returns true if the process is
// successful, and all conditions for firing events are true. // successful, and all conditions for firing events are true.
bool CollectFormlessElements(FormData* output); bool CollectFormlessElements(FormData* output) const;
FRIEND_TEST_ALL_PREFIXES(FormAutocompleteTest, CollectFormlessElements); FRIEND_TEST_ALL_PREFIXES(FormAutocompleteTest, CollectFormlessElements);
void OnTextFieldDidChange(const blink::WebInputElement& element); void OnTextFieldDidChange(const blink::WebInputElement& element);
...@@ -244,7 +244,10 @@ class AutofillAgent : public content::RenderFrameObserver, ...@@ -244,7 +244,10 @@ class AutofillAgent : public content::RenderFrameObserver,
// Attempt to get submitted FormData from last_interacted_form_ or // Attempt to get submitted FormData from last_interacted_form_ or
// provisionally_saved_form_, return true if |form| is set. // provisionally_saved_form_, return true if |form| is set.
bool GetSubmittedForm(FormData* form); base::Optional<FormData> GetSubmittedForm() const;
// Pushes the value of GetSubmittedForm() to the AutofillDriver.
void SendPotentiallySubmittedFormToBrowser();
void ResetLastInteractedElements(); void ResetLastInteractedElements();
void UpdateLastInteractedForm(blink::WebFormElement form); void UpdateLastInteractedForm(blink::WebFormElement form);
......
...@@ -5,7 +5,9 @@ ...@@ -5,7 +5,9 @@
#include "components/autofill/content/renderer/form_tracker.h" #include "components/autofill/content/renderer/form_tracker.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/feature_list.h"
#include "components/autofill/content/renderer/form_autofill_util.h" #include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/core/common/autofill_features.h"
#include "content/public/renderer/document_state.h" #include "content/public/renderer/document_state.h"
#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame.h"
#include "third_party/blink/public/web/modules/autofill/web_form_element_observer.h" #include "third_party/blink/public/web/modules/autofill/web_form_element_observer.h"
...@@ -190,6 +192,11 @@ void FormTracker::FireFormSubmitted(const blink::WebFormElement& form) { ...@@ -190,6 +192,11 @@ void FormTracker::FireFormSubmitted(const blink::WebFormElement& form) {
} }
void FormTracker::FireProbablyFormSubmitted() { void FormTracker::FireProbablyFormSubmitted() {
if (base::FeatureList::IsEnabled(
features::kAutofillProbableFormSubmissionInBrowser)) {
return;
}
for (auto& observer : observers_) for (auto& observer : observers_)
observer.OnProbablyFormSubmitted(); observer.OnProbablyFormSubmitted();
ResetLastInteractedElements(); ResetLastInteractedElements();
......
...@@ -29,6 +29,14 @@ const base::Feature kAutofillAddressEnhancementVotes{ ...@@ -29,6 +29,14 @@ const base::Feature kAutofillAddressEnhancementVotes{
const base::Feature kAutofillAddressNormalizer{ const base::Feature kAutofillAddressNormalizer{
"AutofillAddressNormalizer", base::FEATURE_ENABLED_BY_DEFAULT}; "AutofillAddressNormalizer", base::FEATURE_ENABLED_BY_DEFAULT};
// By default, AutofillAgent and, if |kAutofillProbableFormSubmissionInBrowser|
// is enabled, also ContentAutofillDriver omit duplicate form submissions, even
// though the form's data may have changed substantially. If enabled, the
// below feature allows duplicate form submissions.
// TODO(crbug/1117451): Remove once the form-submission experiment is over.
const base::Feature kAutofillAllowDuplicateFormSubmissions{
"AutofillAllowDuplicateFormSubmissions", base::FEATURE_DISABLED_BY_DEFAULT};
// Controls if a full country name instead of a country code in a field with a // Controls if a full country name instead of a country code in a field with a
// type derived from HTML_TYPE_COUNTRY_CODE can be used to set the profile // type derived from HTML_TYPE_COUNTRY_CODE can be used to set the profile
// country. // country.
...@@ -167,6 +175,13 @@ const base::Feature kAutofillPreferServerNamePredictions{ ...@@ -167,6 +175,13 @@ const base::Feature kAutofillPreferServerNamePredictions{
const base::Feature kAutofillPreventMixedFormsFilling{ const base::Feature kAutofillPreventMixedFormsFilling{
"AutofillPreventMixedFormsFilling", base::FEATURE_DISABLED_BY_DEFAULT}; "AutofillPreventMixedFormsFilling", base::FEATURE_DISABLED_BY_DEFAULT};
// If the feature is enabled, FormTracker's probable-form-submission detection
// is disabled and replaced with browser-side detection.
// TODO(crbug/1117451): Remove once it works.
const base::Feature kAutofillProbableFormSubmissionInBrowser{
"AutofillProbableFormSubmissionInBrowser",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::Feature kAutofillProfileClientValidation{ const base::Feature kAutofillProfileClientValidation{
"AutofillProfileClientValidation", base::FEATURE_DISABLED_BY_DEFAULT}; "AutofillProfileClientValidation", base::FEATURE_DISABLED_BY_DEFAULT};
......
...@@ -23,6 +23,7 @@ namespace features { ...@@ -23,6 +23,7 @@ namespace features {
// All features in alphabetical order. // All features in alphabetical order.
extern const base::Feature kAutofillAddressEnhancementVotes; extern const base::Feature kAutofillAddressEnhancementVotes;
extern const base::Feature kAutofillAddressNormalizer; extern const base::Feature kAutofillAddressNormalizer;
extern const base::Feature kAutofillAllowDuplicateFormSubmissions;
extern const base::Feature kAutofillAllowHtmlTypeCountryCodesWithFullNames; extern const base::Feature kAutofillAllowHtmlTypeCountryCodesWithFullNames;
extern const base::Feature kAutofillAllowNonHttpActivation; extern const base::Feature kAutofillAllowNonHttpActivation;
extern const base::Feature kAutofillAlwaysFillAddresses; extern const base::Feature kAutofillAlwaysFillAddresses;
...@@ -49,6 +50,7 @@ extern const base::Feature kAutofillOffNoServerData; ...@@ -49,6 +50,7 @@ extern const base::Feature kAutofillOffNoServerData;
extern const base::Feature kAutofillOverrideWithRaterConsensus; extern const base::Feature kAutofillOverrideWithRaterConsensus;
extern const base::Feature kAutofillPreferServerNamePredictions; extern const base::Feature kAutofillPreferServerNamePredictions;
extern const base::Feature kAutofillPreventMixedFormsFilling; extern const base::Feature kAutofillPreventMixedFormsFilling;
extern const base::Feature kAutofillProbableFormSubmissionInBrowser;
extern const base::Feature kAutofillProfileClientValidation; extern const base::Feature kAutofillProfileClientValidation;
extern const base::Feature kAutofillProfileImportFromUnfocusableFields; extern const base::Feature kAutofillProfileImportFromUnfocusableFields;
extern const base::Feature kAutofillProfileImportFromUnifiedSection; extern const base::Feature kAutofillProfileImportFromUnifiedSection;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment