// Copyright 2020 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/android_autofill/browser/android_autofill_provider.h"

#include <memory>

#include "base/android/android_info.h"
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "components/android_autofill/browser/android_autofill_bridge_factory.h"
#include "components/android_autofill/browser/android_autofill_features.h"
#include "components/android_autofill/browser/android_autofill_manager.h"
#include "components/android_autofill/browser/android_autofill_provider_bridge.h"
#include "components/android_autofill/browser/android_autofill_provider_test_api.h"
#include "components/android_autofill/browser/autofill_provider.h"
#include "components/android_autofill/browser/form_data_android.h"
#include "components/android_autofill/browser/form_data_android_test_api.h"
#include "components/android_autofill/browser/mock_form_field_data_android_bridge.h"
#include "components/autofill/android/touch_to_fill_keyboard_suppressor.h"
#include "components/autofill/content/browser/test_autofill_client_injector.h"
#include "components/autofill/content/browser/test_autofill_driver_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/foundations/autofill_manager_test_api.h"
#include "components/autofill/core/browser/foundations/test_autofill_manager.h"
#include "components/autofill/core/browser/foundations/test_autofill_manager_waiter.h"
#include "components/autofill/core/browser/test_utils/autofill_form_test_utils.h"
#include "components/autofill/core/common/autofill_test_utils.h"
#include "components/autofill/core/common/form_data.h"
#include "components/autofill/core/common/form_data_test_api.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/autofill/core/common/signatures.h"
#include "components/autofill/core/common/unique_ids.h"
#include "components/webauthn/android/mock_webauthn_cred_man_delegate.h"
#include "components/webauthn/android/stub_webauthn_client_android.h"
#include "components/webauthn/android/webauthn_cred_man_delegate_factory.h"
#include "components/webauthn/android/webauthn_cred_man_delegate_factory_test_api.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_renderer_host.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/origin.h"

namespace autofill {
namespace {

using ::testing::_;
using ::testing::AllOf;
using ::testing::AtLeast;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::Field;
using ::testing::InSequence;
using ::testing::Mock;
using ::testing::MockFunction;
using ::testing::NiceMock;
using ::testing::Optional;
using ::testing::Property;
using ::testing::ResultOf;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::Truly;
using ::testing::UnorderedElementsAre;
using ::testing::WithArg;
using FieldInfo = AndroidAutofillProviderBridge::FieldInfo;
using PrefillRequestState = AndroidAutofillProvider::PrefillRequestState;
using test::CreateFormDataForFrame;
using test::CreateTestCreditCardFormData;
using test::CreateTestFormField;
using test::CreateTestPasswordFormData;
using test::CreateTestPersonalInformationFormData;
using test::GetFormData;

auto EqualsFieldInfo(size_t index) {
  return Field("index", &AndroidAutofillProviderBridge::FieldInfo::index,
               Eq(index));
}

// Creates a matcher that compares a `FormDataAndroid::form()` to `expected`.
auto EqualsFormData(const FormData& expected) {
  return Property(&FormDataAndroid::form, expected);
}

auto EqualsFormDataWithFields(const FormData& form, auto fields_matcher) {
  return AllOf(
      EqualsFormData(form),
      ResultOf(
          [](FormDataAndroid& form_android)
              -> const std::vector<std::unique_ptr<FormFieldDataAndroid>>& {
            return test_api(form_android).fields();
          },
          fields_matcher));
}

// Creates a matcher that compares the results of a `FormDataAndroid`'s `form()`
// and `session_id()` methods to `form` and `session_id_matcher`.
auto EqualsFormDataWithSessionId(const FormData& form,
                                 auto session_id_matcher) {
  return AllOf(EqualsFormData(form),
               Property(&FormDataAndroid::session_id, session_id_matcher));
}

// Returns an action that writes the `SessionId` of a `FormDataAndroid` into the
// out parameter `session_id`. Note that `session_id` must be valid at least
// until the action is executed.
auto SaveSessionId(SessionId* session_id) {
  return [session_id](const FormDataAndroid& form_android) {
    *session_id = form_android.session_id();
  };
}

FormData CreateTestLoginForm() {
  FormData form;
  form.set_renderer_id(test::MakeFormRendererId());
  form.set_name(u"login_form");
  form.set_url(GURL("https://foo.com/form.html"));
  form.set_action(GURL("https://foo.com/submit.html"));
  form.set_main_frame_origin(url::Origin::Create(form.url()));
  form.set_fields(
      {CreateTestFormField(/*label=*/"Username", /*name=*/"username",
                           /*value=*/"", FormControlType::kInputText),
       CreateTestFormField(/*label=*/"Password", /*name=*/"password",
                           /*value=*/"", FormControlType::kInputPassword)});
  return form;
}

FormData CreateTestWebAuthnPasswordFormData() {
  std::vector<FormFieldData> fields;
  fields.push_back(CreateTestFormField(
      /*label=*/"Username:", /*name=*/"username",
      /*value=*/"", FormControlType::kInputText, /*autocomplete=*/"webauthn"));
  fields.push_back(
      CreateTestFormField(/*label=*/"Password:", /*name=*/"password",
                          /*value=*/"", FormControlType::kInputPassword));
  FormData form;
  form.set_renderer_id(test::MakeFormRendererId());
  form.set_url(GURL("https://foo.com/form.html"));
  form.set_action(GURL("https://foo.com/submit.html"));
  form.set_main_frame_origin(url::Origin::Create(form.url()));
  form.set_fields(std::move(fields));
  return form;
}

class TestAndroidAutofillManager
    : public TestAutofillManagerTemplate<AndroidAutofillManager> {
 public:
  using TestAutofillManagerTemplate<
      AndroidAutofillManager>::TestAutofillManagerTemplate;

  void SimulatePropagateAutofillPredictions(FormGlobalId form_id) {
    NotifyObservers(&Observer::OnFieldTypesDetermined, form_id,
                    Observer::FieldTypeSource::kAutofillServer,
                    /*small_forms_were_parsed=*/false);
  }
};

class MockAndroidAutofillProviderBridge : public AndroidAutofillProviderBridge {
 public:
  explicit MockAndroidAutofillProviderBridge() = default;

  ~MockAndroidAutofillProviderBridge() override = default;

  MOCK_METHOD(void,
              AttachToJavaAutofillProvider,
              (JNIEnv*, const base::android::JavaRef<jobject>&),
              (override));
  MOCK_METHOD(void, SendPrefillRequest, (FormDataAndroid&), (override));
  MOCK_METHOD(void,
              StartAutofillSession,
              (FormDataAndroid&, const FieldInfo&, bool),
              (override));
  MOCK_METHOD(void, OnServerPredictionsAvailable, (), (override));
  MOCK_METHOD(void,
              ShowDatalistPopup,
              (base::span<const SelectOption>, bool),
              (override));
  MOCK_METHOD(void, HideDatalistPopup, (), (override));
  MOCK_METHOD(void,
              OnFocusChanged,
              (const std::optional<FieldInfo>&),
              (override));
  MOCK_METHOD(void, OnFormFieldDidChange, (const FieldInfo&), (override));
  MOCK_METHOD(void,
              OnFormFieldVisibilitiesDidChange,
              (base::span<const int>),
              (override));
  MOCK_METHOD(void, OnTextFieldDidScroll, (const FieldInfo&), (override));
  MOCK_METHOD(void, OnFormSubmitted, (mojom::SubmissionSource), (override));
  MOCK_METHOD(void, OnDidAutofillForm, (), (override));
  MOCK_METHOD(void, CancelSession, (), (override));
  MOCK_METHOD(void, Reset, (), (override));
};

content::RenderFrameHost* NavigateAndCommitFrame(content::RenderFrameHost* rfh,
                                                 const GURL& url) {
  std::unique_ptr<content::NavigationSimulator> simulator =
      content::NavigationSimulator::CreateRendererInitiated(url, rfh);
  simulator->Commit();
  return simulator->GetFinalRenderFrameHost();
}

class AndroidAutofillProviderTestBase
    : public content::RenderViewHostTestHarness {
 public:
  void SetUp() override {
    content::RenderViewHostTestHarness::SetUp();

    // Set up mock bridges.
    AndroidAutofillBridgeFactory::GetInstance()
        .SetFormFieldDataAndroidTestingFactory(base::BindLambdaForTesting(
            []() -> std::unique_ptr<FormFieldDataAndroidBridge> {
              return std::make_unique<
                  NiceMock<MockFormFieldDataAndroidBridge>>();
            }));
    AndroidAutofillBridgeFactory::GetInstance()
        .SetAndroidAutofillProviderTestingFactory(base::BindLambdaForTesting(
            [&bridge_ptr = provider_bridge_](
                AndroidAutofillProviderBridge::Delegate* delegate)
                -> std::unique_ptr<AndroidAutofillProviderBridge> {
              auto bridge = std::make_unique<
                  NiceMock<MockAndroidAutofillProviderBridge>>();
              bridge_ptr = bridge.get();
              return bridge;
            }));

    // Create the provider.
    AndroidAutofillProvider::CreateForWebContents(web_contents());
  }

  void TearDown() override {
    provider_bridge_ = nullptr;
    content::RenderViewHostTestHarness::TearDown();
  }

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

  TestContentAutofillClient& autofill_client() {
    return *autofill_client_injector_[web_contents()];
  }

  ContentAutofillDriver& autofill_driver(
      content::RenderFrameHost* rfh = nullptr) {
    return static_cast<ContentAutofillDriver&>(
        android_autofill_manager(rfh).driver());
  }

  TestAndroidAutofillManager& android_autofill_manager(
      content::RenderFrameHost* rfh = nullptr) {
    return *autofill_manager_injector_[rfh ? rfh : main_frame()];
  }

  AndroidAutofillProvider& autofill_provider() {
    return *AndroidAutofillProvider::FromWebContents(web_contents());
  }

  AndroidAutofillProviderBridge::Delegate& provider_bridge_delegate() {
    return static_cast<AndroidAutofillProviderBridge::Delegate&>(
        autofill_provider());
  }

  // Returns the local frame token of the primary main frame.
  LocalFrameToken main_frame_token() {
    return LocalFrameToken(main_frame()->GetFrameToken().value());
  }

  MockAndroidAutofillProviderBridge& provider_bridge() {
    return *provider_bridge_;
  }

 private:
  test::AutofillUnitTestEnvironment autofill_environment_;
  TestAutofillClientInjector<TestContentAutofillClient>
      autofill_client_injector_;
  TestAutofillManagerInjector<TestAndroidAutofillManager>
      autofill_manager_injector_;
  raw_ptr<MockAndroidAutofillProviderBridge> provider_bridge_ = nullptr;
};

class AndroidAutofillProviderTest : public AndroidAutofillProviderTestBase {
 public:
  void SetUp() override {
    AndroidAutofillProviderTestBase::SetUp();

    // Navigation forces the creation of an AndroidAutofillManager for the main
    // frame.
    NavigateAndCommit(GURL("about:blank"));
    FocusWebContentsOnMainFrame();
  }

