910e62b5创建于 1月15日历史提交
// 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 "base/check_deref.h"
#include "base/containers/to_vector.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/autofill/autofill_uitest_util.h"
#include "chrome/browser/devtools/protocol/devtools_protocol_test_support.h"
#include "chrome/browser/ui/browser.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/content/common/mojom/autofill_driver.mojom.h"
#include "components/autofill/core/browser/field_types.h"
#include "components/autofill/core/browser/foundations/browser_autofill_manager.h"
#include "components/autofill/core/browser/foundations/browser_autofill_manager_test_api.h"
#include "components/autofill/core/browser/foundations/mock_autofill_manager_observer.h"
#include "components/autofill/core/browser/foundations/test_autofill_manager_waiter.h"
#include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
#include "components/autofill/core/browser/ui/addresses/autofill_address_util.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_data_test_api.h"
#include "components/autofill/core/common/unique_ids.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gmock/include/gmock/gmock.h"

using testing::_;
using testing::Eq;
using testing::Not;
using testing::ResultOf;

namespace autofill {
namespace {

// Asserts that a filled field sent to devtools has `attribute` set with
// `expected_value`.
auto FilledFieldHasAttributeWithValue(const std::string& attribute,
                                      const std::string& expected_value) {
  return ResultOf(
      [&](const base::Value& filled_field) {
        const std::string* value =
            filled_field.GetDict().FindStringByDottedPath(attribute);
        return value ? *value : "";
      },
      Eq(expected_value));
}

// Returns the all the `autofill::FieldType`s used to build an UI representation
// of an address. These values are the same as the ones used in the settings
// page and edit dialog, furthermore, they depend on a profile's `country_code`.
std::set<autofill::FieldType>
GetExpectedFieldTypesToBuildAddressUiForCountryCode(
    const std::string& country_code) {
  std::set<autofill::FieldType> expected_address_ui_field_types;

  // These types are not part of `AutofillAddressUIComponent`.
  expected_address_ui_field_types.insert(
      autofill::FieldType::ADDRESS_HOME_COUNTRY);
  expected_address_ui_field_types.insert(
      autofill::FieldType::PHONE_HOME_WHOLE_NUMBER);
  expected_address_ui_field_types.insert(autofill::FieldType::EMAIL_ADDRESS);

  std::vector<std::vector<autofill::AutofillAddressUIComponent>> components;
  autofill::GetAddressComponents(country_code, "en-US",
                                 /*include_literals=*/false, &components,
                                 nullptr);
  for (const std::vector<autofill::AutofillAddressUIComponent>& line :
       components) {
    for (const autofill::AutofillAddressUIComponent& component : line) {
      expected_address_ui_field_types.insert(component.field);
    }
  }

  return expected_address_ui_field_types;
}

auto FilledFieldHasAttributeWithValue16(const std::string& attribute,
                                        const std::u16string& expected_value) {
  return FilledFieldHasAttributeWithValue(attribute,
                                          base::UTF16ToUTF8(expected_value));
}

std::string GetProfileInfoFromAddressField(const AutofillProfile profile,
                                           const base::Value& address_field) {
  return base::UTF16ToUTF8(profile.GetInfo(
      TypeNameToFieldType(
          *address_field.GetDict().FindStringByDottedPath("name")),
      "en-US"));
}

// Adds waiting capabilities to BrowserAutofillManager.
class TestAutofillManager : public autofill::BrowserAutofillManager {
 public:
  explicit TestAutofillManager(autofill::ContentAutofillDriver* driver)
      : BrowserAutofillManager(driver) {}

  static TestAutofillManager& GetForRenderFrameHost(
      content::RenderFrameHost* rfh) {
    return static_cast<TestAutofillManager&>(
        autofill::ContentAutofillDriver::GetForRenderFrameHost(rfh)
            ->GetAutofillManager());
  }

  [[nodiscard]] testing::AssertionResult WaitForFormsSeen(
      size_t num_awaited_calls) {
    return forms_seen_.Wait(num_awaited_calls);
  }

  const FormStructure* WaitForFormWithNFields(int n) {
    return WaitForMatchingForm(this, base::BindRepeating(
                                         [](int n, const FormStructure& form) {
                                           return form.fields().size() ==
                                                  static_cast<size_t>(n);
                                         },
                                         n));
  }

