blob: 601bbd273337a63b0f67e4a888f7e1fcce5b7392 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/check.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/mock_callback.h"
#include "base/time/time.h"
#include "base/version_info/version_info.h"
#include "build/build_config.h"
#include "chrome/browser/autofill/autocomplete_history_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/webdata_services/web_data_service_factory.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/autofill/content/browser/content_autofill_driver.h"
#include "components/autofill/content/browser/test_autofill_manager_injector.h"
#include "components/autofill/core/browser/data_manager/personal_data_manager_test_utils.h"
#include "components/autofill/core/browser/foundations/browser_autofill_manager.h"
#include "components/autofill/core/browser/foundations/test_autofill_manager_waiter.h"
#include "components/autofill/core/browser/single_field_fillers/autocomplete/autocomplete_history_manager.h"
#include "components/autofill/core/browser/suggestions/suggestion.h"
#include "components/autofill/core/browser/suggestions/suggestion_test_helpers.h"
#include "components/autofill/core/browser/suggestions/suggestions_context.h"
#include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
#include "components/autofill/core/browser/test_utils/test_autofill_clock.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/pref_test_utils.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/switches.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
namespace autofill {
namespace {
using ::base::UTF8ToUTF16;
using ::testing::_;
using ::testing::AssertionResult;
using ::testing::ElementsAre;
using ::testing::Field;
using ::testing::IsEmpty;
const char kDefaultAutocompleteInputId[] = "n300";
const char kSimpleFormFileName[] = "autocomplete_simple_form.html";
class AutocompleteTest : public InProcessBrowserTest {
public:
class TestAutofillManager : public BrowserAutofillManager {
public:
explicit TestAutofillManager(ContentAutofillDriver* driver)
: BrowserAutofillManager(driver) {}
TestAutofillManagerWaiter& text_field_change_waiter() {
return text_field_change_waiter_;
}
TestAutofillManagerWaiter& forms_seen_waiter() {
return forms_seen_waiter_;
}
TestAutofillManagerWaiter& form_submitted_waiter() {
return form_submitted_waiter_;
}
private:
TestAutofillManagerWaiter text_field_change_waiter_{
*this,
{AutofillManagerEvent::kTextFieldValueChanged}};
TestAutofillManagerWaiter forms_seen_waiter_{
*this,
{AutofillManagerEvent::kFormsSeen}};
TestAutofillManagerWaiter form_submitted_waiter_{
*this,
{AutofillManagerEvent::kFormSubmitted}};
};
TestAutofillManager* autofill_manager() {
return autofill_manager(web_contents()->GetPrimaryMainFrame());
}
TestAutofillManager* autofill_manager(content::RenderFrameHost* rfh) {
return autofill_manager_injector_[rfh];
}
protected:
void SetUpOnMainThread() override {
active_browser_ = browser();
ASSERT_TRUE(embedded_test_server()->Start());
}
void TearDownOnMainThread() override {
// RunUntilIdle() is necessary because otherwise, under the hood
// PasswordFormManager::OnFetchComplete() callback is run after this test is
// destroyed meaning that OsCryptImpl will be used instead of OsCryptMocker,
// causing this test to fail.
base::RunLoop().RunUntilIdle();
// Make sure to close any showing popups prior to tearing down the UI.
ContentAutofillDriver::GetForRenderFrameHost(
web_contents()->GetPrimaryMainFrame())
->GetAutofillManager()
.client()
.HideAutofillSuggestions(SuggestionHidingReason::kTabGone);
active_browser_ = nullptr;
}
// Necessary to avoid flakiness or failure due to input arriving
// before the first compositor commit.
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(blink::switches::kAllowPreCommitInput);
}
// Uses the browser to open the file named `filename` based on the given
// `disposition`.
void NavigateToFile(const char* filename,
const WindowOpenDisposition& disposition =
WindowOpenDisposition::NEW_FOREGROUND_TAB) {
GURL url =
embedded_test_server()->GetURL(base::StrCat({"/autofill/", filename}));
NavigateParams params(active_browser_, url, ui::PAGE_TRANSITION_LINK);
params.disposition = disposition;
ui_test_utils::NavigateToURL(&params);
}
// Fills in the default input with |value|, submits the form and waits
// for the value to have been saved in the DB or skipped, via observers.
void FillInputAndSubmit(const std::string& value) {
const std::string js =
base::StringPrintf(R"(document.getElementById('%s').focus();
document.onclick = function() {
document.getElementById('testform').submit();
};)",
kDefaultAutocompleteInputId);
ASSERT_TRUE(content::ExecJs(web_contents(), js));
content::SimulateEndOfPaintHoldingOnPrimaryMainFrame(web_contents());
for (const char c : value) {
ui::DomKey key = ui::DomKey::FromCharacter(c);
ui::DomCode code = UsLayoutDomKeyToDomCode(key);
ui::KeyboardCode key_code = DomCodeToUsLayoutKeyboardCode(code);
content::SimulateKeyPress(web_contents(), key, code, key_code, false,
false, false, false);
ASSERT_TRUE(autofill_manager()->text_field_change_waiter().Wait(1));
}
// Simulate a mouse click to submit the form because form submissions not
// triggered by user gestures are ignored.
content::SimulateMouseClick(
active_browser_->tab_strip_model()->GetActiveWebContents(), 0,
blink::WebMouseEvent::Button::kLeft);
ASSERT_TRUE(autofill_manager()->form_submitted_waiter().Wait(1));
// Wait for data to have been saved in the DB.
WaitForPendingDBTasks(*GetWebDataService());
}
// The retention policy clean-up is run once per major version during
// initialization. This function triggers it by reinitializing the
// `autocomplete_history_manager()` and waiting for the cleanup to complete.
void TriggerRetentionPolicyCleanup() {
pref_service()->SetInteger(prefs::kAutocompleteLastVersionRetentionPolicy,
version_info::GetMajorVersionNumberAsInt() - 1);
autocomplete_history_manager()->Init(
WebDataServiceFactory::GetAutofillWebDataForProfile(
current_profile(), ServiceAccessType::EXPLICIT_ACCESS),
pref_service(), current_profile()->IsOffTheRecord());
WaitForPendingDBTasks(*GetWebDataService());
}
void set_active_browser(Browser* browser) { active_browser_ = browser; }
AutocompleteHistoryManager* autocomplete_history_manager() {
return AutocompleteHistoryManagerFactory::GetForProfile(current_profile());
}
PrefService* pref_service() { return active_browser_->profile()->GetPrefs(); }
std::vector<Suggestion> GetAutocompleteSuggestions(
const std::string& input_name,
const std::string& prefix) {
base::MockCallback<SingleFieldFillRouter::OnSuggestionsReturnedCallback>
mock_callback;
std::vector<Suggestion> suggestions;
EXPECT_CALL(mock_callback, Run).WillOnce(testing::SaveArg<1>(&suggestions));
FormFieldData field = test::CreateTestFormField(
/*label=*/"", input_name, prefix, FormControlType::kInputText);
FormData form;
form.set_url(GURL("https://www.foo.com"));
form.set_fields({field});
autocomplete_history_manager()->OnGetSingleFieldSuggestions(
form, field, autofill_manager()->client(), mock_callback.Get());
// Make sure the DB task gets executed.
WaitForPendingDBTasks(*GetWebDataService());
return suggestions;
}
scoped_refptr<AutofillWebDataService> GetWebDataService() {
return WebDataServiceFactory::GetAutofillWebDataForProfile(
current_profile(), ServiceAccessType::EXPLICIT_ACCESS);
}
private:
content::WebContents* web_contents() {
return active_browser_->tab_strip_model()->GetActiveWebContents();
}
Profile* current_profile() { return active_browser_->profile(); }
test::AutofillBrowserTestEnvironment autofill_test_environment_;
TestAutofillManagerInjector<TestAutofillManager> autofill_manager_injector_;
raw_ptr<Browser> active_browser_ = nullptr;
};
// Tests that a user can save a simple Autocomplete value.
IN_PROC_BROWSER_TEST_F(AutocompleteTest, SubmitSimpleValue_Saves) {
std::string prefix = "Some";
std::string test_value = "SomeName!";
NavigateToFile(kSimpleFormFileName);
ASSERT_TRUE(autofill_manager()->forms_seen_waiter().Wait(1));
FillInputAndSubmit(test_value);
EXPECT_THAT(GetAutocompleteSuggestions(kDefaultAutocompleteInputId, prefix),
SuggestionVectorMainTextsAre(Suggestion::Text(
UTF8ToUTF16(test_value), Suggestion::Text::IsPrimary(true))));
}
// Tests that we don't save new autocomplete entries when in Incognito.
IN_PROC_BROWSER_TEST_F(AutocompleteTest,
SubmitSimpleValue_OTR_DoesNotSave) {
set_active_browser(CreateIncognitoBrowser());
std::string prefix = "Some";
std::string test_value = "SomeName!";
NavigateToFile(kSimpleFormFileName, WindowOpenDisposition::OFF_THE_RECORD);
ASSERT_TRUE(autofill_manager()->forms_seen_waiter().Wait(1));
FillInputAndSubmit(test_value);
EXPECT_THAT(GetAutocompleteSuggestions(kDefaultAutocompleteInputId, ""),
IsEmpty());
}
// Tests that we don't save new autocomplete entries when Autocomplete was
// disabled by the user.
IN_PROC_BROWSER_TEST_F(AutocompleteTest,
SubmitSimpleValue_Disabled_DoesNotSave) {
pref_service()->SetBoolean(prefs::kAutofillProfileEnabled, false);
std::string prefix = "Some";
std::string test_value = "SomeName!";
NavigateToFile(kSimpleFormFileName);
ASSERT_TRUE(autofill_manager()->forms_seen_waiter().Wait(1));
FillInputAndSubmit(test_value);
EXPECT_THAT(GetAutocompleteSuggestions(kDefaultAutocompleteInputId, ""),
IsEmpty());
}
// Tests that initialization of the AutocompleteHistoryManager sets the
// retention policy last version ran preference when the flag is enabled.
IN_PROC_BROWSER_TEST_F(AutocompleteTest,
RetentionPolicy_Init_SavesVersionPref) {
// Navigate to a file and wait, this will make sure we instantiate
// AutocompleteHistoryManager.
NavigateToFile(kSimpleFormFileName);
ASSERT_TRUE(autofill_manager()->forms_seen_waiter().Wait(1));
// The checkup is executed asynchronously on startup and may not have
// finished, yet.
WaitForPrefValue(pref_service(),
prefs::kAutocompleteLastVersionRetentionPolicy,
base::Value(version_info::GetMajorVersionNumberAsInt()));
int saved_version = pref_service()->GetInteger(
prefs::kAutocompleteLastVersionRetentionPolicy);
EXPECT_EQ(version_info::GetMajorVersionNumberAsInt(), saved_version);
}
// Tests that the retention policy cleanup removes an expired entry.
IN_PROC_BROWSER_TEST_F(AutocompleteTest,
RetentionPolicy_RemovesExpiredEntry) {
TestAutofillClock test_clock(AutofillClock::Now());
// Add an entry.
std::string prefix = "Some";
std::string test_value = "SomeName!";
NavigateToFile(kSimpleFormFileName);
ASSERT_TRUE(autofill_manager()->forms_seen_waiter().Wait(1));
FillInputAndSubmit(test_value);
EXPECT_THAT(GetAutocompleteSuggestions(kDefaultAutocompleteInputId, prefix),
SuggestionVectorMainTextsAre(Suggestion::Text(
UTF8ToUTF16(test_value), Suggestion::Text::IsPrimary(true))));
// Advance time to expire the entry.
test_clock.Advance(2 * kAutocompleteRetentionPolicyPeriod);
TriggerRetentionPolicyCleanup();
EXPECT_THAT(GetAutocompleteSuggestions(kDefaultAutocompleteInputId, ""),
IsEmpty());
}
// Tests that the retention policy cleanup does not remove a valid entry (e.g.
// 20 days old).
IN_PROC_BROWSER_TEST_F(AutocompleteTest,
RetentionPolicy_DoesNot_RemoveValidEntry) {
TestAutofillClock test_clock(AutofillClock::Now());
// Add an entry.
std::string prefix = "Some";
std::string test_value = "SomeName!";
NavigateToFile(kSimpleFormFileName);
ASSERT_TRUE(autofill_manager()->forms_seen_waiter().Wait(1));
FillInputAndSubmit(test_value);
EXPECT_THAT(GetAutocompleteSuggestions(kDefaultAutocompleteInputId, prefix),
SuggestionVectorMainTextsAre(Suggestion::Text(
UTF8ToUTF16(test_value), Suggestion::Text::IsPrimary(true))));
// Advance time by less than `kAutocompleteRetentionPolicyPeriod`.
test_clock.Advance(kAutocompleteRetentionPolicyPeriod - base::Days(2));
TriggerRetentionPolicyCleanup();
// Verify that the entry is still there.
EXPECT_THAT(GetAutocompleteSuggestions(kDefaultAutocompleteInputId, prefix),
SuggestionVectorMainTextsAre(Suggestion::Text(
UTF8ToUTF16(test_value), Suggestion::Text::IsPrimary(true))));
}
} // namespace
} // namespace autofill