  void Reset(ContentAutofillDriver& driver) {
    test_api(autofill_client().GetAutofillDriverFactory())
        .Reset(autofill_driver());
  }
};

// Tests that AndroidAutofillManager keeps track of the predictions it is
// informed about.
TEST_F(AndroidAutofillProviderTest, HasServerPrediction) {
  FormData form = CreateTestPersonalInformationFormData();
  EXPECT_FALSE(
      android_autofill_manager().has_server_prediction(form.global_id()));
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      form.global_id());
  EXPECT_TRUE(
      android_autofill_manager().has_server_prediction(form.global_id()));

  // Resetting removes prediction state.
  Reset(autofill_driver());
  EXPECT_FALSE(
      android_autofill_manager().has_server_prediction(form.global_id()));
}

// Tests that triggering `OnAskForValuesToFill` results in starting an Autofill
// session for the focused form and field.
TEST_F(AndroidAutofillProviderTest, OnAskForValuesToFillStartsSession) {
  base::HistogramTester histogram_tester;

  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  EXPECT_CALL(
      provider_bridge(),
      StartAutofillSession(EqualsFormData(form), EqualsFieldInfo(/*index=*/0),
                           /*has_server_predictions=*/false));
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields().front().global_id());
}

// Tests that a focus change within the form of an ongoing autofill session
// results in a focus change event that is sent to Java.
TEST_F(AndroidAutofillProviderTest, OnFocusChangeInsideCurrentAutofillForm) {
  base::HistogramTester histogram_tester;

  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields().front().global_id());

  MockFunction<void(int)> check;
  {
    InSequence s;
    EXPECT_CALL(provider_bridge(),
                OnFocusChanged(Optional(EqualsFieldInfo(/*index=*/1))));
    EXPECT_CALL(check, Call(1));
    EXPECT_CALL(provider_bridge(), OnFocusChanged(Eq(std::nullopt)));
    EXPECT_CALL(check, Call(2));
  }

  android_autofill_manager().OnFocusOnFormField(
      form, form.fields()[1].global_id(), AutofillManagerTestApi::pass_key());
  check.Call(1);
  android_autofill_manager().OnFocusOnNonFormField(
      AutofillManagerTestApi::pass_key());
  check.Call(2);
}

// Tests that triggering `OnAskForValuesToFill` with a field results in an
// update to last_focused_field_id. The update is important so that
// `AutofillProvider::RendererShouldAcceptDatalistSuggestion` is passed the
// correct field ID.
TEST_F(AndroidAutofillProviderTest, OnAskForValuesToFillFindsCorrectFieldId) {
  base::HistogramTester histogram_tester;

  FormData form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[0].global_id());

  EXPECT_EQ(test_api(autofill_provider()).last_focused_field_id(),
            form.fields()[0].global_id());

  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[1].global_id());

  EXPECT_EQ(test_api(autofill_provider()).last_focused_field_id(),
            form.fields()[1].global_id());
}

// Tests that Java is informed about visibility changes of form fields connected
// to the current Autofill session if they are detected in focus change events.
TEST_F(AndroidAutofillProviderTest, NotifyAboutVisibilityChangeOnFocus) {
  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  // For Android Autofill, focusability is the same as visibility.
  test_api(form).field(0).set_is_focusable(false);
  test_api(form).field(2).set_is_focusable(false);

  // Start an Autofill session.
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[1].global_id());

  test_api(form).field(0).set_is_focusable(true);
  test_api(form).field(2).set_is_focusable(true);

  EXPECT_CALL(provider_bridge(), OnFormFieldVisibilitiesDidChange(
                                     /*indices=*/UnorderedElementsAre(0, 2)));
  EXPECT_CALL(provider_bridge(),
              OnFocusChanged(Optional(EqualsFieldInfo(/*index=*/0))));
  android_autofill_manager().OnFocusOnFormField(
      form, form.fields()[0].global_id(), AutofillManagerTestApi::pass_key());
}

// Tests that asking for values to fill for a different form than that of the
// current Autofill session results in a restart of the session.
TEST_F(AndroidAutofillProviderTest, OnAskForValuesToFillOnOtherForm) {
  base::HistogramTester histogram_tester;

  FormData form1 = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  FormData form2 = CreateFormDataForFrame(
      CreateTestCreditCardFormData(/*is_https=*/true, /*use_month_type=*/true),
      main_frame_token());
  android_autofill_manager().OnFormsSeen({form1, form2}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  MockFunction<void()> check;
  {
    InSequence s;
    EXPECT_CALL(provider_bridge(),
                StartAutofillSession(EqualsFormData(form1),
                                     EqualsFieldInfo(/*index=*/1),
                                     /*has_server_predictions=*/false));
    EXPECT_CALL(check, Call);
    EXPECT_CALL(provider_bridge(),
                StartAutofillSession(EqualsFormData(form2),
                                     EqualsFieldInfo(/*index=*/0),
                                     /*has_server_predictions=*/false));
    EXPECT_CALL(check, Call);
  }

  android_autofill_manager().OnAskForValuesToFillTest(
      form1, form1.fields()[1].global_id());
  check.Call();
  android_autofill_manager().OnAskForValuesToFillTest(
      form2, form2.fields()[0].global_id());
  check.Call();
}

// Tests that asking for values to fill on the same form as that of the current
// Autofill session results in a restart of the session if the form has changed.
TEST_F(AndroidAutofillProviderTest, OnAskForValuesToFillOnChangedForm) {
  base::HistogramTester histogram_tester;

  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  form.set_name_attribute(u"old_name");
  FormData form_changed = form;
  form_changed.set_name_attribute(u"changed_name");
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  MockFunction<void()> check;
  {
    InSequence s;
    EXPECT_CALL(
        provider_bridge(),
        StartAutofillSession(EqualsFormData(form), EqualsFieldInfo(/*index=*/1),
                             /*has_server_predictions=*/false));
    EXPECT_CALL(check, Call);
    EXPECT_CALL(provider_bridge(),
                StartAutofillSession(EqualsFormData(form_changed),
                                     EqualsFieldInfo(/*index=*/1),
                                     /*has_server_predictions=*/false));
    EXPECT_CALL(check, Call);
  }

  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[1].global_id());
  check.Call();
  android_autofill_manager().OnFormsSeen({form_changed}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  android_autofill_manager().OnAskForValuesToFillTest(
      form_changed, form_changed.fields()[1].global_id());
  check.Call();
}

// Tests that asking for values to fill on the same form as that of the current
// Autofill session does not result in a restart of the session if the form
// has not changed.
TEST_F(AndroidAutofillProviderTest, OnAskForValuesToFillOnSameForm) {
  base::HistogramTester histogram_tester;

  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  MockFunction<void()> check;
  {
    InSequence s;
    EXPECT_CALL(
        provider_bridge(),
        StartAutofillSession(EqualsFormData(form), EqualsFieldInfo(/*index=*/1),
                             /*has_server_predictions=*/false));
    EXPECT_CALL(check, Call);
  }

  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[1].global_id());
  check.Call();
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[0].global_id());
}

// Tests that value changes in the form of the Autofill session are propagated
// to Java and to the state that `AndroidAutofillProvider` keeps.
TEST_F(AndroidAutofillProviderTest, OnTextFieldValueChanged) {
  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  // Start Autofill session.
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[1].global_id());

  // Simulate a value change.
  EXPECT_CALL(provider_bridge(),
              OnFormFieldDidChange(EqualsFieldInfo(/*index=*/1)));
  test_api(form).field(1).set_value(form.fields()[1].value() + u"x");
  android_autofill_manager().OnTextFieldValueChanged(
      form, form.fields()[1].global_id(), base::TimeTicks::Now(),
      AutofillManagerTestApi::pass_key());
  // The `FormDataAndroid` object owned by the provider is also updated.
  ASSERT_TRUE(test_api(autofill_provider()).form());
  EXPECT_EQ(test_api(autofill_provider()).form()->form().fields()[1].value(),
            form.fields()[1].value());
}