 private:
  autofill::TestAutofillManagerWaiter forms_seen_{
      *this,
      {autofill::AutofillManagerEvent::kFormsSeen}};
};

class DevToolsAutofillTest : public DevToolsProtocolTestBase {
 public:
  DevToolsAutofillTest() = default;
  void SetUpOnMainThread() override {
    host_resolver()->AddRule("*", "127.0.0.1");
  }

  content::RenderFrameHost* main_frame() {
    return web_contents()->GetPrimaryMainFrame();
  }

  TestAutofillManager& main_autofill_manager() {
    return TestAutofillManager::GetForRenderFrameHost(main_frame());
  }

  std::string EvaluateAndGetValue(const std::string& expression,
                                  const std::string& unique_context_id,
                                  const std::string& session_id) {
    base::Value::Dict params;
    params.Set("expression", expression);
    if (!unique_context_id.empty()) {
      params.Set("uniqueContextId", unique_context_id);
    }
    const base::Value::Dict* result = SendSessionCommand(
        "Runtime.evaluate", std::move(params), session_id, /*wait=*/true);
    return *result->FindStringByDottedPath("result.value");
  }

  int GetBackendNodeIdByIdAttribute(const std::string& id_attribute) {
    return GetBackendNodeIdByIdAttribute(id_attribute, "", "");
  }

  int GetBackendNodeIdByIdAttribute(const std::string& id_attribute,
                                    const std::string& unique_context_id) {
    return GetBackendNodeIdByIdAttribute(id_attribute, unique_context_id, "");
  }

  int GetBackendNodeIdByIdAttribute(const std::string& id_attribute,
                                    const std::string& unique_context_id,
                                    const std::string& session_id) {
    std::string object_id;
    {
      base::Value::Dict params;
      params.Set("expression", base::StrCat({"document.getElementById('",
                                             id_attribute, "')"}));
      if (!unique_context_id.empty()) {
        params.Set("uniqueContextId", unique_context_id);
      }
      const base::Value::Dict* result = SendSessionCommand(
          "Runtime.evaluate", std::move(params), session_id, /*wait=*/true);
      object_id = *result->FindStringByDottedPath("result.objectId");
    }

    base::Value::Dict params;
    params.Set("objectId", object_id);
    const base::Value::Dict* result = SendSessionCommand(
        "DOM.describeNode", std::move(params), session_id, /*wait=*/true);
    return *result->FindIntByDottedPath("node.backendNodeId");
  }

  std::string GetOOPIFTargetId() {
    base::Value::Dict params;
    params.Set("discover", true);
    SendCommandSync("Target.setDiscoverTargets", std::move(params));
    std::string frame_target_id;
    while (true) {
      base::Value::Dict result;
      result = WaitForNotification("Target.targetCreated", true);
      if (*result.FindStringByDottedPath("targetInfo.type") == "iframe") {
        frame_target_id =
            std::string(*result.FindStringByDottedPath("targetInfo.targetId"));
        break;
      }
    }
    return frame_target_id;
  }

  std::string AttachToTarget(const std::string& target_id) {
    base::Value::Dict params;
    params.Set("targetId", target_id);
    params.Set("flatten", true);
    SendCommandSync("Target.attachToTarget", std::move(params));
    return CHECK_DEREF(CHECK_DEREF(result()).FindString("sessionId"));
  }

  base::Value::Dict GetTestCreditCard() {
    base::Value::Dict card;
    card.Set("number", "4444444444444444");
    card.Set("name", "John Smith");
    card.Set("expiryMonth", "01");
    card.Set("expiryYear", "2030");
    card.Set("cvc", "123");
    return card;
  }

  AutofillProfile CreateTestProfile() {
    AutofillProfile profile = test::GetFullProfile();
    AddTestProfile(browser()->profile(), profile);
    return profile;
  }

  FormGlobalId form_id() {
    return {LocalFrameToken(*main_frame()->GetFrameToken()),
            FormRendererId(123)};
  }

  base::Value::Dict GetFilledOutForm(const std::string& unique_context_id) {
    return GetFilledOutForm(unique_context_id, "");
  }

