blob: 01b8e9d823efd3c3dff6cf99d7b534e2d65216b8 [file] [log] [blame]
// Copyright 2025 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/compiler_specific.h"
#include "base/test/run_until.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/autofill/content/browser/content_autofill_driver.h"
#include "components/autofill/content/browser/test_autofill_client_injector.h"
#include "components/autofill/content/browser/test_autofill_manager_injector.h"
#include "components/autofill/content/browser/test_content_autofill_client.h"
#include "components/autofill/core/browser/country_type.h"
#include "components/autofill/core/browser/data_model/addresses/autofill_profile.h"
#include "components/autofill/core/browser/data_model/payments/credit_card.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/common/autofill_test_utils.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/optimization_guide/content/browser/page_content_proto_provider.h"
#include "components/optimization_guide/content/browser/page_content_proto_util.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/optimization_guide/proto/features/common_quality_data.pb.h"
#include "content/public/test/browser_test.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features_generated.h"
namespace autofill {
namespace {
base::FilePath GetTestDataDir() {
return base::FilePath(FILE_PATH_LITERAL("components/test/data/autofill"));
}
blink::mojom::AIPageContentOptionsPtr GetAIPageContentOptions() {
return optimization_guide::DefaultAIPageContentOptions(
/*on_critical_path =*/true);
}
} // namespace
// AutofillAnnotationsProvider is a concept of the "content/" layer. But
// because we don't have a fully set up Autofill at the "content/" layer,
// these tests live in chrome/browser/autofill.
class AutofillAnnotationsProviderBrowserTest : public InProcessBrowserTest {
public:
class TestAutofillManager : public BrowserAutofillManager {
public:
explicit TestAutofillManager(autofill::ContentAutofillDriver* driver)
: BrowserAutofillManager(driver) {}
[[nodiscard]] testing::AssertionResult WaitForFormsSeen(
int min_num_awaited_calls) {
return forms_seen_waiter_.Wait(min_num_awaited_calls);
}
private:
autofill::TestAutofillManagerWaiter forms_seen_waiter_{
*this,
{autofill::AutofillManagerEvent::kFormsSeen}};
};
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
https_server_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTPS);
https_server_->AddDefaultHandlers(GetTestDataDir());
content::SetupCrossSiteRedirector(https_server_.get());
ASSERT_TRUE(https_server_->Start());
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
// HTTPS server only serves a valid cert for localhost, so this is needed
// to load pages from other hosts without an error.
command_line->AppendSwitch(::switches::kIgnoreCertificateErrors);
command_line->AppendSwitchASCII(::switches::kForceDeviceScaleFactor, "1.0");
}
void SetPageContent(
base::OnceClosure quit_closure,
std::optional<optimization_guide::AIPageContentResult> page_content) {
page_content_ = std::move(page_content->proto);
std::move(quit_closure).Run();
}
optimization_guide::proto::AnnotatedPageContent& page_content() {
return *page_content_;
}
void LoadData() {
base::RunLoop run_loop;
GetAIPageContent(
web_contents(), GetAIPageContentOptions(),
base::BindOnce(&AutofillAnnotationsProviderBrowserTest::SetPageContent,
base::Unretained(this), run_loop.QuitClosure()));
run_loop.Run();
CHECK(page_content_);
}
void LoadPage(GURL url, int num_expected_forms) {
content::NavigateToURLBlockUntilNavigationsComplete(web_contents(), url, 1);
{
base::test::TestFuture<bool> future;
web_contents()
->GetPrimaryMainFrame()
->GetRenderWidgetHost()
->InsertVisualStateCallback(future.GetCallback());
ASSERT_TRUE(future.Wait()) << "Timeout waiting for syncing with renderer";
}
ASSERT_TRUE(autofill_manager()->WaitForFormsSeen(num_expected_forms));
LoadData();
}
// Returns pointers to all `FormControlData` elements in `page_content`.
std::vector<const optimization_guide::proto::FormControlData*>
GetFormControlDatas(const optimization_guide::proto::AnnotatedPageContent&
page_content LIFETIME_BOUND) {
std::vector<const optimization_guide::proto::FormControlData*>
form_control_datas;
optimization_guide::VisitContentNodes(
page_content.root_node(),
page_content.main_frame_data().document_identifier().serialized_token(),
[&](const optimization_guide::proto::ContentNode& node,
std::string_view document_identifier) {
if (node.content_attributes().attribute_type() ==
optimization_guide::proto::CONTENT_ATTRIBUTE_FORM_CONTROL) {
form_control_datas.push_back(
&node.content_attributes().form_control_data());
}
});
return form_control_datas;
}
net::EmbeddedTestServer* https_server() { return https_server_.get(); }
content::WebContents* web_contents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
TestAutofillManager* autofill_manager() {
return autofill_manager(web_contents()->GetPrimaryMainFrame());
}
TestAutofillManager* autofill_manager(content::RenderFrameHost* rfh) {
return autofill_manager_injector_[rfh];
}
TestContentAutofillClient* client() {
return autofill_client_injector_[web_contents()];
}
protected:
autofill::test::AutofillBrowserTestEnvironment autofill_test_environment_;
TestAutofillClientInjector<TestContentAutofillClient>
autofill_client_injector_;
autofill::TestAutofillManagerInjector<TestAutofillManager>
autofill_manager_injector_;
std::unique_ptr<net::EmbeddedTestServer> https_server_;
std::optional<optimization_guide::proto::AnnotatedPageContent> page_content_;
};
// Verifies that the `autofill_section_id` and `coarse_autofill_field_type`
// attributes of a FormControlData are correctly set by Autofill.
IN_PROC_BROWSER_TEST_F(AutofillAnnotationsProviderBrowserTest,
AutofillFormAnnotations) {
LoadPage(https_server()->GetURL("/address_sections_and_creditcard.html"),
/*num_expected_forms=*/1);
std::vector<const optimization_guide::proto::FormControlData*>
form_control_datas = GetFormControlDatas(page_content());
ASSERT_EQ(form_control_datas.size(), 15u);
for (int i = 0; i < 14; ++i) {
SCOPED_TRACE(testing::Message() << "Field " << i);
// Shipping address:
if (0 <= i && i <= 4) {
EXPECT_EQ(form_control_datas[i]->autofill_section_id(), 0u);
ASSERT_EQ(form_control_datas[i]->coarse_autofill_field_type_size(), 1);
EXPECT_EQ(form_control_datas[i]->coarse_autofill_field_type(0),
optimization_guide::proto::COARSE_AUTOFILL_FIELD_TYPE_ADDRESS);
}
// Billing address:
if (5 <= i && i <= 9) {
EXPECT_EQ(form_control_datas[i]->autofill_section_id(), 1u);
ASSERT_EQ(form_control_datas[i]->coarse_autofill_field_type_size(), 1);
EXPECT_EQ(form_control_datas[i]->coarse_autofill_field_type(0),
optimization_guide::proto::COARSE_AUTOFILL_FIELD_TYPE_ADDRESS);
}
// Credit card:
if (10 <= i && i <= 13) {
EXPECT_EQ(form_control_datas[i]->autofill_section_id(), 2u);
ASSERT_EQ(form_control_datas[i]->coarse_autofill_field_type_size(), 1);
EXPECT_EQ(
form_control_datas[i]->coarse_autofill_field_type(0),
optimization_guide::proto::COARSE_AUTOFILL_FIELD_TYPE_CREDIT_CARD);
}
}
// Submit button:
EXPECT_FALSE(form_control_datas[14]->has_autofill_section_id());
EXPECT_EQ(form_control_datas[14]->coarse_autofill_field_type_size(), 0);
}
// Verifies that the `autofill_section_id` and `coarse_autofill_field_type`
// attributes of a FormControlData are correctly set by Autofill in case the
// main frame contains a <form> with an embedded <iframe>.
IN_PROC_BROWSER_TEST_F(AutofillAnnotationsProviderBrowserTest,
AutofillFormAnnotationsIframe) {
LoadPage(
https_server()->GetURL("/address_sections_and_creditcard_iframe.html"),
/*num_expected_forms=*/2);
std::vector<const optimization_guide::proto::FormControlData*>
form_control_datas = GetFormControlDatas(page_content());
ASSERT_EQ(form_control_datas.size(), 17u);
for (int i = 0; i < 15; ++i) {
SCOPED_TRACE(testing::Message() << "Field " << i);
// Shipping address:
if (0 <= i && i <= 4) {
EXPECT_EQ(form_control_datas[i]->autofill_section_id(), 0u);
ASSERT_EQ(form_control_datas[i]->coarse_autofill_field_type_size(), 1);
EXPECT_EQ(form_control_datas[i]->coarse_autofill_field_type(0),
optimization_guide::proto::COARSE_AUTOFILL_FIELD_TYPE_ADDRESS);
}
// Billing address:
if (5 <= i && i <= 9) {
EXPECT_EQ(form_control_datas[i]->autofill_section_id(), 1u);
ASSERT_EQ(form_control_datas[i]->coarse_autofill_field_type_size(), 1);
EXPECT_EQ(form_control_datas[i]->coarse_autofill_field_type(0),
optimization_guide::proto::COARSE_AUTOFILL_FIELD_TYPE_ADDRESS);
}
// Credit card:
if (10 <= i && i <= 14) {
EXPECT_EQ(form_control_datas[i]->autofill_section_id(), 2u);
ASSERT_EQ(form_control_datas[i]->coarse_autofill_field_type_size(), 1);
EXPECT_EQ(
form_control_datas[i]->coarse_autofill_field_type(0),
optimization_guide::proto::COARSE_AUTOFILL_FIELD_TYPE_CREDIT_CARD);
}
// A bunch of buttons.
if (15 <= i && i <= 16) {
EXPECT_FALSE(form_control_datas[i]->has_autofill_section_id());
EXPECT_EQ(form_control_datas[i]->coarse_autofill_field_type_size(), 0);
}
}
}
// Verifies that availability of autofill data is propagated in the
// `autofill_information` attribute in `AnnotatedPageContents`.
IN_PROC_BROWSER_TEST_F(AutofillAnnotationsProviderBrowserTest,
AutofillAvailabilityInformation) {
client()->GetPersonalDataManager().address_data_manager().AddProfile(
AutofillProfile(AutofillProfile::RecordType::kAccount,
AddressCountryCode("ES")));
CreditCard card;
test::SetCreditCardInfo(&card, "Elvis Presley", "4111111111111111", "12",
test::NextYear().c_str(), "123");
client()->GetPersonalDataManager().payments_data_manager().AddCreditCard(
card);
LoadPage(https_server()->GetURL("/address_sections_and_creditcard.html"),
/*num_expected_forms=*/1);
ASSERT_TRUE(page_content().has_profile_information());
ASSERT_TRUE(page_content().profile_information().has_autofill_information());
const auto& autofill_info =
page_content().profile_information().autofill_information();
EXPECT_THAT(
autofill_info.fillable_data(),
testing::UnorderedElementsAre(
optimization_guide::proto::AutofillInformation_FillableData_ADDRESS,
optimization_guide::proto::
AutofillInformation_FillableData_CREDIT_CARD));
}
class AutofillAnnotationsProviderRedactionBrowserTest
: public AutofillAnnotationsProviderBrowserTest,
public testing::WithParamInterface<bool> {
public:
AutofillAnnotationsProviderRedactionBrowserTest() {
if (ShouldRedact()) {
feature_list_.InitAndEnableFeature(
optimization_guide::features::
kAnnotatedPageContentAutofillCreditCardRedactions);
} else {
feature_list_.InitAndDisableFeature(
optimization_guide::features::
kAnnotatedPageContentAutofillCreditCardRedactions);
}
}
bool ShouldRedact() const { return GetParam(); }
private:
base::test::ScopedFeatureList feature_list_;
};
// Verifies that sensitive fields are correctly redacted by Autofill.
IN_PROC_BROWSER_TEST_P(AutofillAnnotationsProviderRedactionBrowserTest,
Redaction) {
LoadPage(
https_server()->GetURL("/address_sections_and_creditcard_filled.html"),
/*num_expected_forms=*/1);
std::vector<const optimization_guide::proto::FormControlData*>
form_control_datas = GetFormControlDatas(page_content());
// Find the fields and check their redaction status.
const optimization_guide::proto::FormControlData* name_full_field = nullptr;
const optimization_guide::proto::FormControlData* address_field = nullptr;
const optimization_guide::proto::FormControlData* phone_field = nullptr;
const optimization_guide::proto::FormControlData* cc_name_field = nullptr;
const optimization_guide::proto::FormControlData* cc_number_field = nullptr;
const optimization_guide::proto::FormControlData* cc_exp_field = nullptr;
for (const auto* field : form_control_datas) {
if (field->field_name() == "shipping-name") {
name_full_field = field;
} else if (field->field_name() == "shipping-address") {
address_field = field;
} else if (field->field_name() == "shipping-phone") {
phone_field = field;
} else if (field->field_name() == "cc-name") {
cc_name_field = field;
} else if (field->field_name() == "cc-number") {
cc_number_field = field;
} else if (field->field_name() == "cc-exp") {
cc_exp_field = field;
}
}
ASSERT_TRUE(name_full_field);
EXPECT_EQ(
name_full_field->redaction_decision(),
optimization_guide::proto::REDACTION_DECISION_NO_REDACTION_NECESSARY);
EXPECT_EQ(name_full_field->field_value(), "John Doe");
ASSERT_TRUE(address_field);
EXPECT_EQ(
address_field->redaction_decision(),
optimization_guide::proto::REDACTION_DECISION_NO_REDACTION_NECESSARY);
EXPECT_EQ(address_field->field_value(), "123 Main St");
ASSERT_TRUE(phone_field);
EXPECT_EQ(
phone_field->redaction_decision(),
optimization_guide::proto::REDACTION_DECISION_NO_REDACTION_NECESSARY);
EXPECT_EQ(phone_field->field_value(), "555-555-5555");
ASSERT_TRUE(cc_name_field);
EXPECT_EQ(
cc_name_field->redaction_decision(),
optimization_guide::proto::REDACTION_DECISION_NO_REDACTION_NECESSARY);
EXPECT_EQ(cc_name_field->field_value(), "Jane Doe");
ASSERT_TRUE(cc_number_field);
if (ShouldRedact()) {
EXPECT_EQ(cc_number_field->redaction_decision(),
optimization_guide::proto::
REDACTION_DECISION_REDACTED_IS_SENSITIVE_PAYMENT_FIELD);
EXPECT_EQ(cc_number_field->field_value(), "");
} else {
EXPECT_EQ(
cc_number_field->redaction_decision(),
optimization_guide::proto::REDACTION_DECISION_NO_REDACTION_NECESSARY);
EXPECT_EQ(cc_number_field->field_value(), "4111111111111111");
}
ASSERT_TRUE(cc_exp_field);
if (ShouldRedact()) {
EXPECT_EQ(cc_exp_field->redaction_decision(),
optimization_guide::proto::
REDACTION_DECISION_REDACTED_IS_SENSITIVE_PAYMENT_FIELD);
EXPECT_EQ(cc_exp_field->field_value(), "");
} else {
EXPECT_EQ(
cc_exp_field->redaction_decision(),
optimization_guide::proto::REDACTION_DECISION_NO_REDACTION_NECESSARY);
EXPECT_EQ(cc_exp_field->field_value(), "12/2030");
}
}
INSTANTIATE_TEST_SUITE_P(All,
AutofillAnnotationsProviderRedactionBrowserTest,
testing::Bool());
} // namespace autofill