// Tests that value changes in a form that is not part of the current Autofill
// session are ignored.
TEST_F(AndroidAutofillProviderTest, OnTextFieldValueChangedInUnrelatedForm) {
  FormData form1 = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  FormData form2 = CreateFormDataForFrame(
      CreateTestCreditCardFormData(/*is_https=*/true, /*use_month_type=*/true),
      main_frame_token());
  android_autofill_manager().OnFormsSeen({form1, form2}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  // Start the Autofill session.
  android_autofill_manager().OnAskForValuesToFillTest(
      form1, form1.fields()[1].global_id());

  // Simulate a value change in a different form.
  EXPECT_CALL(provider_bridge(), OnFormFieldDidChange).Times(0);
  test_api(form2).field(1).set_value(form2.fields()[1].value() + u"x");
  android_autofill_manager().OnTextFieldValueChanged(
      form2, form2.fields()[1].global_id(), base::TimeTicks::Now(),
      AutofillManagerTestApi::pass_key());
}

// Tests that scrolling events in the form of the Autofill session are
// propagated to Java.
TEST_F(AndroidAutofillProviderTest, OnTextFieldDidScroll) {
  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  // Start the Autofill session.
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[2].global_id());

  // Simulate scrolling.
  EXPECT_CALL(provider_bridge(),
              OnTextFieldDidScroll(EqualsFieldInfo(/*index=*/2)));
  android_autofill_manager().OnTextFieldDidScroll(
      form, form.fields()[2].global_id(), AutofillManagerTestApi::pass_key());
}

// Tests that scrolling envets in a form that is not part of the current
// Autofill session are ignored.
TEST_F(AndroidAutofillProviderTest, OnTextFieldDidScrollInUnrelatedForm) {
  FormData form1 = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  FormData form2 = CreateFormDataForFrame(
      CreateTestCreditCardFormData(/*is_https=*/true, /*use_month_type=*/true),
      main_frame_token());
  android_autofill_manager().OnFormsSeen({form1, form2}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  // Start the Autofill session.
  android_autofill_manager().OnAskForValuesToFillTest(
      form1, form1.fields()[1].global_id());

  // Simulate a scroll event in a different form.
  EXPECT_CALL(provider_bridge(), OnFormFieldDidChange).Times(0);
  android_autofill_manager().OnTextFieldDidScroll(
      form2, form2.fields()[1].global_id(), AutofillManagerTestApi::pass_key());
}

// Tests that a form submission of an ongoing Autofill session is propagated to
// Java.
TEST_F(AndroidAutofillProviderTest, OnFormSubmitted) {
  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  // Start an Autofill session.
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[0].global_id());

  EXPECT_CALL(provider_bridge(),
              OnFormSubmitted(mojom::SubmissionSource::FORM_SUBMISSION));
  android_autofill_manager().OnFormSubmitted(
      form, mojom::SubmissionSource::FORM_SUBMISSION,
      AutofillManagerTestApi::pass_key());
}

// Tests that a form submission of an ongoing Autofill session with source
// DOM_MUTATION_AFTER_AUTOFILL is propagated to Java for password forms.
TEST_F(AndroidAutofillProviderTest,
       DomMutationAfterAutofillFormSubmission_PasswordForm) {
  base::test::ScopedFeatureList scoped_feature_list{
      features::kAutofillAcceptDomMutationAfterAutofillSubmission};
  FormData form =
      CreateFormDataForFrame(CreateTestPasswordFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  // Start an Autofill session.
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[0].global_id());

  EXPECT_CALL(
      provider_bridge(),
      OnFormSubmitted(mojom::SubmissionSource::DOM_MUTATION_AFTER_AUTOFILL))
      .Times(1);
  android_autofill_manager().OnFormSubmitted(
      form, mojom::SubmissionSource::DOM_MUTATION_AFTER_AUTOFILL,
      AutofillManagerTestApi::pass_key());
}

// Tests that a form submission of an ongoing Autofill session with source
// DOM_MUTATION_AFTER_AUTOFILL is not propagated to Java for non-password forms.
TEST_F(AndroidAutofillProviderTest,
       DomMutationAfterAutofillFormSubmission_NonPasswordForm) {
  base::test::ScopedFeatureList scoped_feature_list{
      features::kAutofillAcceptDomMutationAfterAutofillSubmission};
  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  // Start an Autofill session.
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[0].global_id());

  EXPECT_CALL(
      provider_bridge(),
      OnFormSubmitted(mojom::SubmissionSource::DOM_MUTATION_AFTER_AUTOFILL))
      .Times(0);
  android_autofill_manager().OnFormSubmitted(
      form, mojom::SubmissionSource::DOM_MUTATION_AFTER_AUTOFILL,
      AutofillManagerTestApi::pass_key());
}

// Tests that a form submission of an ongoing Autofill session is propagated to
// Java directly on submission, even if the form submission was not known to be
// a success.
TEST_F(AndroidAutofillProviderTest, FormSubmissionHappensDirectly) {
  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  // Start an Autofill session.
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[0].global_id());

  EXPECT_CALL(
      provider_bridge(),
      OnFormSubmitted(mojom::SubmissionSource::PROBABLY_FORM_SUBMITTED));
  android_autofill_manager().OnFormSubmitted(
      form, mojom::SubmissionSource::PROBABLY_FORM_SUBMITTED,
      AutofillManagerTestApi::pass_key());
}

// Tests the predictions from `password_manager::FormDataParser` are used to
// overwrite all type predictions of the respective `FormDataAndroidField`s.
TEST_F(AndroidAutofillProviderTest,
       UsePasswordManagerOverridesInPrefillRequest) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  FormData form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  ASSERT_TRUE(android_autofill_manager().FindCachedFormById(form.global_id()));

  auto has_field_type = [](FieldType field_type) {
    return Pointee(
        Property(&FormFieldDataAndroid::field_types, Eq(field_type)));
  };
  EXPECT_CALL(provider_bridge(),
              SendPrefillRequest(EqualsFormDataWithFields(
                  form, ElementsAre(has_field_type(FieldType::USERNAME),
                                    has_field_type(FieldType::PASSWORD)))));
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      form.global_id());
}

// Tests that the session id used in a prefill request is also used for starting
// the Autofill session even if the forms are not similar as long as their form
// signatures (and predictions) match.
TEST_F(AndroidAutofillProviderTest,
       SessionIdIsReusedForCachedFormsAsLongAsPredictionsAgree) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  FormData form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  ASSERT_TRUE(android_autofill_manager().FindCachedFormById(form.global_id()));
  FormData changed_form = form;
  changed_form.set_name_attribute(changed_form.name_attribute() +
                                  u"some-suffix");

  SessionId cache_session_id = SessionId(0);
  MockFunction<void()> check;
  {
    InSequence s;
    EXPECT_CALL(provider_bridge(), SendPrefillRequest(EqualsFormData(form)))
        .WillOnce(SaveSessionId(&cache_session_id));
    EXPECT_CALL(check, Call);
    // Pass a lambda to perform the check because passing `cache_session_id`
    // would match against the current value of cache_session_id (0).
    EXPECT_CALL(provider_bridge(),
                StartAutofillSession(
                    EqualsFormDataWithSessionId(
                        changed_form, Truly([&cache_session_id](SessionId id) {
                          return id == cache_session_id;
                        })),
                    EqualsFieldInfo(/*index=*/0),
                    /*has_server_predictions=*/true));
  }

  // Upon receiving server predictions a prefill request should be sent.
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      form.global_id());
  check.Call();

  // The changed form has the same signature as the cached form - therefore it
  // should have the session id of the cached form.
  ASSERT_EQ(CalculateFormSignature(form), CalculateFormSignature(changed_form));
  android_autofill_manager().OnFormsSeen({changed_form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  android_autofill_manager().OnAskForValuesToFillTest(
      changed_form, changed_form.fields().front().global_id());
}

// Tests that new document navigation (manager reset) cancels the ongoing
// autofill session
TEST_F(AndroidAutofillProviderTest, CancelSessionOnNavigation) {
  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  EXPECT_CALL(
      provider_bridge(),
      StartAutofillSession(EqualsFormData(form), EqualsFieldInfo(/*index=*/0),
                           /*has_server_predictions=*/false));
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields().front().global_id());

  EXPECT_CALL(provider_bridge(), CancelSession());
  Reset(autofill_driver());
}

// Tests that OnSelectControlSelectionChanged updates the current field's origin
// when the feature flag is enabled.
TEST_F(AndroidAutofillProviderTest,
       OnSelectControlSelectionChangedUpdatesOrigin) {
  base::test::ScopedFeatureList scoped_feature_list{
      features::kAndroidAutofillFieldsUpdatedOnSelect};

  FormData form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  // Start an Autofill session.
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields()[0].global_id());

  EXPECT_EQ(test_api(autofill_provider()).last_focused_field_origin(),
            form.fields()[0].origin());

  // Create a new field with a different origin.
  FormFieldData field = form.fields()[1];
  url::Origin new_origin = url::Origin::Create(GURL("https://bar.com"));
  field.set_origin(new_origin);

  // Simulate OnSelectControlSelectionChanged.
  autofill_provider().OnSelectControlSelectionChanged(
      &android_autofill_manager(), form, field);

  EXPECT_EQ(test_api(autofill_provider()).last_focused_field_origin(),
            new_origin);
}