  base::Value::Dict GetFilledOutForm(const std::string& unique_context_id,
                                     const std::string& session_id) {
    base::Value::Dict card;
    card.Set("number",
             EvaluateAndGetValue(
                 "document.getElementById('CREDIT_CARD_NUMBER').value",
                 unique_context_id, session_id));
    card.Set("name",
             EvaluateAndGetValue(
                 "document.getElementById('CREDIT_CARD_NAME_FULL').value",
                 unique_context_id, session_id));
    card.Set("expiryMonth",
             EvaluateAndGetValue(
                 "document.getElementById('CREDIT_CARD_EXP_MONTH').value",
                 unique_context_id, session_id));
    card.Set(
        "expiryYear",
        EvaluateAndGetValue(
            "document.getElementById('CREDIT_CARD_EXP_4_DIGIT_YEAR').value",
            unique_context_id, session_id));
    // CVC is not filled out in the form.
    card.Set("cvc", "123");
    return card;
  }

 private:
  test::AutofillBrowserTestEnvironment autofill_test_environment_;
  autofill::TestAutofillManagerInjector<TestAutofillManager>
      autofill_manager_injector_;
};

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, SetAddresses) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL url(
      embedded_test_server()->GetURL("/autofill_creditcard_form.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
  Attach();

  EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));

  base::Value::Dict address_1_fields;
  address_1_fields.Set("name", "ADDRESS_HOME_LINE1");
  address_1_fields.Set("value", "Erika-mann");
  base::Value::Dict address_1;
  base::Value::List fields;
  fields.Append(std::move(address_1_fields));
  address_1.Set("fields", std::move(fields));

  base::Value::Dict address_2_fields;
  address_2_fields.Set("name", "ADDRESS_HOME_LINE2");
  address_2_fields.Set("value", "Faria lima");
  base::Value::Dict address_2;
  base::Value::List fields_2;
  fields_2.Append(std::move(address_2_fields));
  address_2.Set("fields", std::move(fields_2));

  base::Value::List test_addresses;
  test_addresses.Append(std::move(address_1));
  test_addresses.Append(std::move(address_2));

  base::Value::Dict params;
  params.Set("addresses", std::move(test_addresses));

  SendCommandSync("Autofill.setAddresses", std::move(params));

