blob: b2d714d641a30111389e1656d6df10a2928efb21 [file] [log] [blame]
// Copyright 2023 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/renderer/form_tracker.h"
#include "base/test/scoped_feature_list.h"
#include "components/autofill/content/renderer/autofill_agent_test_api.h"
#include "components/autofill/content/renderer/autofill_renderer_test.h"
#include "components/autofill/core/common/autofill_features.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/web/web_form_control_element.h"
#include "third_party/blink/public/web/web_local_frame.h"
namespace autofill {
namespace {
class MockFormTrackerObserver : public FormTracker::Observer {
public:
// Not a mock method so that gmock ignores calls to this method.
void OnProvisionallySaveForm(const blink::WebFormElement&,
const blink::WebFormControlElement&,
SaveFormReason) override {}
MOCK_METHOD0(OnProbablyFormSubmitted, void());
MOCK_METHOD1(OnFormSubmitted, void(const blink::WebFormElement&));
MOCK_METHOD1(OnInferredFormSubmission, void(mojom::SubmissionSource));
};
class FormTrackerTest : public test::AutofillRendererTest,
public testing::WithParamInterface<int> {
public:
FormTrackerTest() {
EXPECT_LE(GetParam(), 3);
std::vector<base::test::FeatureRef> features = {
features::kAutofillUnifyAndFixFormTracking,
features::kAutofillReplaceCachedWebElementsByRendererIds,
features::kAutofillReplaceFormElementObserver};
std::vector<base::test::FeatureRef> enabled_features(
features.begin(), features.begin() + GetParam());
std::vector<base::test::FeatureRef> disabled_features(
features.begin() + GetParam(), features.end());
scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
blink::WebFormControlElement GetFormControlById(const std::string& id) {
return GetMainFrame()
->GetDocument()
.GetElementById(blink::WebString::FromUTF8(id))
.DynamicTo<blink::WebFormControlElement>();
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(AutofillSubmissionTest,
FormTrackerTest,
::testing::Values(0, 1, 2, 3));
// Check that submission is detected on a page with no <form> when in sequence:
// 1) User types into a field.
// 2) Page does an XHR.
// 3) Page hides all of the inputs.
TEST_P(FormTrackerTest, FormlessXHRThenHide) {
LoadHTML("<!DOCTYPE HTML><input id='input1'><input id='input2'/>");
blink::WebFormControlElement input1 = GetFormControlById("input1");
testing::StrictMock<MockFormTrackerObserver> observer;
test_api(autofill_agent()).form_tracker().AddObserver(&observer);
GetMainFrame()->NotifyUserActivation(
blink::mojom::UserActivationNotificationType::kTest);
ExecuteJavaScriptForTests("document.getElementById('input1').focus();");
test_api(autofill_agent()).form_tracker().TextFieldDidChange(input1);
task_environment_.RunUntilIdle();
test_api(autofill_agent()).form_tracker().AjaxSucceeded();
task_environment_.RunUntilIdle();
// FormTracker should not think there is a submission because the <input>s are
// still visible.
// FormTracker should detect a submission after the <input>s are hidden.
EXPECT_CALL(observer, OnInferredFormSubmission).Times(1);
ExecuteJavaScriptForTests(
R"(document.getElementById('input1').style.display = 'none';
document.getElementById('input2').style.display = 'none';)");
ForceLayoutUpdate();
}
// Check that submission is detected on a page with no <form> when in sequence:
// 1) User types into a field.
// 2) Page hides all of the inputs.
// 3) Page does an XHR.
TEST_P(FormTrackerTest, FormlessHideThenXhr) {
LoadHTML("<!DOCTYPE HTML><input id='input1'><input id='input2'/>");
blink::WebFormControlElement input1 = GetFormControlById("input1");
testing::StrictMock<MockFormTrackerObserver> observer;
test_api(autofill_agent()).form_tracker().AddObserver(&observer);
GetMainFrame()->NotifyUserActivation(
blink::mojom::UserActivationNotificationType::kTest);
ExecuteJavaScriptForTests("document.getElementById('input1').focus();");
test_api(autofill_agent()).form_tracker().TextFieldDidChange(input1);
task_environment_.RunUntilIdle();
ExecuteJavaScriptForTests(
"document.getElementById('input1').style.display = 'none';"
"document.getElementById('input2').style.display = 'none';");
ForceLayoutUpdate();
task_environment_.RunUntilIdle();
// FormTracker should not think there is a submission because the page has not
// done any XHRs.
// FormTracker should detect a submission when the XHR succeeds.
EXPECT_CALL(observer, OnInferredFormSubmission).Times(1);
test_api(autofill_agent()).form_tracker().AjaxSucceeded();
task_environment_.RunUntilIdle();
}
} // namespace
} // namespace autofill