class AndroidAutofillProviderWithCredManTest
    : public AndroidAutofillProviderTestBase {
 public:
  void SetUp() override {
    AndroidAutofillProviderTestBase::SetUp();

    autofill_provider().MaybeInitKeyboardSuppressor();

    // Navigation creates the AndroidAutofillManager for the main frame.
    NavigateAndCommit(GURL("https://foo.com"));
    FocusWebContentsOnMainFrame();

    // Load a form with webuthn-annotated username and regular password fields.
    test_webauthn_form_ = CreateFormDataForFrame(
        CreateTestWebAuthnPasswordFormData(), main_frame_token());
    url::Origin foo_origin = url::Origin::Create(GURL("https://foo.com"));
    test_api(test_webauthn_form_).field(0).set_origin(foo_origin);
    test_api(test_webauthn_form_).field(1).set_origin(foo_origin);

    InitializeWebAuthnFactoryWithMock();
  }

  void TearDown() override {
    webauthn::WebAuthnClientAndroid::ClearClientForTesting();
    AndroidAutofillProviderTestBase::TearDown();
  }

  void InitializeWebAuthnFactoryWithMock() {
    webauthn::WebAuthnClientAndroid::SetClient(
        std::make_unique<webauthn::StubWebAuthnClientAndroid>());
    auto mock_cred_man_delegate =
        std::make_unique<NiceMock<webauthn::MockWebAuthnCredManDelegate>>();
    webauthn::test_api(web_authn_delegate_factory())
        .EmplaceDelegateForFrame(main_frame(),
                                 std::move(mock_cred_man_delegate));

    ON_CALL(cred_man_delegate(), HasPasskeys())
        .WillByDefault(
            Return(webauthn::WebAuthnCredManDelegate::State::kHasPasskeys));
  }

  webauthn::WebAuthnCredManDelegateFactory* web_authn_delegate_factory() {
    return webauthn::WebAuthnCredManDelegateFactory::GetFactory(web_contents());
  }

  const FormData& test_form() const { return test_webauthn_form_; }

  const FormFieldData& webauthn_email_field() const {
    return test_form().fields()[0];
  }

  const FormFieldData& non_webauthn_password_field() const {
    return test_form().fields()[1];
  }

  void FocusFormField(const FormFieldData& field) {
    // TODO(crbug.com/537704721): This is a hack to make the tests happy.
    // Otherwise, the tests rely on a particular, unrealistic order of events.
    // So either the tests or the production code are wrong.
    test_api(autofill_provider())
        .StartNewSession(&android_autofill_manager(), test_webauthn_form_,
                         field);

    android_autofill_manager().OnFocusOnFormField(
        test_webauthn_form_, field.global_id(),
        AutofillManagerTestApi::pass_key());
    android_autofill_manager().OnAskForValuesToFillTest(test_webauthn_form_,
                                                        field.global_id());
  }

  webauthn::MockWebAuthnCredManDelegate& cred_man_delegate() {
    return *static_cast<webauthn::MockWebAuthnCredManDelegate*>(
        web_authn_delegate_factory()->GetRequestDelegate(main_frame()));
  }

  TouchToFillKeyboardSuppressor& keyboard_suppressor() {
    return test_api(autofill_provider()).keyboard_suppressor();
  }

 private:
  FormData test_webauthn_form_;
};

TEST_F(AndroidAutofillProviderWithCredManTest, CallsCredManOnlyOnce) {
  EXPECT_CALL(cred_man_delegate(),
              TriggerCredManUi(Eq(
                  webauthn::WebAuthnCredManDelegate::RequestPasswords(false))));
  FocusFormField(webauthn_email_field());

  // No further call!
  FocusFormField(non_webauthn_password_field());
  FocusFormField(webauthn_email_field());
}

TEST_F(AndroidAutofillProviderWithCredManTest,
       CallsCredManAgainAfterNavigation) {
  EXPECT_CALL(cred_man_delegate(),
              TriggerCredManUi(Eq(
                  webauthn::WebAuthnCredManDelegate::RequestPasswords(false))));
  FocusFormField(webauthn_email_field());

  // Navigate which allows CredMan to be shown again.
  NavigateAndCommit(GURL("about:to-do-stuff"));
  EXPECT_CALL(cred_man_delegate(),
              TriggerCredManUi(Eq(
                  webauthn::WebAuthnCredManDelegate::RequestPasswords(false))));
  FocusFormField(webauthn_email_field());
}

TEST_F(AndroidAutofillProviderWithCredManTest,
       SuppressesKeyboardForCredManCall) {
  // Since CredMan handles this focus, stop the propagation of the focus event.
  EXPECT_CALL(provider_bridge(), OnFocusChanged).Times(0);
  EXPECT_CALL(cred_man_delegate(),
              TriggerCredManUi(Eq(
                  webauthn::WebAuthnCredManDelegate::RequestPasswords(false))));
  base::RepeatingCallback<void(bool)> completed_callback;
  EXPECT_CALL(cred_man_delegate(), SetRequestCompletionCallback)
      .WillOnce(SaveArg<0>(&completed_callback));
  FocusFormField(webauthn_email_field());

  // Keyboard is suppressed while CredMan is showing.
  EXPECT_TRUE(keyboard_suppressor().is_suppressing());
  Mock::VerifyAndClearExpectations(&cred_man_delegate());

  // After resetting CredMan, the keyboard suppressing stopped.
  completed_callback.Run(/*success=*/true);
  EXPECT_FALSE(keyboard_suppressor().is_suppressing());
}

TEST_F(AndroidAutofillProviderWithCredManTest,
       LogConditionalPasskeysFlowPasskeysAvailableMetricWithPasskeys) {
  base::HistogramTester histogram_tester;
  ON_CALL(cred_man_delegate(), HasPasskeys())
      .WillByDefault(
          Return(webauthn::WebAuthnCredManDelegate::State::kHasPasskeys));

  // Focus the form field.
  base::RepeatingCallback<void(bool)> completed_callback;
  EXPECT_CALL(cred_man_delegate(), SetRequestCompletionCallback)
      .WillOnce(SaveArg<0>(&completed_callback));
  FocusFormField(webauthn_email_field());

  // Keyboard is suppressed while CredMan is showing.
  EXPECT_TRUE(keyboard_suppressor().is_suppressing());
  Mock::VerifyAndClearExpectations(&cred_man_delegate());

  // Hide CredMan.
  completed_callback.Run(/*success=*/true);

  histogram_tester.ExpectUniqueSample(
      "Autofill.ConditionalPasskeysFlow.PasskeysState",
      webauthn::WebAuthnCredManDelegate::State::kHasPasskeys, 1);
}

TEST_F(AndroidAutofillProviderWithCredManTest,
       LogConditionalPasskeysFlowPasskeysAvailableOnLongPress) {
  ON_CALL(cred_man_delegate(), HasPasskeys())
      .WillByDefault(
          Return(webauthn::WebAuthnCredManDelegate::State::kNoPasskeys));

  // Focus the form field which has no immediate passkey effect.
  FocusFormField(webauthn_email_field());
  EXPECT_FALSE(keyboard_suppressor().is_suppressing());

  // Simulate the long-press suggestion was accepted.
  EXPECT_CALL(cred_man_delegate(),
              TriggerCredManUi(Eq(
                  webauthn::WebAuthnCredManDelegate::RequestPasswords(false))));
  test_api(autofill_provider()).OnTriggerPasskeyRequest();
}

TEST_F(AndroidAutofillProviderWithCredManTest,
       LogConditionalPasskeysFlowPasskeysUnavailableWithoutPasskeys) {
  base::HistogramTester histogram_tester;
  ON_CALL(cred_man_delegate(), HasPasskeys())
      .WillByDefault(
          Return(webauthn::WebAuthnCredManDelegate::State::kNoPasskeys));

  // Focus the form field.
  EXPECT_CALL(cred_man_delegate(), SetRequestCompletionCallback).Times(0);
  FocusFormField(webauthn_email_field());

  // Don't expect Keyboard without CredMan showing.
  EXPECT_FALSE(keyboard_suppressor().is_suppressing());
  Mock::VerifyAndClearExpectations(&cred_man_delegate());

  histogram_tester.ExpectUniqueSample(
      "Autofill.ConditionalPasskeysFlow.PasskeysState",
      webauthn::WebAuthnCredManDelegate::State::kNoPasskeys, 0);
}

TEST_F(AndroidAutofillProviderWithCredManTest, NoCredManWithoutAnnotation) {
  EXPECT_CALL(provider_bridge(), OnFocusChanged);
  EXPECT_CALL(cred_man_delegate(), TriggerCredManUi).Times(0);
  FocusFormField(non_webauthn_password_field());

  EXPECT_FALSE(keyboard_suppressor().is_suppressing());
}

TEST_F(AndroidAutofillProviderWithCredManTest, SkipsCredManCallBeforeReady) {
  ON_CALL(cred_man_delegate(), HasPasskeys())
      .WillByDefault(
          Return(webauthn::WebAuthnCredManDelegate::State::kNotReady));

  EXPECT_CALL(cred_man_delegate(), TriggerCredManUi).Times(0);
  FocusFormField(webauthn_email_field());
}

TEST_F(AndroidAutofillProviderWithCredManTest, OfferLongPressOption) {
  FocusFormField(webauthn_email_field());
  EXPECT_TRUE(provider_bridge_delegate().HasPasskeyRequest());
}

TEST_F(AndroidAutofillProviderWithCredManTest,
       OfferNoLongPressOptionWithoutAnnotation) {
  FocusFormField(non_webauthn_password_field());
  EXPECT_FALSE(provider_bridge_delegate().HasPasskeyRequest());
}

TEST_F(AndroidAutofillProviderWithCredManTest,
       OfferNoLongPressOptionWithoutField) {
  EXPECT_FALSE(provider_bridge_delegate().HasPasskeyRequest());
}

TEST_F(AndroidAutofillProviderWithCredManTest, NotifyFocusOnCredManError) {
  EXPECT_CALL(cred_man_delegate(), TriggerCredManUi);
  base::RepeatingCallback<void(bool)> completed_callback;
  EXPECT_CALL(cred_man_delegate(), SetRequestCompletionCallback)
      .WillOnce(SaveArg<0>(&completed_callback));
  FocusFormField(webauthn_email_field());

  // Since CredMan didn't handles this focus, inform the autofill bridge.
  EXPECT_CALL(provider_bridge(), OnFocusChanged);
  completed_callback.Run(/*success=*/false);
}