  base::span<const autofill::AutofillProfile> res =
      main_autofill_manager().client().GetTestAddresses();
  ASSERT_EQ(res.size(), 2u);
  ASSERT_EQ(
      res[0].GetAddress().GetRawInfo(autofill::FieldType::ADDRESS_HOME_LINE1),
      u"Erika-mann");
  ASSERT_EQ(
      res[1].GetAddress().GetRawInfo(autofill::FieldType::ADDRESS_HOME_LINE2),
      u"Faria lima");
}

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerCreditCardInIframe) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL url(embedded_test_server()->GetURL(
      "/autofill_creditcard_form_in_iframe.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
  Attach();

  EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));

  std::string frame_id;
  {
    const base::Value::Dict* result = SendCommandSync("Page.getFrameTree");
    const base::Value::List* frames =
        result->FindListByDottedPath("frameTree.childFrames");
    const base::Value::Dict* frame_dict = frames->front().GetIfDict();
    frame_id = *frame_dict->FindStringByDottedPath("frame.id");
  }

  std::string unique_context_id;
  {
    base::Value::Dict command_params;
    SendCommandSync("Runtime.enable");
    base::Value::Dict params;
    for (int context_count = 1; true; context_count++) {
      params = WaitForNotification("Runtime.executionContextCreated", true);
      if (*params.FindStringByDottedPath("context.auxData.frameId") ==
          frame_id) {
        unique_context_id = *params.FindStringByDottedPath("context.uniqueId");
        break;
      }
      ASSERT_LT(context_count, 2);
    }
  }

  int backend_node_id =
      GetBackendNodeIdByIdAttribute("CREDIT_CARD_NUMBER", unique_context_id);

  {
    base::Value::Dict params;
    params.Set("fieldId", backend_node_id);
    params.Set("card", GetTestCreditCard());
    params.Set("frameId", "wrong");
    SendCommandSync("Autofill.trigger", std::move(params));
    EXPECT_EQ(*error()->FindString("message"), "Frame not found");
  }

  {
    base::Value::Dict params;
    params.Set("fieldId", backend_node_id);
    params.Set("card", GetTestCreditCard());
    params.Set("frameId", frame_id);
    SendCommandSync("Autofill.trigger", std::move(params));
    ASSERT_TRUE(result());
    EXPECT_EQ(*result(), base::Value::Dict());
  }

  EXPECT_EQ(GetFilledOutForm(unique_context_id), GetTestCreditCard());
}

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerCreditCardInOOPIFIframe) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL(
      "a.com", "/autofill_creditcard_form_in_oopif.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
  Attach();

  EXPECT_TRUE(main_autofill_manager().WaitForFormWithNFields(4));

  std::string frame_target_id = GetOOPIFTargetId();
  std::string session_id = AttachToTarget(frame_target_id);

  int backend_node_id =
      GetBackendNodeIdByIdAttribute("CREDIT_CARD_NUMBER", "", session_id);

  base::Value::Dict params;
  params.Set("fieldId", backend_node_id);
  params.Set("frameId", frame_target_id);
  params.Set("card", GetTestCreditCard());
  SendSessionCommand("Autofill.trigger", std::move(params), session_id,
                     /*wait=*/true);
  ASSERT_TRUE(result());
  EXPECT_EQ(*result(), base::Value::Dict());
  EXPECT_EQ(GetFilledOutForm("", session_id), GetTestCreditCard());
}

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerCreditCardAcrossOOPIFs) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL(
      "a.com", "/autofill_creditcard_form_in_oopif.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
  Attach();

  EXPECT_TRUE(main_autofill_manager().WaitForFormWithNFields(4));

  std::string frame_target_id = GetOOPIFTargetId();
  std::string session_id = AttachToTarget(frame_target_id);

  int backend_node_id =
      GetBackendNodeIdByIdAttribute("CREDIT_CARD_NUMBER", "", session_id);

  base::Value::Dict params;
  params.Set("fieldId", backend_node_id);
  params.Set("frameId", frame_target_id);
  params.Set("card", GetTestCreditCard());
  SendSessionCommand("Autofill.trigger", std::move(params), session_id,
                     /*wait=*/true);
  ASSERT_TRUE(result());
  EXPECT_EQ(*result(), base::Value::Dict());
  EXPECT_EQ(GetFilledOutForm("", session_id), GetTestCreditCard());
}

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, AddressFormFilled) {
  Attach();
  // Create a profile to read information from and send to devtools.
  AutofillProfile profile = CreateTestProfile();

  // TODO(crbug.com/40227496): Get rid of FormFieldData.
  FormData form;
  form.set_host_frame(form_id().frame_token);
  form.set_renderer_id(form_id().renderer_id);
  std::vector<FormFieldData> fields;
  fields.push_back(test::CreateTestFormField(
      /*label=*/"", "name_1", /*value=*/"", FormControlType::kInputText));
  fields.back().set_id_attribute(u"id_1");
  fields.back().set_host_frame(form.host_frame());
  fields.push_back(test::CreateTestFormField(
      /*label=*/"", "name_2", /*value=*/"", FormControlType::kInputText));
  fields.back().set_id_attribute(u"id_2");
  fields.back().set_host_frame(form.host_frame());
  form.set_fields(std::move(fields));

  // The parsed form is queried by
  // AutofillHandler::OnFillOrPreviewForm() to obtain the type
  // predictions.
  auto form_structure = std::make_unique<FormStructure>(form);
  form_structure->field(0)->set_server_predictions(
      {test::CreateFieldPrediction(NAME_FULL)});
  form_structure->field(0)->SetHtmlType(HtmlFieldType::kName,
                                        HtmlFieldMode::kShipping);
  form_structure->field(1)->set_server_predictions(
      {test::CreateFieldPrediction(NAME_FULL)});
  form_structure->field(1)->SetHtmlType(HtmlFieldType::kUnspecified,
                                        HtmlFieldMode::kShipping);

  const std::vector<FormFieldData> filled_fields_by_autofill = {
      {form.fields()[0], form.fields()[1]}};

  test_api(main_autofill_manager())
      .AddSeenFormStructure(std::move(form_structure));

  // Enable events and emit event about form being filled.
  SendCommandSync("Autofill.enable");
  main_autofill_manager().NotifyObservers(
      &autofill::AutofillManager::Observer::OnFillOrPreviewForm, form_id(),
      autofill::mojom::ActionPersistence::kFill,
      base::MakeFlatSet<FieldGlobalId>(filled_fields_by_autofill, {},
                                       &FormFieldData::global_id),
      &profile);

  base::Value::Dict notification = WaitForNotification(
      "Autofill.addressFormFilled", /*allow_existing=*/true);

  std::set<autofill::FieldType> field_types_added_to_address_ui;
  for (const base::Value& address_line :
       *notification.FindListByDottedPath("addressUi.addressFields")) {
    for (const base::Value& address_field :
         *address_line.GetDict().FindListByDottedPath("fields")) {
      // Test that the profile address values sent to devtools match what we
      // have in `profile`.
      EXPECT_EQ(GetProfileInfoFromAddressField(profile, address_field),
                *address_field.GetDict().FindStringByDottedPath("value"));
      field_types_added_to_address_ui.insert(TypeNameToFieldType(
          *address_field.GetDict().FindStringByDottedPath("name")));
    }
  }

  // Assert the expected values used to build the address ui were sent to
  // devtools.
  ASSERT_EQ(field_types_added_to_address_ui,
            GetExpectedFieldTypesToBuildAddressUiForCountryCode(
                base::UTF16ToUTF8(profile.GetInfo(
                    autofill::FieldType::ADDRESS_HOME_COUNTRY, "en-US"))));
  // Assert that the filled fields sent to devtools match exactly the ones
  // filled by autofill.
  const base::Value::List* filled_fields =
      notification.FindListByDottedPath("filledFields");
  ASSERT_EQ(filled_fields->size(), filled_fields_by_autofill.size());
  for (size_t i = 0; i < filled_fields->size(); ++i) {
    FormStructure& fs =
        CHECK_DEREF(main_autofill_manager().FindCachedFormById(form_id()));
    const base::Value& ff = (*filled_fields)[i];
    const FormFieldData& ffd = filled_fields_by_autofill[i];
    const AutofillField* af = fs.GetFieldById(ffd.global_id());

    std::vector<std::string_view> field_type_strings =
        base::ToVector(af->Type().GetTypes(),
                       &autofill::FieldTypeToDeveloperRepresentationString);
    std::erase(field_type_strings, "");

    EXPECT_THAT(ff,
                FilledFieldHasAttributeWithValue16("id", af->id_attribute()));
    EXPECT_THAT(
        ff, FilledFieldHasAttributeWithValue(
                "autofillType", base::JoinString(field_type_strings, ", ")));
    EXPECT_THAT(ff, FilledFieldHasAttributeWithValue16(
                        "value", profile.GetInfo(af->Type(), "en-us")));
    EXPECT_THAT(ff, FilledFieldHasAttributeWithValue16(
                        "frameId",
                        base::UTF8ToUTF16(
                            main_frame()->GetDevToolsFrameToken().ToString())));
    EXPECT_THAT(ff,
                FilledFieldHasAttributeWithValue(
                    "htmlType", std::string(autofill::FormControlTypeToString(
                                    af->form_control_type()))));
    EXPECT_THAT(
        ff, FilledFieldHasAttributeWithValue16("name", af->name_attribute()));
    EXPECT_EQ(*ff.GetDict().FindIntByDottedPath("fieldId"),
              (int)(ffd.renderer_id().value()));
  }

  // The first filled field uses autocomplete attribute as filling strategy.
  EXPECT_EQ(*filled_fields->front().GetDict().FindStringByDottedPath(
                "fillingStrategy"),
            "autocompleteAttribute");
  // The second one used autofill internals, either local heuristics or server
  // predictions.
  EXPECT_EQ(*filled_fields->back().GetDict().FindStringByDottedPath(
                "fillingStrategy"),
            "autofillInferred");
}