class AndroidAutofillProviderWithCredManMultiFrameTest
    : public AndroidAutofillProviderWithCredManTest {
 public:
  void SetUp() override {
    AndroidAutofillProviderWithCredManTest::SetUp();

    // Create a subframe, a form in the subframe and register it in the root
    // manager.
    sub_frame_ = content::RenderFrameHostTester::For(main_frame())
                     ->AppendChild(std::string("child"));
    sub_frame_ = NavigateAndCommitFrame(sub_frame_, GURL("https://bar.com"));
    sub_frame_webauthn_form_ = CreateFormDataForFrame(
        CreateTestWebAuthnPasswordFormData(),
        LocalFrameToken(sub_frame_->GetFrameToken().value()));
    url::Origin bar_origin = url::Origin::Create(GURL("https://bar.com"));
    test_api(sub_frame_webauthn_form_).field(0).set_origin(bar_origin);
    test_api(sub_frame_webauthn_form_).field(1).set_origin(bar_origin);
    android_autofill_manager().OnFormsSeen({sub_frame_webauthn_form_},
                                           /*removed_forms=*/{},
                                           AutofillManagerTestApi::pass_key());

    // Create a mock delegate for the subframe and mock passkeys to be
    // default-available.
    auto sub_frame_mock_delegate =
        std::make_unique<NiceMock<webauthn::MockWebAuthnCredManDelegate>>();
    sub_frame_mock_delegate_ = sub_frame_mock_delegate.get();
    webauthn::test_api(web_authn_delegate_factory())
        .EmplaceDelegateForFrame(sub_frame_,
                                 std::move(sub_frame_mock_delegate));
    ON_CALL(*sub_frame_mock_delegate_, HasPasskeys())
        .WillByDefault(
            Return(webauthn::WebAuthnCredManDelegate::State::kHasPasskeys));
  }

  void TearDown() override {
    sub_frame_mock_delegate_ = nullptr;
    sub_frame_ = nullptr;
    AndroidAutofillProviderWithCredManTest::TearDown();
  }

  void FocusSubFrameFormField(const FormFieldData& field) {
    // TODO(crbug.com/537704721): This is a hack to make the tests happy.
    // Otherwise, the tests rely on a particular, unrealistic order of events.
    // So either the tests or the production code are wrong.
    test_api(autofill_provider())
        .StartNewSession(&android_autofill_manager(), sub_frame_webauthn_form_,
                         field);

    android_autofill_manager().OnFocusOnFormField(
        sub_frame_webauthn_form_, field.global_id(),
        AutofillManagerTestApi::pass_key());
    keyboard_suppressor().OnBeforeAskForValuesToFill(
        android_autofill_manager(), sub_frame_webauthn_form_.global_id(),
        field.global_id(), sub_frame_webauthn_form_);
    android_autofill_manager().OnAskForValuesToFillTest(
        sub_frame_webauthn_form_, field.global_id());
  }

  const FormData& sub_frame_test_form() const {
    return sub_frame_webauthn_form_;
  }

  const FormFieldData& sub_frame_webauthn_email_field() const {
    return sub_frame_test_form().fields()[0];
  }

 protected:
  raw_ptr<content::RenderFrameHost> sub_frame_ = nullptr;
  raw_ptr<webauthn::MockWebAuthnCredManDelegate> sub_frame_mock_delegate_ =
      nullptr;
  FormData sub_frame_webauthn_form_;
};

// Tests that focus events are correctly routed to the delegate of the
// appropriate frame.
TEST_F(AndroidAutofillProviderWithCredManMultiFrameTest,
       CredentialManagerSheetTriggeredOnSubFrameDelegate) {
  // Expect CredMan UI to be triggered on the subframe's delegate
  EXPECT_CALL(*sub_frame_mock_delegate_, TriggerCredManUi);
  // Expect CredMan UI to NOT be triggered on the main frame's delegate
  EXPECT_CALL(cred_man_delegate(), TriggerCredManUi).Times(0);

  FocusSubFrameFormField(sub_frame_webauthn_email_field());
}

// Tests that when CredMan is triggered for a field in an unrelated form
// (different from the active session's form), the session origin is preserved
// rather than overwritten with the unrelated form's origin. (see
// crbug.com/523714535, crbug.com/518084475).
TEST_F(AndroidAutofillProviderWithCredManMultiFrameTest,
       CredManTriggerForUnrelatedFormPreservesSessionOrigin) {
  // 1. Start session on main frame (origin https://foo.com).
  android_autofill_manager().OnFormsSeen({test_form()}, {},
                                         AutofillManagerTestApi::pass_key());
  // Focus main frame field to start session and set origin to foo.com.
  test_api(autofill_provider())
      .StartNewSession(&android_autofill_manager(), test_form(),
                       non_webauthn_password_field());
  android_autofill_manager().OnFocusOnFormField(
      test_form(), non_webauthn_password_field().global_id(),
      AutofillManagerTestApi::pass_key());
  android_autofill_manager().OnAskForValuesToFillTest(
      test_form(), non_webauthn_password_field().global_id());
  ASSERT_EQ(test_api(autofill_provider()).last_focused_field_origin(),
            url::Origin::Create(GURL("https://foo.com")));

  // 2. Focus subframe field (origin https://bar.com) on an unrelated form,
  // which triggers CredMan. Expect CredMan to be triggered on subframe.
  EXPECT_CALL(*sub_frame_mock_delegate_, TriggerCredManUi);

  // Simulate Focus FIRST (which should NOT update origin to bar.com because the
  // field does not belong to the active session form).
  android_autofill_manager().OnFocusOnFormField(
      sub_frame_test_form(), sub_frame_webauthn_email_field().global_id(),
      AutofillManagerTestApi::pass_key());
  EXPECT_EQ(test_api(autofill_provider()).last_focused_field_origin(),
            url::Origin::Create(GURL("https://foo.com")));

  // Simulate AskForValuesToFill() SECOND (which returns early because CredMan
  // is showing) and verify origin is STILL foo.com (not overwritten by
  // bar.com).
  android_autofill_manager().OnAskForValuesToFillTest(
      sub_frame_test_form(), sub_frame_webauthn_email_field().global_id());
  EXPECT_EQ(test_api(autofill_provider()).last_focused_field_origin(),
            url::Origin::Create(GURL("https://foo.com")));
}

// Tests that when CredMan is triggered for a field that belongs to the active
// session's form (even in a different frame of a multi-frame form), the current
// field's origin is proactively updated to that field's origin.
// (see crbug.com/518084475).
TEST_F(AndroidAutofillProviderWithCredManMultiFrameTest,
       CredManTriggerForSessionFormFieldUpdatesOrigin) {
  const url::Origin foo_origin = url::Origin::Create(GURL("https://foo.com"));
  const url::Origin bar_origin = url::Origin::Create(GURL("https://bar.com"));

  FormData multi_frame_form = test::GetFormData({
      .fields = {{
                     .host_frame =
                         LocalFrameToken(main_frame()->GetFrameToken().value()),
                     .origin = foo_origin,
                 },
                 {
                     .host_frame =
                         LocalFrameToken(sub_frame_->GetFrameToken().value()),
                     .autocomplete_attribute = "webauthn",
                     .origin = bar_origin,
                 }},
      .url = "https://foo.com/form.html",
      .main_frame_origin = foo_origin,
  });

  const FormFieldData& foo_field = multi_frame_form.fields()[0];
  const FormFieldData& bar_field = multi_frame_form.fields()[1];

  android_autofill_manager().OnFormsSeen({multi_frame_form}, {},
                                         AutofillManagerTestApi::pass_key());

  // 1. Start session on foo_field (origin https://foo.com).
  test_api(autofill_provider())
      .StartNewSession(&android_autofill_manager(), multi_frame_form,
                       foo_field);
  android_autofill_manager().OnFocusOnFormField(
      multi_frame_form, foo_field.global_id(),
      AutofillManagerTestApi::pass_key());
  android_autofill_manager().OnAskForValuesToFillTest(multi_frame_form,
                                                      foo_field.global_id());
  ASSERT_EQ(test_api(autofill_provider()).last_focused_field_origin(),
            foo_origin);

  // 2. Focus bar_field (origin https://bar.com) which is part of the SAME form
  // and triggers CredMan.
  EXPECT_CALL(*sub_frame_mock_delegate_, TriggerCredManUi);

  // Simulate Focus FIRST (which should proactively update origin to bar.com).
  android_autofill_manager().OnFocusOnFormField(
      multi_frame_form, bar_field.global_id(),
      AutofillManagerTestApi::pass_key());
  EXPECT_EQ(test_api(autofill_provider()).last_focused_field_origin(),
            bar_origin);

  // Simulate AskForValuesToFill() SECOND (which returns early because CredMan
  // is showing) and verify origin is STILL bar.com.
  android_autofill_manager().OnAskForValuesToFillTest(multi_frame_form,
                                                      bar_field.global_id());
  EXPECT_EQ(test_api(autofill_provider()).last_focused_field_origin(),
            bar_origin);
}

// Tests that a compromised renderer cannot spoof the session origin by sending
// a malicious FocusOnFormField() IPC while a CredMan sheet is active.
// (see crbug.com/523714535).
TEST_F(AndroidAutofillProviderWithCredManMultiFrameTest,
       CredManActiveBlocksSpoofedFocusOnFormField) {
  // 1. Start session on main frame (origin https://foo.com) and trigger
  // CredMan.
  android_autofill_manager().OnFormsSeen({test_form()}, {},
                                         AutofillManagerTestApi::pass_key());
  EXPECT_CALL(cred_man_delegate(), TriggerCredManUi);
  android_autofill_manager().OnFocusOnFormField(
      test_form(), webauthn_email_field().global_id(),
      AutofillManagerTestApi::pass_key());
  android_autofill_manager().OnAskForValuesToFillTest(
      test_form(), webauthn_email_field().global_id());
  // Because test_form() was not linked when CredMan opened on this fresh page,
  // UpdateCurrentField() is skipped to prevent cross-origin poisoning, so
  // last_focused_field_origin() remains uninitialized right while CredMan is
  // showing.
  ASSERT_TRUE(
      test_api(autofill_provider()).last_focused_field_origin().opaque());
  ASSERT_EQ(test_api(autofill_provider()).form(), nullptr);

  // 2. Spoof FocusOnFormField() from attacker.com: Attacker sends fake
  // FocusOnFormField() while CredMan is showing. Verify that origin remains
  // unchanged and that the spoof is blocked.
  android_autofill_manager().OnFocusOnFormField(
      sub_frame_test_form(), sub_frame_webauthn_email_field().global_id(),
      AutofillManagerTestApi::pass_key());
  EXPECT_TRUE(
      test_api(autofill_provider()).last_focused_field_origin().opaque());
  EXPECT_EQ(test_api(autofill_provider()).form(), nullptr);
}

// Tests that a compromised renderer cannot spoof the session origin by sending
// a malicious AskForValuesToFill IPC while a CredMan sheet is active.
// (see crbug.com/513237800).
TEST_F(AndroidAutofillProviderWithCredManMultiFrameTest,
       CredManActiveBlocksSpoofedAskForValuesToFill) {
  // 1. Start session on main frame (origin https://foo.com) and trigger
  // CredMan.
  android_autofill_manager().OnFormsSeen({test_form()}, {},
                                         AutofillManagerTestApi::pass_key());
  EXPECT_CALL(cred_man_delegate(), TriggerCredManUi);
  android_autofill_manager().OnFocusOnFormField(
      test_form(), webauthn_email_field().global_id(),
      AutofillManagerTestApi::pass_key());
  android_autofill_manager().OnAskForValuesToFillTest(
      test_form(), webauthn_email_field().global_id());
  ASSERT_TRUE(
      test_api(autofill_provider()).last_focused_field_origin().opaque());
  ASSERT_EQ(test_api(autofill_provider()).form(), nullptr);

  // 2. Spoof AskForValuesToFill() from attacker.com: Attacker sends fake
  // AskForValuesToFill() while CredMan is showing. Verify that origin remains
  // unchanged and that the spoof is blocked.
  android_autofill_manager().OnAskForValuesToFillTest(
      sub_frame_test_form(), sub_frame_webauthn_email_field().global_id());
  EXPECT_TRUE(
      test_api(autofill_provider()).last_focused_field_origin().opaque());
  EXPECT_EQ(test_api(autofill_provider()).form(), nullptr);
}

// Tests that a compromised renderer cannot spoof the session origin by sending
// a malicious SelectControlSelectionChanged() IPC while a CredMan sheet is
// active (see crbug.com/518115316).
TEST_F(AndroidAutofillProviderWithCredManMultiFrameTest,
       CredManActiveBlocksSpoofedSelectControlSelectionChanged) {
  base::test::ScopedFeatureList scoped_feature_list{
      features::kAndroidAutofillFieldsUpdatedOnSelect};

  // 1. Start session on main frame (origin https://foo.com) and trigger
  // CredMan.
  android_autofill_manager().OnFormsSeen({test_form()}, {},
                                         AutofillManagerTestApi::pass_key());
  EXPECT_CALL(cred_man_delegate(), TriggerCredManUi);
  android_autofill_manager().OnFocusOnFormField(
      test_form(), webauthn_email_field().global_id(),
      AutofillManagerTestApi::pass_key());
  android_autofill_manager().OnAskForValuesToFillTest(
      test_form(), webauthn_email_field().global_id());
  ASSERT_TRUE(
      test_api(autofill_provider()).last_focused_field_origin().opaque());
  ASSERT_EQ(test_api(autofill_provider()).form(), nullptr);

  // 2. Spoof SelectControlSelectionChanged() from attacker.com: Attacker sends
  // fake SelectControlSelectionChanged() while CredMan is showing. Verify that
  // origin remains unchanged and that the spoof is blocked.
  autofill_provider().OnSelectControlSelectionChanged(
      &android_autofill_manager(), sub_frame_test_form(),
      sub_frame_webauthn_email_field());

  // Verify origin remains unchanged (spoof blocked!).
  EXPECT_TRUE(
      test_api(autofill_provider()).last_focused_field_origin().opaque());
  EXPECT_EQ(test_api(autofill_provider()).form(), nullptr);
}

class AndroidAutofillProviderCredManSpoofSheetStatusTest
    : public AndroidAutofillProviderWithCredManTest {
 public:
  static constexpr char kAttackerOrigin[] = "https://attacker.com";
  static constexpr char kVictimOrigin[] = "https://victim.com";

  void SetUp() override {
    AndroidAutofillProviderWithCredManTest::SetUp();

    // Navigate main frame to victim origin.
    // This will destroy the delegate emplaced by base class SetUp.
    NavigateAndCommit(GURL(kVictimOrigin));

    // Re-emplace mock delegate for main frame.
    auto mock_delegate =
        std::make_unique<NiceMock<webauthn::MockWebAuthnCredManDelegate>>();
    ON_CALL(*mock_delegate, HasPasskeys())
        .WillByDefault(
            Return(webauthn::WebAuthnCredManDelegate::State::kHasPasskeys));

    webauthn::test_api(web_authn_delegate_factory())
        .EmplaceDelegateForFrame(main_frame(), std::move(mock_delegate));

    // Create subframe (attacker).
    attacker_frame_ = content::RenderFrameHostTester::For(main_frame())
                          ->AppendChild(std::string("child"));
    attacker_frame_ = NavigateAndCommitFrame(
        attacker_frame_, GURL(std::string(kAttackerOrigin)));
  }

  void TearDown() override {
    attacker_frame_ = nullptr;
    AndroidAutofillProviderWithCredManTest::TearDown();
  }

 protected:
  raw_ptr<content::RenderFrameHost> attacker_frame_ = nullptr;
};

// Tests that navigation in an attacker subframe does not reset the CredMan
// sheet status if the CredMan UI was triggered by a different frame.
// (See crbug.com/519485704)
TEST_F(AndroidAutofillProviderCredManSpoofSheetStatusTest,
       AttackerSubframeNavDoesNotResetCredManSheetStatus) {
  const url::Origin attacker_origin =
      url::Origin::Create(GURL(kAttackerOrigin));
  const url::Origin victim_origin = url::Origin::Create(GURL(kVictimOrigin));

  // Setup form spanning multiple frames.
  FormData form = GetFormData({
      .fields = {{
                     .host_frame = LocalFrameToken(
                         attacker_frame_->GetFrameToken().value()),
                     .origin = attacker_origin,
                 },
                 {
                     .host_frame =
                         LocalFrameToken(main_frame()->GetFrameToken().value()),
                     .autocomplete_attribute = "webauthn",
                     .origin = victim_origin,
                 }},
      .url = std::string(kVictimOrigin) + "/form.html",
      .main_frame_origin = victim_origin,
  });

  const FormFieldData& attacker_field = form.fields()[0];
  const FormFieldData& victim_field = form.fields()[1];

  android_autofill_manager().OnFormsSeen({form}, {},
                                         AutofillManagerTestApi::pass_key());

  // Attacker frame queries autofill.
  android_autofill_manager().OnAskForValuesToFillTest(
      form, attacker_field.global_id());
  ASSERT_EQ(test_api(autofill_provider()).last_queried_field_rfh_id(),
            attacker_frame_->GetGlobalId());

  // Victim frame focuses, triggering CredMan sheet.
  android_autofill_manager().OnFocusOnFormField(
      form, victim_field.global_id(), AutofillManagerTestApi::pass_key());
  ASSERT_TRUE(test_api(autofill_provider()).is_credman_sheet_showing());

  // Navigate attacker frame. This should keep `credman_sheet_status_ ==
  // kIsShowing` because the navigated frame is not the one hosting the CredMan
  // sheet.
  NavigateAndCommitFrame(
      attacker_frame_, GURL(std::string(kAttackerOrigin) + "/navigated.html"));
  EXPECT_TRUE(test_api(autofill_provider()).is_credman_sheet_showing());
}

// Tests that a compromised renderer cannot reset the session state or CredMan
// sheet status by sending a malicious FormSubmitted IPC while a CredMan sheet
// is active. (See crbug.com/534856303)
TEST_F(AndroidAutofillProviderCredManSpoofSheetStatusTest,
       FormSubmittedWhileCredManShowingDoesNotResetSession) {
  const url::Origin attacker_origin =
      url::Origin::Create(GURL(kAttackerOrigin));
  const url::Origin victim_origin = url::Origin::Create(GURL(kVictimOrigin));

  // Setup form spanning multiple frames.
  FormData form = GetFormData({
      .fields = {{
                     .host_frame = LocalFrameToken(
                         attacker_frame_->GetFrameToken().value()),
                     .origin = attacker_origin,
                 },
                 {
                     .host_frame =
                         LocalFrameToken(main_frame()->GetFrameToken().value()),
                     .autocomplete_attribute = "webauthn",
                     .origin = victim_origin,
                 }},
      .url = std::string(kVictimOrigin) + "/form.html",
      .main_frame_origin = victim_origin,
  });

  const FormFieldData& attacker_field = form.fields()[0];
  const FormFieldData& victim_field = form.fields()[1];

  android_autofill_manager().OnFormsSeen({form}, {},
                                         AutofillManagerTestApi::pass_key());

  // Victim frame queries and focuses, starting session and triggering CredMan
  // sheet.
  test_api(autofill_provider())
      .StartNewSession(&android_autofill_manager(), form, victim_field);
  android_autofill_manager().OnFocusOnFormField(
      form, victim_field.global_id(), AutofillManagerTestApi::pass_key());
  android_autofill_manager().OnAskForValuesToFillTest(form,
                                                      victim_field.global_id());
  ASSERT_TRUE(test_api(autofill_provider()).is_credman_sheet_showing());
  ASSERT_EQ(test_api(autofill_provider()).last_focused_field_origin(),
            victim_origin);

  // Attacker subframe attempts to submit form while CredMan is active.
  EXPECT_CALL(provider_bridge(), OnFormSubmitted).Times(0);
  android_autofill_manager().OnFormSubmitted(
      form, mojom::SubmissionSource::FORM_SUBMISSION,
      AutofillManagerTestApi::pass_key());

  // Verify sheet status is still showing and session was not reset.
  EXPECT_TRUE(test_api(autofill_provider()).is_credman_sheet_showing());
  EXPECT_EQ(test_api(autofill_provider()).last_focused_field_origin(),
            victim_origin);

  // Subsequent AskForValuesToFill from attacker frame should be ignored while
  // CredMan is showing, preventing origin poisoning.
  android_autofill_manager().OnAskForValuesToFillTest(
      form, attacker_field.global_id());
  EXPECT_EQ(test_api(autofill_provider()).last_focused_field_origin(),
            victim_origin);
}