// This test guards the assumption that autofill events in iframes are routed to
// the main frame on the browser side. It's implicitly assumed in tests that
// make direct `AutofillManager::Observer` calls on `main_autofill_manager()`
// while testing iframes.
IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, AutofillInOOPIFs) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL(
      "a.com", "/autofill_address_multi_form_in_oopif.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_TRUE(content::WaitForLoadStop(web_contents()));

  EXPECT_TRUE(main_autofill_manager().WaitForFormWithNFields(10));
  ASSERT_EQ(main_autofill_manager().form_structures().size(), 1u);

  FormData form =
      main_autofill_manager().form_structures().begin()->second->ToFormData();

  autofill::MockAutofillManagerObserver observer;
  main_autofill_manager().AddObserver(&observer);

  // Expect the `AskForValuesToFill()` call below to be routed to the main
  // frame `AutofillManager`.
  EXPECT_CALL(observer,
              OnBeforeAskForValuesToFill(_, form.global_id(),
                                         form.fields()[0].global_id(), _));

  const std::vector<const FormFieldData*> filled_fields_by_autofill = {
      {&form.fields()[0], &form.fields()[1]}};
  web_contents()->ForEachRenderFrameHost([&](content::RenderFrameHost* rfh) {
    // Call the driver of the field host iframe.
    if (rfh->GetFrameToken().ToString() ==
        form.fields()[0].host_frame()->ToString()) {
      ASSERT_NE(rfh->GetFrameToken(), main_frame()->GetFrameToken());
      auto* driver = static_cast<mojom::AutofillDriver*>(
          autofill::ContentAutofillDriver::GetForRenderFrameHost(rfh));
      driver->AskForValuesToFill(
          form, form.fields()[0].renderer_id(), gfx::Rect(0, 10),
          ::autofill::mojom::AutofillSuggestionTriggerSource::kUnspecified,
          std::nullopt);
    }
  });

  main_autofill_manager().RemoveObserver(&observer);
}