using AndroidAutofillProviderPrefillRequestTest = AndroidAutofillProviderTest;

// Tests that we can send another prefill request after navigation.
TEST_F(AndroidAutofillProviderPrefillRequestTest,
       MultiplePrefillRequestsOnNavigation) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  FormData form = test::GetFormData(
      {.fields = {{.autocomplete_attribute = "username"},
                  {.autocomplete_attribute = "current-password",
                   .form_control_type = FormControlType::kInputPassword}}});

  EXPECT_CALL(provider_bridge(), SendPrefillRequest).Times(2);
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      form.global_id());
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields().front().global_id());
  Reset(autofill_driver());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      form.global_id());
}

// Tests that a metric is emitted if prefill requests are supported and there
// was not enough time to send a prefill request.
TEST_F(AndroidAutofillProviderPrefillRequestTest,
       OnAskForValuesToFillRecordsPrefillRequestStateUmaMetric) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  base::HistogramTester histogram_tester;
  FormData form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields().front().global_id());
  histogram_tester.ExpectUniqueSample(
      AndroidAutofillProvider::kPrefillRequestStateUma,
      PrefillRequestState::kRequestNotSentNoTime, 1);
}

// Tests that no prefill requests are sent on Android versions prior to U even
// if all other requirements are satisfied.
TEST_F(AndroidAutofillProviderPrefillRequestTest,
       NoPrefillRequestOnVersionsPriorToU) {
  // This test only makes sense on Android versions smaller than U.
  if (base::android::android_info::sdk_int() >=
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  FormData form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  ASSERT_TRUE(android_autofill_manager().FindCachedFormById(form.global_id()));

  // No prefill request is ever sent.
  EXPECT_CALL(provider_bridge(), SendPrefillRequest).Times(0);
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      form.global_id());
}

// Tests that a prefill request is sent if all requirements for it are
// satisfied.
TEST_F(AndroidAutofillProviderPrefillRequestTest, SendPrefillRequest) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  FormData form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  ASSERT_TRUE(android_autofill_manager().FindCachedFormById(form.global_id()));

  // Upon receiving server predictions a prefill request should be sent.
  EXPECT_CALL(provider_bridge(), SendPrefillRequest(EqualsFormData(form)));
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      form.global_id());
}

// Tests that no prefill request is sent if there is already an ongoing Autofill
// session.
TEST_F(AndroidAutofillProviderPrefillRequestTest,
       NoPrefillRequestIfOngoingSession) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  base::HistogramTester histogram_tester;
  FormData login_form1 =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({login_form1}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  EXPECT_CALL(provider_bridge(), StartAutofillSession);
  android_autofill_manager().OnAskForValuesToFillTest(
      login_form1, login_form1.fields().front().global_id());
  histogram_tester.ExpectUniqueSample(
      AndroidAutofillProvider::kPrefillRequestStateUma,
      PrefillRequestState::kRequestNotSentNoTime, 1);

  FormData login_form2 =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({login_form2}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  ASSERT_TRUE(
      android_autofill_manager().FindCachedFormById(login_form2.global_id()));

  // No prefill request is ever sent.
  EXPECT_CALL(provider_bridge(), SendPrefillRequest).Times(0);
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      login_form2.global_id());
}

// Tests that no prefill request is sent if there has already been another
// prefill request.
TEST_F(AndroidAutofillProviderPrefillRequestTest, NoSecondPrefillRequest) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  base::HistogramTester histogram_tester;
  FormData login_form1 =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({login_form1}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  ASSERT_TRUE(
      android_autofill_manager().FindCachedFormById(login_form1.global_id()));

  FormData login_form2 =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({login_form2}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  ASSERT_TRUE(
      android_autofill_manager().FindCachedFormById(login_form2.global_id()));
  // The helper method should generate different ids every time it is called.
  ASSERT_FALSE(
      FormData::IdenticalAndEquivalentDomElements(login_form1, login_form2));

  EXPECT_CALL(provider_bridge(),
              SendPrefillRequest(EqualsFormData(login_form1)));
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      login_form1.global_id());
  Mock::VerifyAndClearExpectations(&provider_bridge());

  EXPECT_CALL(provider_bridge(), SendPrefillRequest).Times(0);
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      login_form2.global_id());

  android_autofill_manager().OnAskForValuesToFillTest(
      login_form2, login_form2.fields().front().global_id());
  histogram_tester.ExpectUniqueSample(
      AndroidAutofillProvider::kPrefillRequestStateUma,
      PrefillRequestState::kRequestNotSentMaxNumberReached, 1);
}

// Tests that the session id used in a prefill request is also used for starting
// the Autofill session for that form.
TEST_F(AndroidAutofillProviderPrefillRequestTest,
       SessionIdIsReusedForCachedForms) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  FormData form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  ASSERT_TRUE(android_autofill_manager().FindCachedFormById(form.global_id()));

  // Upon receiving server predictions a prefill request should be sent.
  SessionId cache_session_id = SessionId(0);
  EXPECT_CALL(provider_bridge(), SendPrefillRequest(EqualsFormData(form)))
      .WillOnce(SaveSessionId(&cache_session_id));
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      form.global_id());
  Mock::VerifyAndClearExpectations(&provider_bridge());

  EXPECT_CALL(
      provider_bridge(),
      StartAutofillSession(EqualsFormDataWithSessionId(form, cache_session_id),
                           EqualsFieldInfo(/*index=*/0),
                           /*has_server_predictions=*/true));
  android_autofill_manager().OnAskForValuesToFillTest(
      form, form.fields().front().global_id());
}

// Tests that the session id used in a prefill request is not reused when
// starting a session on a form with the same id, but changed field content.
TEST_F(AndroidAutofillProviderPrefillRequestTest,
       SessionIdIsNotReusedForCachedFormsIfContentHasChanged) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  base::HistogramTester histogram_tester;
  FormData form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  // Upon receiving server predictions a prefill request should be sent.
  SessionId cache_session_id = SessionId(0);
  EXPECT_CALL(provider_bridge(), SendPrefillRequest(EqualsFormData(form)))
      .WillOnce(SaveSessionId(&cache_session_id));
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      form.global_id());
  Mock::VerifyAndClearExpectations(&provider_bridge());

  FormData changed_form = form;
  test_api(changed_form).Remove(-1);
  android_autofill_manager().OnFormsSeen({changed_form},
                                         /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  SessionId autofill_session_id = SessionId(0);
  EXPECT_CALL(provider_bridge(),
              StartAutofillSession(EqualsFormData(changed_form),
                                   EqualsFieldInfo(/*index=*/0),
                                   /*has_server_predictions=*/true))
      .WillOnce(WithArg<0>(SaveSessionId(&autofill_session_id)));
  android_autofill_manager().OnAskForValuesToFillTest(
      changed_form, changed_form.fields().front().global_id());
  Mock::VerifyAndClearExpectations(&provider_bridge());

  // A new session id is used to start the Autofill session.
  EXPECT_NE(cache_session_id, autofill_session_id);
  histogram_tester.ExpectUniqueSample(
      AndroidAutofillProvider::kPrefillRequestStateUma,
      PrefillRequestState::kRequestSentFormChanged, 1);
}

// Tests that the session id used in a prefill request is only used once to
// start an Autofill session. If the user then focuses on a different form
// before returning to the (formerly) cached form, a new session is started.
TEST_F(AndroidAutofillProviderPrefillRequestTest,
       SessionIdIsNotReusedMultipleAutofillSessions) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  FormData pw_form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  FormData pi_form = CreateFormDataForFrame(
      CreateTestPersonalInformationFormData(), main_frame_token());
  android_autofill_manager().OnFormsSeen({pw_form, pi_form},
                                         /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());

  // Upon receiving server predictions a prefill request should be sent.
  SessionId cache_session_id = SessionId(0);
  EXPECT_CALL(provider_bridge(), SendPrefillRequest(EqualsFormData(pw_form)))
      .WillOnce(SaveSessionId(&cache_session_id));
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      pw_form.global_id());
  Mock::VerifyAndClearExpectations(&provider_bridge());

  EXPECT_CALL(provider_bridge(),
              StartAutofillSession(
                  EqualsFormDataWithSessionId(pw_form, cache_session_id),
                  EqualsFieldInfo(/*index=*/0),
                  /*has_server_predictions=*/true));
  android_autofill_manager().OnAskForValuesToFillTest(
      pw_form, pw_form.fields().front().global_id());
  Mock::VerifyAndClearExpectations(&provider_bridge());

  // Now focus on a different form.
  SessionId pi_form_session_id = SessionId(0);
  EXPECT_CALL(provider_bridge(),
              StartAutofillSession(EqualsFormData(pi_form),
                                   EqualsFieldInfo(/*index=*/0),
                                   /*has_server_predictions=*/false))
      .WillOnce(WithArg<0>(SaveSessionId(&pi_form_session_id)));
  android_autofill_manager().OnAskForValuesToFillTest(
      pi_form, pi_form.fields().front().global_id());
  Mock::VerifyAndClearExpectations(&provider_bridge());

  // Unrelated forms should have different session ids.
  EXPECT_NE(cache_session_id, pi_form_session_id);

  // Focus back on the original password form.
  SessionId pw_form_second_session_id = SessionId(0);
  EXPECT_CALL(provider_bridge(),
              StartAutofillSession(EqualsFormData(pw_form),
                                   EqualsFieldInfo(/*index=*/0),
                                   /*has_server_predictions=*/true))
      .WillOnce(WithArg<0>(SaveSessionId(&pw_form_second_session_id)));
  android_autofill_manager().OnAskForValuesToFillTest(
      pw_form, pw_form.fields().front().global_id());
  Mock::VerifyAndClearExpectations(&provider_bridge());
  // The session id used when focusing back should be different from both those
  // before.
  EXPECT_NE(cache_session_id, pw_form_second_session_id);
  EXPECT_NE(pi_form_session_id, pw_form_second_session_id);
}

// Tests that metrics are emitted when the bottom sheet is shown.
TEST_F(AndroidAutofillProviderPrefillRequestTest,
       PrefillRequestStateEmittedOnShowingBottomSheet) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  base::HistogramTester histogram_tester;

  FormData login_form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({login_form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      login_form.global_id());

  EXPECT_CALL(provider_bridge(), StartAutofillSession);
  android_autofill_manager().OnAskForValuesToFillTest(
      login_form, login_form.fields().front().global_id());

  // Simulate a successfully shown bottom sheet.
  provider_bridge_delegate().OnShowBottomSheetResult(
      /*is_shown=*/true, /*provided_autofill_structure=*/true);
  histogram_tester.ExpectUniqueSample(
      AndroidAutofillProvider::kPrefillRequestStateUma,
      PrefillRequestState::kRequestSentStructureProvidedBottomSheetShown, 1);
}

// Tests that the correct metrics are emitted when the bottom sheet is not shown
// and no view structure was provided to the Android framework.
TEST_F(AndroidAutofillProviderPrefillRequestTest,
       PrefillRequestStateEmittedOnNotShowingBottomSheetWithoutViewStructure) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  base::HistogramTester histogram_tester;
  FormData login_form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({login_form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      login_form.global_id());
  android_autofill_manager().OnAskForValuesToFillTest(
      login_form, login_form.fields().front().global_id());

  // Simulate a successfully shown bottom sheet.
  provider_bridge_delegate().OnShowBottomSheetResult(
      /*is_shown=*/false, /*provided_autofill_structure=*/false);
  histogram_tester.ExpectUniqueSample(
      AndroidAutofillProvider::kPrefillRequestStateUma,
      PrefillRequestState::kRequestSentStructureNotProvided, 1);
}

// Tests that the correct metrics are emitted when the bottom sheet is not shown
// and a view structure was provided to the Android framework.
TEST_F(AndroidAutofillProviderPrefillRequestTest,
       PrefillRequestStateEmittedOnNotShowingBottomSheetWithViewStructure) {
  if (base::android::android_info::sdk_int() <
      base::android::android_info::SDK_VERSION_U) {
    GTEST_SKIP();
  }

  base::HistogramTester histogram_tester;
  FormData login_form =
      CreateFormDataForFrame(CreateTestLoginForm(), main_frame_token());
  android_autofill_manager().OnFormsSeen({login_form}, /*removed_forms=*/{},
                                         AutofillManagerTestApi::pass_key());
  android_autofill_manager().SimulatePropagateAutofillPredictions(
      login_form.global_id());
  android_autofill_manager().OnAskForValuesToFillTest(
      login_form, login_form.fields().front().global_id());

  // Simulate a successfully shown bottom sheet.
  provider_bridge_delegate().OnShowBottomSheetResult(
      /*is_shown=*/false, /*provided_autofill_structure=*/true);
  histogram_tester.ExpectUniqueSample(
      AndroidAutofillProvider::kPrefillRequestStateUma,
      PrefillRequestState::kRequestSentStructureProvidedBottomSheetNotShown, 1);
}

class AndroidAutofillProviderTestHidingLogic
    : public AndroidAutofillProviderTest {
 public:
  void SetUp() override {
    AndroidAutofillProviderTest::SetUp();
    NavigateAndCommit(GURL("https://foo.com"));
    sub_frame_ = content::RenderFrameHostTester::For(main_frame())
                     ->AppendChild(std::string("child"));
    sub_frame_ = NavigateAndCommitFrame(sub_frame_, GURL("https://bar.com"));
  }

  void TearDown() override {
    sub_frame_ = nullptr;
    AndroidAutofillProviderTest::TearDown();
  }

  void AskForValuesToFill(content::RenderFrameHost* rfh) {
    FocusWebContentsOnFrame(rfh);
    FormData form =
        CreateFormDataForFrame(CreateTestPersonalInformationFormData(),
                               LocalFrameToken(rfh->GetFrameToken().value()));
    android_autofill_manager(rfh).OnFormsSeen(
        {form}, /*removed_forms=*/{}, AutofillManagerTestApi::pass_key());
    // Start an Autofill session.
    android_autofill_manager(rfh).OnAskForValuesToFillTest(
        form, form.fields()[0].global_id());
  }

 protected:
  raw_ptr<content::RenderFrameHost> sub_frame_ = nullptr;
};

// Tests that if the popup is shown in the *main frame*, destruction of the
// *sub frame* does not hide the popup.
TEST_F(AndroidAutofillProviderTestHidingLogic,
       KeepOpenInMainFrameOnSubFrameDestruction) {
  AskForValuesToFill(main_frame());
  EXPECT_CALL(provider_bridge(), HideDatalistPopup).Times(0);
  content::RenderFrameHostTester::For(sub_frame_)->Detach();
  // Verify and clear before TearDown() closes the popup.
  Mock::VerifyAndClearExpectations(&provider_bridge());
}

// Tests that if the popup is shown in the *main frame*, a navigation in the
// *sub frame* does not hide the popup.
TEST_F(AndroidAutofillProviderTestHidingLogic,
       KeepOpenInMainFrameOnSubFrameNavigation) {
  AskForValuesToFill(main_frame());
  EXPECT_CALL(provider_bridge(), HideDatalistPopup).Times(0);
  NavigateAndCommitFrame(sub_frame_, GURL("https://bar.com/"));
  // Verify and clear before TearDown() closes the popup.
  Mock::VerifyAndClearExpectations(&provider_bridge());
}

// Tests that if the popup is shown in the *main frame*, destruction of the
// *main frame* resets the java instance which hides the popup.
TEST_F(AndroidAutofillProviderTestHidingLogic, HideInMainFrameOnDestruction) {
  AskForValuesToFill(main_frame());
  EXPECT_CALL(provider_bridge(), Reset);
  // TearDown() destructs the main frame.
}

// Tests that if the popup is shown in the *sub frame*, destruction of the
// *sub frame* hides the popup.
TEST_F(AndroidAutofillProviderTestHidingLogic, HideInSubFrameOnDestruction) {
  AskForValuesToFill(sub_frame_);
  EXPECT_CALL(provider_bridge(), Reset);
  NavigateAndCommitFrame(sub_frame_, GURL("https://bar.com/"));
  // Verify and clear before TearDown() closes the popup.
  Mock::VerifyAndClearExpectations(&provider_bridge());
}

// Tests that if the popup is shown in the *main frame*, a navigation in the
// *main frame* hides the popup.
TEST_F(AndroidAutofillProviderTestHidingLogic,
       HideInMainFrameOnMainFrameNavigation) {
  AskForValuesToFill(main_frame());
  EXPECT_CALL(provider_bridge(), HideDatalistPopup).Times(AtLeast(1));
  NavigateAndCommitFrame(main_frame(), GURL("https://bar.com/"));
}

// Tests that if the popup is shown in the *sub frame*, a navigation in the
// *sub frame* hides the popup.
//
// TODO(crbug.com/40283554): Disabled because AndroidAutofillProvider::Reset()
// resets AndroidAutofillProvider::field_rfh_ before RenderFrameDeleted(), which
// prevents OnPopupHidden().
TEST_F(AndroidAutofillProviderTestHidingLogic,
       DISABLED_HideInSubFrameOnSubFrameNavigation) {
  AskForValuesToFill(sub_frame_);
  EXPECT_CALL(provider_bridge(), HideDatalistPopup).Times(AtLeast(1));
  NavigateAndCommitFrame(sub_frame_, GURL("https://bar.com/"));
}

// Tests that if the popup is shown in the *sub frame*, a navigation in the
// *main frame* hides the popup.
TEST_F(AndroidAutofillProviderTestHidingLogic,
       HideInSubFrameOnMainFrameNavigation) {
  AskForValuesToFill(main_frame());
  EXPECT_CALL(provider_bridge(), HideDatalistPopup).Times(AtLeast(1));
  NavigateAndCommitFrame(main_frame(), GURL("https://bar.com/"));
}

// Tests that AndroidAutofillProvider::last_queried_field_rfh_id_ is updated
// when different frames are queried.
TEST_F(AndroidAutofillProviderTestHidingLogic,
       FollowAskForValuesInDifferentFrames) {
  AskForValuesToFill(main_frame());
  AskForValuesToFill(sub_frame_);
  EXPECT_CALL(provider_bridge(), Reset);
  NavigateAndCommitFrame(sub_frame_, GURL("https://bar.com/"));
}

}  // namespace
}  // namespace autofill