// Tests that only the handler associated with the root frame (page) handles
// AutofillManager events (for cross-iframe filling, the filling events from all
// frames are routed to the root AutofillManager and others don't emit them).
// It also tests that only relevant elements (those from forms which have at
// least one field autofilled, others are ignored) are sent to the frontend.
IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, AddressFormFilledInOOPIFs) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());
  GURL url = embedded_test_server()->GetURL(
      "a.com", "/autofill_address_multi_form_in_oopif.html");

  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_TRUE(content::WaitForLoadStop(web_contents()));

  Attach();

  EXPECT_TRUE(main_autofill_manager().WaitForFormWithNFields(10));
  ASSERT_EQ(main_autofill_manager().form_structures().size(), 1u);

  std::string frame_target_id = GetOOPIFTargetId();
  std::string session_id = AttachToTarget(frame_target_id);

  SendCommandSync("Autofill.enable");
  SendSessionCommand("Autofill.enable", base::Value::Dict(), session_id, true);

  AutofillProfile profile = CreateTestProfile();
  FormData form =
      main_autofill_manager().form_structures().begin()->second->ToFormData();
  base::flat_set<FieldGlobalId> filled_fields_by_autofill{
      {form.fields()[0].global_id(), form.fields()[1].global_id()}};
  main_autofill_manager().NotifyObservers(
      &autofill::AutofillManager::Observer::OnFillOrPreviewForm,
      form.global_id(), autofill::mojom::ActionPersistence::kFill,
      filled_fields_by_autofill, &profile);

  base::Value::Dict notification = WaitForNotification(
      "Autofill.addressFormFilled", /*allow_existing=*/true);
  EXPECT_EQ(notification.FindListByDottedPath("filledFields")->size(), 6u);
  EXPECT_FALSE(HasExistingNotification("Autofill.addressFormFilled"))
      << "The other handler should not handle `OnFillOrPreviewForm`";
}

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest,
                       AutofillManagerEventsAfterNavigation) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());

  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(),
      embedded_test_server()->GetURL("a.com", "/autofill_test_form.html")));
  ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
  EXPECT_TRUE(main_autofill_manager().WaitForFormWithNFields(9));

  Attach();
  SendCommandSync("Autofill.enable");

  AutofillProfile profile_a = CreateTestProfile();
  FormData form_a =
      main_autofill_manager().form_structures().begin()->second->ToFormData();
  const base::flat_set<FieldGlobalId> filled_fields_by_autofill_a = {
      {form_a.fields()[0].global_id(), form_a.fields()[1].global_id()}};
  main_autofill_manager().NotifyObservers(
      &autofill::AutofillManager::Observer::OnFillOrPreviewForm,
      form_a.global_id(), autofill::mojom::ActionPersistence::kFill,
      filled_fields_by_autofill_a, &profile_a);

  WaitForNotification("Autofill.addressFormFilled", /*allow_existing=*/true);

  // Navigating from "a.com" to "b.com".
  ASSERT_TRUE(ui_test_utils::NavigateToURL(
      browser(),
      embedded_test_server()->GetURL("b.com", "/autofill_test_form.html")));
  ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
  EXPECT_TRUE(main_autofill_manager().WaitForFormWithNFields(9));

  AutofillProfile profile_b = CreateTestProfile();
  FormData form_b =
      main_autofill_manager().form_structures().begin()->second->ToFormData();
  const base::flat_set<FieldGlobalId> filled_fields_by_autofill_b = {
      {form_b.fields()[0].global_id(), form_b.fields()[1].global_id()}};
  main_autofill_manager().NotifyObservers(
      &autofill::AutofillManager::Observer::OnFillOrPreviewForm,
      form_b.global_id(), autofill::mojom::ActionPersistence::kFill,
      filled_fields_by_autofill_b, &profile_b);
  WaitForNotification("Autofill.addressFormFilled", /*allow_existing=*/true);
}

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerAddressAutofill) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL url =
      embedded_test_server()->GetURL("/autofill_address_enabled.html");
  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));

  Attach();
  SendCommandSync("Autofill.enable");

  EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));

  int backend_node_id = GetBackendNodeIdByIdAttribute("street-address");

  auto address_fields = base::Value::List()
                            .Append(base::Value::Dict()
                                        .Set("name", "ADDRESS_HOME_LINE1")
                                        .Set("value", "123 Main Street"))
                            .Append(base::Value::Dict()
                                        .Set("name", "ADDRESS_HOME_CITY")
                                        .Set("value", "New York"))
                            .Append(base::Value::Dict()
                                        .Set("name", "ADDRESS_HOME_STATE")
                                        .Set("value", "NY"))
                            .Append(base::Value::Dict()
                                        .Set("name", "ADDRESS_HOME_ZIP")
                                        .Set("value", "10001"))
                            .Append(base::Value::Dict()
                                        .Set("name", "ADDRESS_HOME_COUNTRY")
                                        .Set("value", "US"));

  auto address = base::Value::Dict().Set("fields", std::move(address_fields));

  base::Value::Dict params;
  params.Set("fieldId", backend_node_id);
  params.Set("address", std::move(address));

  SendCommandSync("Autofill.trigger", std::move(params));

  if (error()) {
    FAIL() << "Autofill trigger failed with error: "
           << *error()->FindString("message");
  } else {
    // Verify the field was filled
    content::EvalJsResult street_address = content::EvalJs(
        web_contents(), "document.getElementById('street-address').value");
    EXPECT_EQ("123 Main Street", street_address.ExtractString());
    content::EvalJsResult city = content::EvalJs(
        web_contents(), "document.getElementById('city').value");
    EXPECT_EQ("New York", city.ExtractString());
    content::EvalJsResult zip = content::EvalJs(
        web_contents(), "document.getElementById('postal-code').value");
    EXPECT_EQ("10001", zip.ExtractString());
    content::EvalJsResult state = content::EvalJs(
        web_contents(), "document.getElementById('state').value");
    EXPECT_EQ("NY", state.ExtractString());
    content::EvalJsResult country = content::EvalJs(
        web_contents(), "document.getElementById('country').value");
    EXPECT_EQ("US", country.ExtractString());
  }
}

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerWithInvalidFieldId) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL url(
      embedded_test_server()->GetURL("/autofill_creditcard_form.html"));
  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));

  Attach();
  SendCommandSync("Autofill.enable");

  EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));

  base::Value::Dict params;
  params.Set("fieldId", 99999);  // Invalid field ID
  params.Set("card", GetTestCreditCard());

  SendCommandSync("Autofill.trigger", std::move(params));

  EXPECT_TRUE(error());
  EXPECT_EQ(*error()->FindString("message"), "Field not found");
}

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest,
                       TriggerAddressWithUnsupportedFieldType) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());

  const GURL url =
      embedded_test_server()->GetURL("/autofill_address_enabled.html");
  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));

  Attach();
  SendCommandSync("Autofill.enable");

  EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));

  int backend_node_id = GetBackendNodeIdByIdAttribute("street-address");

  // Use an invalid/unsupported field type
  auto address_fields =
      base::Value::List().Append(base::Value::Dict()
                                     .Set("name", "INVALID_FIELD_TYPE")
                                     .Set("value", "123 Main Street"));

  auto address = base::Value::Dict().Set("fields", std::move(address_fields));

  base::Value::Dict params;
  params.Set("fieldId", backend_node_id);
  params.Set("address", std::move(address));

  SendCommandSync("Autofill.trigger", std::move(params));

  EXPECT_TRUE(error());
  EXPECT_EQ(*error()->FindString("message"),
            "Unsupported field type: INVALID_FIELD_TYPE");

  address_fields =
      base::Value::List().Append(base::Value::Dict()
                                     .Set("name", "LOYALTY_MEMBERSHIP_ID")
                                     .Set("value", "1234567890"));

  address = base::Value::Dict().Set("fields", std::move(address_fields));

  params.Set("fieldId", backend_node_id);
  params.Set("address", std::move(address));

  SendCommandSync("Autofill.trigger", std::move(params));

  EXPECT_TRUE(error());
  EXPECT_EQ(*error()->FindString("message"),
            "Unsupported field type: LOYALTY_MEMBERSHIP_ID");
}

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerAddressAutofillInIframe) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL url(embedded_test_server()->GetURL(
      "/autofill_address_multi_form_in_oopif.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
  Attach();

  EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));

  std::string frame_id;
  {
    const base::Value::Dict* result = SendCommandSync("Page.getFrameTree");
    const base::Value::List* frames =
        result->FindListByDottedPath("frameTree.childFrames");
    const base::Value::Dict* frame_dict = frames->front().GetIfDict();
    frame_id = *frame_dict->FindStringByDottedPath("frame.id");
  }

  std::string unique_context_id;
  {
    base::Value::Dict command_params;
    SendCommandSync("Runtime.enable");
    base::Value::Dict params;
    for (int context_count = 1; true; context_count++) {
      params = WaitForNotification("Runtime.executionContextCreated", true);
      if (*params.FindStringByDottedPath("context.auxData.frameId") ==
          frame_id) {
        unique_context_id = *params.FindStringByDottedPath("context.uniqueId");
        break;
      }
      ASSERT_LT(context_count, 2);
    }
  }

  int backend_node_id =
      GetBackendNodeIdByIdAttribute("street-address", unique_context_id);

  // Use valid address field types
  auto address_fields = base::Value::List()
                            .Append(base::Value::Dict()
                                        .Set("name", "ADDRESS_HOME_LINE1")
                                        .Set("value", "123 Main Street"))
                            .Append(base::Value::Dict()
                                        .Set("name", "ADDRESS_HOME_CITY")
                                        .Set("value", "New York"));

  auto address = base::Value::Dict().Set("fields", std::move(address_fields));

  base::Value::Dict params;
  params.Set("fieldId", backend_node_id);
  params.Set("address", std::move(address));
  params.Set("frameId", frame_id);

  SendCommandSync("Autofill.trigger", std::move(params));

  if (error()) {
    FAIL() << "Autofill trigger failed with error: "
           << *error()->FindString("message");
  } else {
    // Verify the fields were filled in the iframe
    content::EvalJsResult street_address =
        content::EvalJs(web_contents(),
                        "document.querySelector('iframe').contentDocument."
                        "getElementById('street-address').value");
    EXPECT_EQ("123 Main Street", street_address.ExtractString());

    content::EvalJsResult city =
        content::EvalJs(web_contents(),
                        "document.querySelector('iframe').contentDocument."
                        "getElementById('city').value");
    EXPECT_EQ("New York", city.ExtractString());
  }
}

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerWithBothCardAndAddress) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL url(
      embedded_test_server()->GetURL("/autofill_creditcard_form.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
  Attach();

  EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));

  int backend_node_id = GetBackendNodeIdByIdAttribute("CREDIT_CARD_NUMBER");

  auto address_fields = base::Value::List()
                            .Append(base::Value::Dict()
                                        .Set("name", "ADDRESS_HOME_LINE1")
                                        .Set("value", "123 Main Street"))
                            .Append(base::Value::Dict()
                                        .Set("name", "ADDRESS_HOME_COUNTRY")
                                        .Set("value", "US"));

  base::Value::Dict params;
  params.Set("fieldId", backend_node_id);
  params.Set("card", GetTestCreditCard());
  params.Set("address",
             base::Value::Dict().Set("fields", std::move(address_fields)));

  SendCommandSync("Autofill.trigger", std::move(params));

  EXPECT_TRUE(error());
  EXPECT_EQ(*error()->FindString("message"),
            "Card and address cannot both be provided");
}

IN_PROC_BROWSER_TEST_F(DevToolsAutofillTest, TriggerWithNeitherCardNorAddress) {
  embedded_test_server()->ServeFilesFromSourceDirectory(
      "chrome/test/data/autofill");
  ASSERT_TRUE(embedded_test_server()->Start());
  const GURL url(
      embedded_test_server()->GetURL("/autofill_creditcard_form.html"));
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
  ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
  Attach();

  EXPECT_TRUE(main_autofill_manager().WaitForFormsSeen(1));

  int backend_node_id = GetBackendNodeIdByIdAttribute("CREDIT_CARD_NUMBER");

  base::Value::Dict params;
  params.Set("fieldId", backend_node_id);
  // Neither card nor address provided

  SendCommandSync("Autofill.trigger", std::move(params));

  EXPECT_TRUE(error());
  EXPECT_EQ(*error()->FindString("message"),
            "Either card or address must be provided");
}

}  // namespace
}  // namespace autofill