// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/omnibox/omnibox_view.h"

#include <stddef.h>
#include <stdio.h>

#include <array>
#include <memory>
#include <optional>
#include <string>

#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/run_until.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/contextual_search/contextual_search_web_contents_helper.h"
#include "chrome/browser/contextual_tasks/contextual_tasks_service_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/page_load_metrics/chrome_initiator_location.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/navigator/browser_navigator_params.h"
#include "chrome/browser/ui/omnibox/omnibox_controller.h"
#include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
#include "chrome/browser/ui/omnibox/omnibox_tab_helper.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/views/omnibox/omnibox_popup_closer.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/search_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/bookmark_utils.h"
#include "components/bookmarks/test/bookmark_test_helpers.h"
#include "components/contextual_tasks/public/mock_contextual_tasks_service.h"
#include "components/history/core/browser/history_service.h"
#include "components/lens/lens_features.h"
#include "components/omnibox/browser/aim_eligibility_service_features.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/history_quick_provider.h"
#include "components/omnibox/browser/omnibox_popup_selection.h"
#include "components/omnibox/browser/test_location_bar_model.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/page_load_metrics/browser/navigation_handle_user_data.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/policy_constants.h"
#include "components/search_engines/enterprise/search_aggregator_policy_handler.h"
#include "components/search_engines/enterprise/site_search_policy_handler.h"
#include "components/search_engines/search_engines_switches.h"
#include "components/search_engines/template_url.h"
#include "components/search_engines/template_url_data.h"
#include "components/search_engines/template_url_service.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/url_loader_interceptor.h"
#include "net/dns/mock_host_resolver.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/gfx/geometry/point.h"
#include "url/url_features.h"

using base::ASCIIToUTF16;
using base::UTF16ToUTF8;
using bookmarks::BookmarkModel;

namespace {

const char16_t kSearchKeyword[] = u"foo";
const char16_t kSearchKeyword2[] = u"footest.com";
const char16_t kSiteSearchPolicyKeyword[] = u"work";
const char16_t kSiteSearchPolicyKeywordWithAtPrefix[] = u"@work";
const ui::KeyboardCode kSearchKeywordKeys[] = {ui::VKEY_F, ui::VKEY_O,
                                               ui::VKEY_O, ui::VKEY_UNKNOWN};
const ui::KeyboardCode kSiteSearchPolicyKeywordKeys[] = {
    ui::VKEY_W, ui::VKEY_O, ui::VKEY_R, ui::VKEY_K, ui::VKEY_UNKNOWN};
const char kSearchURL[] = "http://www.foo.com/search?q={searchTerms}";
const char kSiteSearchPolicyURL[] =
    "http://www.work.com/search?q={searchTerms}";
const char16_t kSearchShortName[] = u"foo";
const char16_t kSiteSearchPolicyName[] = u"Work";
const char16_t kSearchText[] = u"abc";
const ui::KeyboardCode kSearchTextKeys[] = {ui::VKEY_A, ui::VKEY_B, ui::VKEY_C,
                                            ui::VKEY_UNKNOWN};
const char kSearchTextURL[] = "http://www.foo.com/search?q=abc";
const char kSiteSearchPolicyTextURL[] = "http://www.work.com/search?q=abc";
const char kSearchAggregatorPolicyIconUrl[] =
    "https://www.aggregator.com/icon.png";
const char16_t kSearchAggregatorPolicyKeyword[] = u"aggregator";
const char16_t kSearchAggregatorPolicyKeywordWithAtPrefix[] = u"@aggregator";
const ui::KeyboardCode kSearchAggregatorPolicyKeywordKeys[] = {
    ui::VKEY_A, ui::VKEY_G, ui::VKEY_G, ui::VKEY_R, ui::VKEY_E,      ui::VKEY_G,
    ui::VKEY_A, ui::VKEY_T, ui::VKEY_O, ui::VKEY_R, ui::VKEY_UNKNOWN};
const char kSearchAggregatorPolicySearchUrl[] =
    "https://www.aggregator.com/search?q={searchTerms}";
const char kSearchAggregatorPolicySuggestUrl[] =
    "https://www.aggregator.com/suggest?q={searchTerms}";
const char16_t kSearchAggregatorPolicyName[] = u"Aggregator";
const char kSearchAggregatorPolicyTextURL[] =
    "https://www.aggregator.com/search?q=abc";

const char kInlineAutocompleteText[] = "def";
const ui::KeyboardCode kInlineAutocompleteTextKeys[] = {
    ui::VKEY_D, ui::VKEY_E, ui::VKEY_F, ui::VKEY_UNKNOWN};

// Hostnames that shall be blocked by host resolver.
constexpr const char* kBlockedHostnames[] = {
    "foo", "*.foo.com", "bar",        "*.bar.com", "abc", "*.abc.com",
    "def", "*.def.com", "*.site.com", "history",   "z"};

struct TestHistoryEntry {
  const char* url;
  const char* title;
  int visit_count;
  int typed_count;
  bool starred;
};
const auto kHistoryEntries = std::to_array<TestHistoryEntry>({
    {"http://www.bar.com/1", "Page 1", 10, 10, false},
    {"http://www.bar.com/2", "Page 2", 9, 9, false},
    {"http://www.bar.com/3", "Page 3", 8, 8, false},
    {"http://www.bar.com/4", "Page 4", 7, 7, false},
    {"http://www.bar.com/5", "Page 5", 6, 6, false},
    {"http://www.bar.com/6", "Page 6", 5, 5, false},
    {"http://www.bar.com/7", "Page 7", 4, 4, false},
    {"http://www.bar.com/8", "Page 8", 3, 3, false},
    {"http://www.bar.com/9", "Page 9", 2, 2, false},
    {"http://www.site.com/path/1", "Site 1", 4, 4, false},
    {"http://www.site.com/path/2", "Site 2", 3, 3, false},
    {"http://www.site.com/path/3", "Site 3", 2, 2, false},

    // To trigger inline autocomplete.
    {"http://www.def.com", "Page def", 10000, 10000, true},

    // Used in particular for the desired TLD test.  This makes it test
    // the interesting case when there's an intranet host with the same
    // name as the .com.
    {"http://bar/", "Bar", 1, 0, false},
});

// Stores the given text to clipboard.
void SetClipboardText(const std::u16string& text) {
  ui::ScopedClipboardWriter writer(ui::ClipboardBuffer::kCopyPaste);
  writer.WriteText(text);
}

#if BUILDFLAG(IS_MAC)
const int kCtrlOrCmdMask = ui::EF_COMMAND_DOWN;
#else
const int kCtrlOrCmdMask = ui::EF_CONTROL_DOWN;
#endif

}  // namespace

class OmniboxViewTest : public InProcessBrowserTest {
 public:
  OmniboxViewTest() = default;
  OmniboxViewTest(const OmniboxViewTest&) = delete;
  OmniboxViewTest& operator=(const OmniboxViewTest&) = delete;

 protected:
  raw_ptr<contextual_tasks::MockContextualTasksService>
      mock_contextual_tasks_service_ = nullptr;

  void SetUpOnMainThread() override {
    ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
    ASSERT_NO_FATAL_FAILURE(SetupComponents());
    chrome::FocusLocationBar(browser());
    ASSERT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));

    identity_test_env_adaptor_ =
        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(
            browser()->GetProfile());
    identity_test_env()->SetPrimaryAccount("test@mail.com",
                                           signin::ConsentLevel::kSignin);
    identity_test_env()->SetRefreshTokenForPrimaryAccount();
    identity_test_env()->SetAutomaticIssueOfAccessTokens(true);
  }

  void TearDownOnMainThread() override {
    mock_contextual_tasks_service_ = nullptr;
    InProcessBrowserTest::TearDownOnMainThread();
  }

  void SetUp() override {
    policy_provider_.SetDefaultReturns(
        /*is_initialization_complete_return=*/true,
        /*is_first_policy_load_complete_return=*/true);
    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
        &policy_provider_);
    InProcessBrowserTest::SetUp();
  }

  void SetUpInProcessBrowserTestFixture() override {
    create_services_subscription_ =
        BrowserContextDependencyManager::GetInstance()
            ->RegisterCreateServicesCallbackForTesting(base::BindRepeating(
                &OmniboxViewTest::OnWillCreateBrowserContextServices,
                base::Unretained(this)));
  }

  static void GetOmniboxViewForBrowser(const BrowserWindowInterface* browser,
                                       OmniboxView** omnibox_view) {
    const BrowserWindow* window = BrowserWindow::FromBrowser(browser);
    ASSERT_TRUE(window);
    LocationBar* location_bar = window->GetLocationBar();
    ASSERT_TRUE(location_bar);
    *omnibox_view = location_bar->GetOmniboxView();
    ASSERT_TRUE(*omnibox_view);
  }

  void GetOmniboxView(OmniboxView** omnibox_view) {
    GetOmniboxViewForBrowser(browser(), omnibox_view);
  }

  OmniboxEditModel* GetOmniboxEditModel() {
    BrowserWindow* window = BrowserWindow::FromBrowser(browser());
    LocationBar* location_bar = window->GetLocationBar();
    return location_bar->GetOmniboxController()->edit_model();
  }

  OmniboxController* GetOmniboxController() {
    BrowserWindow* window = BrowserWindow::FromBrowser(browser());
    LocationBar* location_bar = window->GetLocationBar();
    return location_bar->GetOmniboxController();
  }

  omnibox::OmniboxPopupCloser* GetOmniboxPopupCloser() {
    return omnibox::OmniboxPopupCloser::From(browser());
  }

  static void SendKeyForBrowser(const BrowserWindowInterface* browser,
                                ui::KeyboardCode key,
                                int modifiers) {
    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
        browser, key, (modifiers & ui::EF_CONTROL_DOWN) != 0,
        (modifiers & ui::EF_SHIFT_DOWN) != 0,
        (modifiers & ui::EF_ALT_DOWN) != 0,
        (modifiers & ui::EF_COMMAND_DOWN) != 0));
  }

  void SendKey(ui::KeyboardCode key, int modifiers) {
    SendKeyForBrowser(browser(), key, modifiers);
  }

  void SendKeySequence(const ui::KeyboardCode* keys) {
    for (; *keys != ui::VKEY_UNKNOWN; UNSAFE_TODO(++keys)) {
      ASSERT_NO_FATAL_FAILURE(SendKey(*keys, 0));
    }
  }

  void ExpectBrowserClosed(BrowserWindowInterface* browser,
                           ui::KeyboardCode key,
                           int modifiers) {
    ui_test_utils::BrowserDestroyedObserver observer(browser);
    EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
        browser, key, (modifiers & ui::EF_CONTROL_DOWN) != 0,
        (modifiers & ui::EF_SHIFT_DOWN) != 0,
        (modifiers & ui::EF_ALT_DOWN) != 0,
        (modifiers & ui::EF_COMMAND_DOWN) != 0));
    observer.Wait();
  }

  void NavigateExpectUrl(const GURL& url, int modifiers = 0) {
    content::TestNavigationObserver observer(url);
    observer.WatchExistingWebContents();
    ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_RETURN, modifiers));
    observer.WaitForNavigationFinished();
  }

  void WaitForTabOpenOrClose(int expected_tab_count) {
    int tab_count = browser()->GetTabStripModel()->count();
    if (tab_count == expected_tab_count) {
      return;
    }

    while (!HasFailure() &&
           browser()->GetTabStripModel()->count() != expected_tab_count) {
      content::RunMessageLoop();
    }

    ASSERT_EQ(expected_tab_count, browser()->GetTabStripModel()->count());
  }

  void WaitForAutocompleteControllerDone() {
    BrowserWindow* window = BrowserWindow::FromBrowser(browser());
    ASSERT_TRUE(window);
    LocationBar* location_bar = window->GetLocationBar();
    ASSERT_TRUE(location_bar);

    AutocompleteController* controller =
        location_bar->GetOmniboxController()->autocomplete_controller();
    ASSERT_TRUE(controller);

    if (controller->done()) {
      return;
    }

    ui_test_utils::WaitForAutocompleteDone(browser());
    ASSERT_TRUE(controller->done());
  }

  void SetupSearchEngine() {
    Profile* profile = browser()->GetProfile();
    TemplateURLService* model =
        TemplateURLServiceFactory::GetForProfile(profile);
    ASSERT_TRUE(model);

    search_test_utils::WaitForTemplateURLServiceToLoad(model);

    ASSERT_TRUE(model->loaded());

    TemplateURLData data;
    data.SetShortName(kSearchShortName);
    data.SetKeyword(kSearchKeyword);
    data.SetURL(kSearchURL);
    TemplateURL* template_url = model->Add(std::make_unique<TemplateURL>(data));
    model->SetUserSelectedDefaultSearchProvider(template_url);

    data.SetKeyword(kSearchKeyword2);
    model->Add(std::make_unique<TemplateURL>(data));

    // Remove built-in template urls, like google.com, bing.com etc., as they
    // may appear as autocomplete suggests and interfere with our tests.
    TemplateURLService::TemplateURLVector urls = model->GetTemplateURLs();
    for (const auto& url : urls) {
      if (url->prepopulate_id() != 0) {
        model->Remove(url);
      }
    }
  }

  void AddHistoryEntry(const TestHistoryEntry& entry, const base::Time& time) {
    Profile* profile = browser()->GetProfile();
    history::HistoryService* history_service =
        HistoryServiceFactory::GetForProfile(
            profile, ServiceAccessType::EXPLICIT_ACCESS);
    ASSERT_TRUE(history_service);

    if (!history_service->BackendLoaded()) {
      // Running the task scheduler until idle loads the history backend.
      content::RunAllTasksUntilIdle();
      ASSERT_TRUE(history_service->BackendLoaded());
    }

    BookmarkModel* bookmark_model =
        BookmarkModelFactory::GetForBrowserContext(profile);
    ASSERT_TRUE(bookmark_model);
    bookmarks::test::WaitForBookmarkModelToLoad(bookmark_model);

    GURL url(entry.url);
    // Add everything in order of time. We don't want to have a time that
    // is "right now" or it will nondeterministically appear in the results.
    history_service->AddPageWithDetails(url, base::UTF8ToUTF16(entry.title),
                                        entry.visit_count, entry.typed_count,
                                        time, false, history::SOURCE_BROWSED);
    if (entry.starred) {
      bookmarks::AddIfNotBookmarked(bookmark_model, url, std::u16string());
    }

    // Running the task scheduler until idle finishes AddPageWithDetails.
    content::RunAllTasksUntilIdle();
  }

  void SetupHistory() {
    // Add enough history pages containing |kSearchText| to trigger
    // open history page url in autocomplete result.
    for (size_t i = 0; i < std::size(kHistoryEntries); i++) {
      // Add everything in order of time. We don't want to have a time that
      // is "right now" or it will nondeterministically appear in the results.
      base::Time t = base::Time::Now() - base::Hours(i + 1);
      ASSERT_NO_FATAL_FAILURE(AddHistoryEntry(kHistoryEntries[i], t));
    }
  }

  void SetupHostResolver() {
    for (auto* kBlockedHostname : kBlockedHostnames) {
      host_resolver()->AddSimulatedFailure(kBlockedHostname);
    }
  }

  void SetupComponents() {
    ASSERT_NO_FATAL_FAILURE(SetupHostResolver());
    ASSERT_NO_FATAL_FAILURE(SetupSearchEngine());
    ASSERT_NO_FATAL_FAILURE(SetupHistory());
  }

  void SetTestToolbarPermanentText(const std::u16string& text) {
    BrowserWindow* window = BrowserWindow::FromBrowser(browser());
    ASSERT_TRUE(window);
    LocationBar* location_bar = window->GetLocationBar();
    ASSERT_TRUE(location_bar);
    OmniboxEditModel* edit_model =
        location_bar->GetOmniboxController()->edit_model();
    ASSERT_NE(nullptr, edit_model);

    OmniboxView* omnibox_view = location_bar->GetOmniboxView();
    ASSERT_TRUE(omnibox_view);

    if (!test_location_bar_model_) {
      test_location_bar_model_ = new TestLocationBarModel;
      std::unique_ptr<LocationBarModel> location_bar_model(
          test_location_bar_model_);
      browser()->GetFeatures().swap_location_bar_models(&location_bar_model);
    }

    test_location_bar_model_->set_formatted_full_url(text);

    // Normally the URL for display has portions elided. We aren't doing that in
    // this case, because that is irrevelant for these tests.
    test_location_bar_model_->set_url_for_display(text);

    omnibox_view->Update();
  }

  void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
    IdentityTestEnvironmentProfileAdaptor::
        SetIdentityTestEnvironmentFactoriesOnBrowserContext(context);
    contextual_tasks::ContextualTasksServiceFactory::GetInstance()
        ->SetTestingFactory(
            context,
            base::BindRepeating(
                [](OmniboxViewTest* self,
                   content::BrowserContext*) -> std::unique_ptr<KeyedService> {
                  auto mock = std::make_unique<testing::NiceMock<
                      contextual_tasks::MockContextualTasksService>>();
                  self->mock_contextual_tasks_service_ = mock.get();
                  return mock;
                },
                base::Unretained(this)));
  }

  policy::MockConfigurationPolicyProvider* policy_provider() {
    return &policy_provider_;
  }

  signin::IdentityTestEnvironment* identity_test_env() {
    return identity_test_env_adaptor_->identity_test_env();
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
  testing::NiceMock<policy::MockConfigurationPolicyProvider> policy_provider_;
  base::CallbackListSubscription create_services_subscription_;
  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
      identity_test_env_adaptor_;

  // Non-owning pointer.
  raw_ptr<TestLocationBarModel> test_location_bar_model_ = nullptr;
};

// Test if ctrl-* accelerators are workable in omnibox.
// Flaky. See https://crbug.com/41337043.
IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_BrowserAccelerators) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  int tab_count = browser()->GetTabStripModel()->count();

  // Create a new Tab.
  chrome::NewTab(browser(), NewTabTypes::kNoUserAction);
  ASSERT_NO_FATAL_FAILURE(WaitForTabOpenOrClose(tab_count + 1));

  // Select the first Tab.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_1, kCtrlOrCmdMask));
  ASSERT_EQ(0, browser()->GetTabStripModel()->active_index());

  chrome::FocusLocationBar(browser());

  // Select the second Tab.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_2, kCtrlOrCmdMask));
  ASSERT_EQ(1, browser()->GetTabStripModel()->active_index());

  chrome::FocusLocationBar(browser());

  // Try ctrl-w to close a Tab.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_W, kCtrlOrCmdMask));
  ASSERT_NO_FATAL_FAILURE(WaitForTabOpenOrClose(tab_count));

  // Try ctrl-l to focus location bar.
  omnibox_view->SetUserText(u"Hello world");
  EXPECT_FALSE(omnibox_view->IsSelectAll());
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_L, kCtrlOrCmdMask));
  EXPECT_TRUE(omnibox_view->IsSelectAll());

  // Try editing the location bar text.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_RIGHT, 0));
  EXPECT_FALSE(omnibox_view->IsSelectAll());
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_S, 0));
  EXPECT_EQ(u"Hello worlds", omnibox_view->GetText());

  // Try ctrl-x to cut text.
#if BUILDFLAG(IS_MAC)
  // Mac uses alt-left/right to select a word.
  ASSERT_NO_FATAL_FAILURE(
      SendKey(ui::VKEY_LEFT, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN));
#else
  ASSERT_NO_FATAL_FAILURE(
      SendKey(ui::VKEY_LEFT, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN));
#endif
  EXPECT_FALSE(omnibox_view->IsSelectAll());
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_X, kCtrlOrCmdMask));
  EXPECT_EQ(u"Hello ", omnibox_view->GetText());

#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_MAC)
  // Try alt-f4 to close the browser.
  ExpectBrowserClosed(browser(), ui::VKEY_F4, ui::EF_ALT_DOWN);
#endif
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, PopupAccelerators) {
  // Create a popup.
  BrowserWindowInterface* popup =
      CreateBrowserForPopup(browser()->GetProfile());
  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(popup));
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxViewForBrowser(popup, &omnibox_view));
  chrome::FocusLocationBar(popup);
  EXPECT_TRUE(omnibox_view->IsSelectAll());

  // Try ctrl/cmd-w to close the popup.
  ExpectBrowserClosed(popup, ui::VKEY_W, kCtrlOrCmdMask);

  // Create another popup.
  popup = CreateBrowserForPopup(browser()->GetProfile());
  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(popup));
  ASSERT_NO_FATAL_FAILURE(GetOmniboxViewForBrowser(popup, &omnibox_view));

  // Set the edit text to "Hello world".
  omnibox_view->SetUserText(u"Hello world");
  chrome::FocusLocationBar(popup);
  EXPECT_TRUE(omnibox_view->IsSelectAll());

  // Try editing the location bar text -- should be disallowed.
  ASSERT_NO_FATAL_FAILURE(SendKeyForBrowser(popup, ui::VKEY_S, 0));
  EXPECT_EQ(u"Hello world", omnibox_view->GetText());
  EXPECT_TRUE(omnibox_view->IsSelectAll());

  ASSERT_NO_FATAL_FAILURE(SendKeyForBrowser(popup, ui::VKEY_X, kCtrlOrCmdMask));
  EXPECT_EQ(u"Hello world", omnibox_view->GetText());
  EXPECT_TRUE(omnibox_view->IsSelectAll());

#if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_MAC)
  // Try alt-f4 to close the popup.
  ExpectBrowserClosed(popup, ui::VKEY_F4, ui::EF_ALT_DOWN);
#endif
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, BackspaceInKeywordMode) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Trigger keyword hint mode.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchKeywordKeys));
  ASSERT_TRUE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(kSearchKeyword, GetOmniboxEditModel()->keyword());

  // Trigger keyword mode.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_TAB, 0));
  ASSERT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(kSearchKeyword, GetOmniboxEditModel()->keyword());

  // Backspace without search text should bring back keyword hint mode.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
  ASSERT_TRUE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(kSearchKeyword, GetOmniboxEditModel()->keyword());

  // Trigger keyword mode again.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_TAB, 0));
  ASSERT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(kSearchKeyword, GetOmniboxEditModel()->keyword());

  // Input something as search text.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));

  // Should stay in keyword mode while deleting search text by pressing
  // backspace.
  for (size_t i = 0; i < std::size(kSearchText) - 1; ++i) {
    ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
    ASSERT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
    ASSERT_EQ(kSearchKeyword, GetOmniboxEditModel()->keyword());
  }

  // Input something as search text.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));

  // Move cursor to the beginning of the search text.
#if BUILDFLAG(IS_MAC)
  // Home doesn't work on Mac trybot.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_A, ui::EF_CONTROL_DOWN));
#else
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_HOME, 0));
#endif
  // Backspace at the beginning of the search text shall turn off
  // the keyword mode.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
  // A keyword 'hint'/button will persist as long as the entry begins with a
  // keyword.
  ASSERT_TRUE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(kSearchKeyword, GetOmniboxEditModel()->keyword());
}

// TODO(crbug.com/40661918): This test flakily times out.
IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_DesiredTLD) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  // Test ctrl-Enter.
  const ui::KeyboardCode kKeys[] = {ui::VKEY_B, ui::VKEY_A, ui::VKEY_R,
                                    ui::VKEY_UNKNOWN};
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  // ctrl-Enter triggers desired_tld feature, thus www.bar.com shall be
  // opened.
  ASSERT_NO_FATAL_FAILURE(
      NavigateExpectUrl(GURL("http://www.bar.com/"), ui::EF_CONTROL_DOWN));
}

// TODO(crbug.com/40661918): Test times out on Win and Linux.
IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_DesiredTLDWithTemporaryText) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  Profile* profile = browser()->GetProfile();
  TemplateURLService* template_url_service =
      TemplateURLServiceFactory::GetForProfile(profile);

  // Add a non-substituting keyword. This ensures the popup will have a
  // non-verbatim entry with "ab" as a prefix. This way, by arrowing down, we
  // can set "abc" as temporary text in the omnibox.
  TemplateURLData data;
  data.SetShortName(u"abc");
  data.SetKeyword(kSearchText);
  data.SetURL("http://abc.com/");
  template_url_service->Add(std::make_unique<TemplateURL>(data));

  // Send "ab", so that an "abc" entry appears in the popup.
  const ui::KeyboardCode kSearchTextPrefixKeys[] = {ui::VKEY_A, ui::VKEY_B,
                                                    ui::VKEY_UNKNOWN};
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextPrefixKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  // Arrow down to the "abc" entry in the popup.
  size_t size =
      GetOmniboxController()->autocomplete_controller()->result().size();
  while (GetOmniboxEditModel()->GetPopupSelection().line < size - 1) {
    ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_DOWN, 0));
    if (omnibox_view->GetText() == u"abc") {
      break;
    }
  }
  ASSERT_EQ(u"abc", omnibox_view->GetText());

  // Hitting ctrl-enter should navigate based on the current text rather than
  // the original input, i.e. to www.abc.com instead of www.ab.com.
  ASSERT_NO_FATAL_FAILURE(
      NavigateExpectUrl(GURL("http://www.abc.com/"), ui::EF_CONTROL_DOWN));
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, ClearUserTextAfterBackgroundCommit) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Navigate in first tab and enter text into the omnibox.
  GURL url1("data:text/html,page1");
  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url1));
  omnibox_view->SetUserText(u"foo");
  content::WebContents* contents =
      browser()->GetTabStripModel()->GetActiveWebContents();

  // Create another tab in the foreground.
  ASSERT_TRUE(AddTabAtIndex(1, url1, ui::PAGE_TRANSITION_TYPED));
  EXPECT_EQ(2, browser()->GetTabStripModel()->count());
  EXPECT_EQ(1, browser()->GetTabStripModel()->active_index());

  // Navigate in the first tab, currently in the background.
  GURL url2("data:text/html,page2");
  NavigateParams params(browser(), url2, ui::PAGE_TRANSITION_LINK);
  params.source_contents = contents;
  params.disposition = WindowOpenDisposition::CURRENT_TAB;
  ui_test_utils::NavigateToURL(&params);

  // Switch back to the first tab.  The user text should be cleared, and the
  // omnibox should have the new URL.
  browser()->GetTabStripModel()->ActivateTabAt(
      0, TabStripUserGestureDetails(
             TabStripUserGestureDetails::GestureType::kOther));
  EXPECT_EQ(ASCIIToUTF16(url2.spec()), omnibox_view->GetText());
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, AltEnter) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  omnibox_view->SetUserText(chrome::kChromeUIHistoryURL16);
  int tab_count = browser()->GetTabStripModel()->count();
  // alt-Enter opens a new tab.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_RETURN, ui::EF_ALT_DOWN));
  ASSERT_NO_FATAL_FAILURE(WaitForTabOpenOrClose(tab_count + 1));
}

// TODO(crbug.com/40661918): This test flakily times out.
IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_EnterToSearch) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  // Test Enter to search.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());
  ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
            GetOmniboxController()
                ->autocomplete_controller()
                ->result()
                .default_match()
                ->type);
  ASSERT_NO_FATAL_FAILURE(NavigateExpectUrl(GURL(kSearchTextURL)));

  // Test that entering a single character then Enter performs a search.
  chrome::FocusLocationBar(browser());
  EXPECT_TRUE(omnibox_view->IsSelectAll());
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_Z, 0));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());
  ASSERT_EQ(AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED,
            GetOmniboxController()
                ->autocomplete_controller()
                ->result()
                .default_match()
                ->type);
  ASSERT_NO_FATAL_FAILURE(
      NavigateExpectUrl(GURL("http://www.foo.com/search?q=z")));
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, EscapeToDefaultMatch) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Input something to trigger inline autocomplete.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kInlineAutocompleteTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  std::u16string old_text = omnibox_view->GetText();

  // Make sure inline autocomplete is triggered.
  EXPECT_GT(old_text.length(), std::size(kInlineAutocompleteText) - 1);

  size_t old_selected_line = GetOmniboxEditModel()->GetPopupSelection().line;
  EXPECT_EQ(0U, old_selected_line);

  // Move to another line with different text.
  size_t size =
      GetOmniboxController()->autocomplete_controller()->result().size();
  while (GetOmniboxEditModel()->GetPopupSelection().line < size - 1) {
    ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_DOWN, 0));
    ASSERT_NE(old_selected_line,
              GetOmniboxEditModel()->GetPopupSelection().line);
    if (old_text != omnibox_view->GetText()) {
      break;
    }
  }

  EXPECT_NE(old_text, omnibox_view->GetText());

  // Escape shall revert back to the default match item.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_ESCAPE, 0));
  EXPECT_EQ(old_text, omnibox_view->GetText());
  EXPECT_EQ(old_selected_line, GetOmniboxEditModel()->GetPopupSelection().line);
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
                       RevertDefaultRevertInlineTextWhenSelectingDefaultMatch) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Input something to trigger inline autocomplete.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kInlineAutocompleteTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  std::u16string old_text = omnibox_view->GetText();

  // Make sure inline autocomplete is triggered.
  EXPECT_GT(old_text.length(), std::size(kInlineAutocompleteText) - 1);

  size_t old_selected_line = GetOmniboxEditModel()->GetPopupSelection().line;
  EXPECT_EQ(0U, old_selected_line);

  // Move to another line with different text.
  size_t size =
      GetOmniboxController()->autocomplete_controller()->result().size();
  while (GetOmniboxEditModel()->GetPopupSelection().line < size - 1) {
    ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_DOWN, 0));
    ASSERT_NE(old_selected_line,
              GetOmniboxEditModel()->GetPopupSelection().line);
    if (old_text != omnibox_view->GetText()) {
      break;
    }
  }

  EXPECT_NE(old_text, omnibox_view->GetText());

  // Move back to the first line
  while (GetOmniboxEditModel()->GetPopupSelection().line > 0) {
    ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_UP, 0));
  }

  EXPECT_EQ(old_text, omnibox_view->GetText());
  EXPECT_EQ(old_selected_line, GetOmniboxEditModel()->GetPopupSelection().line);
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, BasicTextOperations) {
  ASSERT_TRUE(
      ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
  chrome::FocusLocationBar(browser());

  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  std::u16string old_text = omnibox_view->GetText();
  EXPECT_EQ(url::kAboutBlankURL16, old_text);
  EXPECT_TRUE(omnibox_view->IsSelectAll());

  gfx::Range selection = omnibox_view->GetSelectionBounds();
#if defined(TOOLKIT_VIEWS)
  // Views textfields select-all in reverse to show the leading text.
  selection = {selection.end(), selection.start()};
#endif
  EXPECT_EQ(0U, selection.start());
  EXPECT_EQ(old_text.size(), selection.end());

  // Move the cursor to the end.
#if BUILDFLAG(IS_MAC)
  // End doesn't work on Mac trybot.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_E, ui::EF_CONTROL_DOWN));
#else
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_END, 0));
#endif
  EXPECT_FALSE(omnibox_view->IsSelectAll());

  // Make sure the cursor is placed correctly.
  selection = omnibox_view->GetSelectionBounds();
  EXPECT_EQ(old_text.size(), selection.start());
  EXPECT_EQ(old_text.size(), selection.end());

  // Insert one character at the end.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_A, 0));
  EXPECT_EQ(old_text + u'a', omnibox_view->GetText());

  // Delete one character from the end.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
  EXPECT_EQ(old_text, omnibox_view->GetText());

  omnibox_view->SelectAll(true);
  EXPECT_TRUE(omnibox_view->IsSelectAll());
  selection = omnibox_view->GetSelectionBounds();
#if defined(TOOLKIT_VIEWS)
  // Views textfields select-all in reverse to show the leading text.
  selection = {selection.end(), selection.start()};
#endif
  EXPECT_EQ(0U, selection.start());
  EXPECT_EQ(old_text.size(), selection.end());

  // Delete the content
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_DELETE, 0));
  EXPECT_FALSE(omnibox_view->IsSelectAll());
  selection = omnibox_view->GetSelectionBounds();
  EXPECT_EQ(0U, selection.start());
  EXPECT_EQ(0U, selection.end());
  EXPECT_TRUE(omnibox_view->GetText().empty());

  // Add a small amount of text to move the cursor past offset 0.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_A, 0));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_B, 0));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_C, 0));

  // Check if RevertAll() resets the text and preserves the cursor position.
  omnibox_view->RevertAll();
  EXPECT_FALSE(omnibox_view->IsSelectAll());
  EXPECT_EQ(old_text, omnibox_view->GetText());
  selection = omnibox_view->GetSelectionBounds();
  EXPECT_EQ(3U, selection.start());
  EXPECT_EQ(3U, selection.end());

  // Check that reverting clamps the cursor to the bounds of the new text.
  // Move the cursor to the end.
#if BUILDFLAG(IS_MAC)
  // End doesn't work on Mac trybot.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_E, ui::EF_CONTROL_DOWN));
#else
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_END, 0));
#endif
  // Add a small amount of text to push the cursor past where the text end
  // will be once we revert.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_A, 0));
  omnibox_view->RevertAll();
  // Cursor should be no further than original text.
  selection = omnibox_view->GetSelectionBounds();
  EXPECT_EQ(11U, selection.start());
  EXPECT_EQ(11U, selection.end());
}

// Make sure the cursor position doesn't get set past the last character of
// user input text when the URL is longer than the keyword.
// (http://crbug.com/40489150)
IN_PROC_BROWSER_TEST_F(OmniboxViewTest, FocusSearchLongUrl) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  ASSERT_GT(strlen(url::kAboutBlankURL),
            std::char_traits<char16_t>::length(kSearchKeyword));
  ASSERT_TRUE(
      ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));

  // Make sure nothing DCHECKs.
  chrome::FocusSearch(browser());
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, AcceptKeywordByTypingQuestionMark) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  std::u16string search_keyword(kSearchKeyword);

  // If the user gets into keyword mode by typing '?', they should be put into
  // keyword mode for their default search provider.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_OEM_2, ui::EF_SHIFT_DOWN));
  ASSERT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_TRUE(GetOmniboxEditModel()->is_keyword_selected());
  ASSERT_EQ(search_keyword, GetOmniboxEditModel()->keyword());
  ASSERT_EQ(std::u16string(), omnibox_view->GetText());

  // If the user press backspace, they should be left with '?' in the omnibox.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
  EXPECT_EQ(u"?", omnibox_view->GetText());
  EXPECT_EQ(std::u16string(), GetOmniboxEditModel()->keyword());
  EXPECT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  EXPECT_FALSE(GetOmniboxEditModel()->is_keyword_selected());
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, SearchDisabledDontCrashOnQuestionMark) {
  policy::PolicyMap policies;
  policies.Set("DefaultSearchProviderEnabled", policy::POLICY_LEVEL_MANDATORY,
               policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_PLATFORM,
               base::Value(false), nullptr);
  policy_provider()->UpdateChromePolicy(policies);

  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // '?' isn't in the same place on all keyboard layouts, so send the character
  // instead of keystrokes.
  ASSERT_NO_FATAL_FAILURE({
    omnibox_view->OnBeforePossibleChange();
    omnibox_view->SetUserText(u"?");
    omnibox_view->OnAfterPossibleChange(true);
  });
  ASSERT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_FALSE(GetOmniboxEditModel()->is_keyword_selected());
  ASSERT_EQ(u"?", omnibox_view->GetText());
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, NonDefaultSubstitutingKeywordTest) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  Profile* profile = browser()->GetProfile();
  TemplateURLService* template_url_service =
      TemplateURLServiceFactory::GetForProfile(profile);

  // Add a non-default substituting keyword.
  TemplateURLData data;
  data.SetShortName(u"Search abc");
  data.SetKeyword(kSearchText);
  data.SetURL("http://abc.com/{searchTerms}");
  data.is_active = TemplateURLData::ActiveStatus::kTrue;
  template_url_service->Add(std::make_unique<TemplateURL>(data));

  omnibox_view->SetUserText(std::u16string());

  // Non-default substituting keyword shouldn't be matched by default.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  // Check if the default match result is Search Primary Provider.
  auto* default_match = GetOmniboxController()
                            ->autocomplete_controller()
                            ->result()
                            .default_match();
  EXPECT_EQ(default_match->type, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED);
  EXPECT_EQ(default_match->destination_url.spec(), kSearchTextURL);

  omnibox_view->SetUserText(std::u16string());
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_FALSE(GetOmniboxController()->IsPopupOpen());
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, NonSubstitutingKeywordTest) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  Profile* profile = browser()->GetProfile();
  TemplateURLService* template_url_service =
      TemplateURLServiceFactory::GetForProfile(profile);

  // Add a non-substituting keyword.
  TemplateURLData data;
  data.SetShortName(u"abc");
  data.SetKeyword(kSearchText);
  data.SetURL("http://abc.com/");
  data.is_active = TemplateURLData::ActiveStatus::kTrue;
  template_url_service->Add(std::make_unique<TemplateURL>(data));

  omnibox_view->SetUserText(std::u16string());

  // We always allow exact matches for non-substituting keywords.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  auto* default_match = GetOmniboxController()
                            ->autocomplete_controller()
                            ->result()
                            .default_match();
  EXPECT_EQ(default_match->type, AutocompleteMatchType::HISTORY_KEYWORD);
  EXPECT_EQ(default_match->destination_url.spec(), "http://abc.com/");

  omnibox_view->SetUserText(std::u16string());
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_FALSE(GetOmniboxController()->IsPopupOpen());
}

// Flaky. See https://crbug.com/41337043.
IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_DeleteItem) {
  // Disable the search provider, to make sure the popup contains only history
  // items.
  TemplateURLService* model =
      TemplateURLServiceFactory::GetForProfile(browser()->GetProfile());
  model->SetUserSelectedDefaultSearchProvider(nullptr);

  ASSERT_TRUE(
      ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
  chrome::FocusLocationBar(browser());

  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  std::u16string old_text = omnibox_view->GetText();

  // Input something that can match history items.
  omnibox_view->SetUserText(u"site.com/p");
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  // Delete the inline autocomplete part.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_DELETE, 0));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());
  ASSERT_GE(GetOmniboxController()->autocomplete_controller()->result().size(),
            3U);

  std::u16string user_text = omnibox_view->GetText();
  ASSERT_EQ(u"site.com/p", user_text);
  omnibox_view->SelectAll(true);
  ASSERT_TRUE(omnibox_view->IsSelectAll());

  // Move down.
  size_t default_line = GetOmniboxEditModel()->GetPopupSelection().line;
  GetOmniboxEditModel()->OnUpOrDownPressed(true, false);
  ASSERT_EQ(default_line + 1, GetOmniboxEditModel()->GetPopupSelection().line);
  std::u16string selected_text = GetOmniboxController()
                                     ->autocomplete_controller()
                                     ->result()
                                     .match_at(default_line + 1)
                                     .fill_into_edit;
  // Temporary text is shown.
  ASSERT_EQ(selected_text, omnibox_view->GetText());
  ASSERT_FALSE(omnibox_view->IsSelectAll());

  // Delete the item.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_DELETE, ui::EF_SHIFT_DOWN));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  // The selected line shouldn't be changed, because we have more than two
  // items.
  ASSERT_EQ(default_line + 1, GetOmniboxEditModel()->GetPopupSelection().line);
  // Make sure the item is really deleted.
  ASSERT_NE(selected_text, GetOmniboxController()
                               ->autocomplete_controller()
                               ->result()
                               .match_at(default_line + 1)
                               .fill_into_edit);
  selected_text = GetOmniboxController()
                      ->autocomplete_controller()
                      ->result()
                      .match_at(default_line + 1)
                      .fill_into_edit;
  // New temporary text is shown.
  ASSERT_EQ(selected_text, omnibox_view->GetText());

  // Revert to the default match.
  ASSERT_TRUE(GetOmniboxEditModel()->OnEscapeKeyPressed());
  ASSERT_EQ(default_line, GetOmniboxEditModel()->GetPopupSelection().line);
  ASSERT_EQ(user_text, omnibox_view->GetText());
  ASSERT_TRUE(omnibox_view->IsSelectAll());

  // Move down and up to select the default match as temporary text.
  GetOmniboxEditModel()->OnUpOrDownPressed(true, false);
  ASSERT_EQ(default_line + 1, GetOmniboxEditModel()->GetPopupSelection().line);
  GetOmniboxEditModel()->OnUpOrDownPressed(false, false);
  ASSERT_EQ(default_line, GetOmniboxEditModel()->GetPopupSelection().line);

  selected_text = GetOmniboxController()
                      ->autocomplete_controller()
                      ->result()
                      .match_at(default_line)
                      .fill_into_edit;
  // New temporary text is shown.
  ASSERT_EQ(selected_text, omnibox_view->GetText());
  ASSERT_FALSE(omnibox_view->IsSelectAll());

  // As the current selected item is the new default item, pressing Escape key
  // should revert all directly.
  ASSERT_TRUE(GetOmniboxEditModel()->OnEscapeKeyPressed());
  ASSERT_EQ(old_text, omnibox_view->GetText());
  ASSERT_TRUE(omnibox_view->IsSelectAll());
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, TabAcceptKeyword) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  std::u16string text = kSearchKeyword;

  // Trigger keyword hint mode.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchKeywordKeys));
  ASSERT_TRUE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(text, GetOmniboxEditModel()->keyword());
  ASSERT_EQ(text, omnibox_view->GetText());

  // Trigger keyword mode by tab.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_TAB, 0));
  ASSERT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(text, GetOmniboxEditModel()->keyword());
  ASSERT_TRUE(omnibox_view->GetText().empty());

  // Revert to keyword hint mode.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
  ASSERT_TRUE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(text, GetOmniboxEditModel()->keyword());
  ASSERT_EQ(text, omnibox_view->GetText());

  // The location bar should still have focus.
  ASSERT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));

  // Trigger keyword mode by tab.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_TAB, 0));
  ASSERT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(text, GetOmniboxEditModel()->keyword());
  ASSERT_TRUE(omnibox_view->GetText().empty());

  // Revert to keyword hint mode with SHIFT+TAB.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_TAB, ui::EF_SHIFT_DOWN));
  ASSERT_TRUE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(text, GetOmniboxEditModel()->keyword());
  ASSERT_EQ(text, omnibox_view->GetText());
  ASSERT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, PersistKeywordModeOnTabSwitch) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Trigger keyword hint mode.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchKeywordKeys));
  ASSERT_TRUE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(kSearchKeyword, GetOmniboxEditModel()->keyword());

  // Trigger keyword mode.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_TAB, 0));
  ASSERT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  ASSERT_EQ(kSearchKeyword, GetOmniboxEditModel()->keyword());

  // Create a new tab.
  chrome::NewTab(browser(), NewTabTypes::kNoUserAction);

  // Switch back to the first tab.
  browser()->GetTabStripModel()->ActivateTabAt(
      0, TabStripUserGestureDetails(
             TabStripUserGestureDetails::GestureType::kOther));

  // Make sure we're still in keyword mode.
  ASSERT_TRUE(GetOmniboxEditModel()->is_keyword_selected());
  ASSERT_EQ(kSearchKeyword, GetOmniboxEditModel()->keyword());
  ASSERT_EQ(omnibox_view->GetText(), std::u16string());

  // Input something as search text.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));

  // Switch to the second tab and back to the first.
  browser()->GetTabStripModel()->ActivateTabAt(
      1, TabStripUserGestureDetails(
             TabStripUserGestureDetails::GestureType::kOther));
  browser()->GetTabStripModel()->ActivateTabAt(
      0, TabStripUserGestureDetails(
             TabStripUserGestureDetails::GestureType::kOther));

  // Make sure we're still in keyword mode.
  ASSERT_TRUE(GetOmniboxEditModel()->is_keyword_selected());
  ASSERT_EQ(kSearchKeyword, GetOmniboxEditModel()->keyword());
  ASSERT_EQ(omnibox_view->GetText(), kSearchText);
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
                       CtrlKeyPressedWithInlineAutocompleteTest) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Input something to trigger inline autocomplete.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kInlineAutocompleteTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  std::u16string old_text = omnibox_view->GetText();

  // Make sure inline autocomplete is triggered.
  EXPECT_GT(old_text.length(), std::size(kInlineAutocompleteText) - 1);

  // Press ctrl key.
  GetOmniboxEditModel()->OnControlKeyChanged(true);

  // Inline autocomplete should still be there.
  EXPECT_EQ(old_text, omnibox_view->GetText());
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, UndoRedo) {
  ASSERT_TRUE(
      ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
  chrome::FocusLocationBar(browser());

  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  std::u16string old_text = omnibox_view->GetText();
  EXPECT_EQ(url::kAboutBlankURL16, old_text);
  EXPECT_TRUE(omnibox_view->IsSelectAll());

  // Delete the text, then undo.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
  EXPECT_TRUE(omnibox_view->GetText().empty());
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_Z, kCtrlOrCmdMask));
  EXPECT_EQ(old_text, omnibox_view->GetText());

  // Redo should delete the text again.
  ASSERT_NO_FATAL_FAILURE(
      SendKey(ui::VKEY_Z, kCtrlOrCmdMask | ui::EF_SHIFT_DOWN));
  EXPECT_TRUE(omnibox_view->GetText().empty());

  // Perform an undo.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_Z, kCtrlOrCmdMask));
  EXPECT_TRUE(omnibox_view->IsSelectAll());

  // The text should be selected.
  gfx::Range selection = omnibox_view->GetSelectionBounds();
  EXPECT_EQ(old_text.size(), selection.start());
  EXPECT_EQ(0U, selection.end());

  // Delete three characters; "about:bl" should not trigger inline autocomplete.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_END, 0));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
  EXPECT_EQ(old_text.substr(0, old_text.size() - 3), omnibox_view->GetText());

  // Undo delete.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_Z, kCtrlOrCmdMask));
  EXPECT_EQ(old_text, omnibox_view->GetText());

  // Redo delete.
  ASSERT_NO_FATAL_FAILURE(
      SendKey(ui::VKEY_Z, kCtrlOrCmdMask | ui::EF_SHIFT_DOWN));
  EXPECT_EQ(old_text.substr(0, old_text.size() - 3), omnibox_view->GetText());

  // Delete everything.
  omnibox_view->SelectAll(true);
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
  EXPECT_TRUE(omnibox_view->GetText().empty());

  // Undo delete everything.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_Z, kCtrlOrCmdMask));
  EXPECT_EQ(old_text.substr(0, old_text.size() - 3), omnibox_view->GetText());

  // Undo delete two characters.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_Z, kCtrlOrCmdMask));
  EXPECT_EQ(old_text, omnibox_view->GetText());
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, BackspaceDeleteHalfWidthKatakana) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
  // Insert text: ﾀﾞ. This is two, 3-byte UTF-8 characters:
  // U+FF80 "HALFWIDTH KATAKANA LETTER TA" and
  // U+FF9E "HALFWIDTH KATAKANA VOICED SOUND MARK".
  omnibox_view->SetUserText(u"\uFF80\uFF9E");
  EXPECT_FALSE(omnibox_view->GetText().empty());

  // Move the cursor to the end.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_END, 0));

  // Backspace should delete the character. In http://crbug.com/40975847, the
  // bug was that nothing was deleted.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_BACK, 0));
#if BUILDFLAG(IS_MAC)
  // Cocoa text fields attach the sound mark and delete the whole thing. This
  // behavior should remain on Mac even when using a toolkit-views browser
  // window.
  EXPECT_TRUE(omnibox_view->GetText().empty());
#else
  // Toolkit-views text fields delete just the sound mark.
  EXPECT_EQ(u"\uFF80", omnibox_view->GetText());
#endif
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DoesNotUpdateAutocompleteOnBlur) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Input something to trigger inline autocomplete.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kInlineAutocompleteTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());
  gfx::Range selection = omnibox_view->GetSelectionBounds();
  EXPECT_FALSE(selection.is_empty());
  std::u16string old_autocomplete_text =
      GetOmniboxController()->autocomplete_controller()->input_.text();

  // Unfocus the omnibox. This should close the popup but should not run
  // autocomplete.
  ui_test_utils::ClickOnView(browser(), VIEW_ID_TAB_CONTAINER);
  ASSERT_FALSE(GetOmniboxController()->IsPopupOpen());
  EXPECT_EQ(old_autocomplete_text,
            GetOmniboxController()->autocomplete_controller()->input_.text());
}

namespace {

class TestOmniboxNavigationObserver : public content::WebContentsObserver {
 public:
  explicit TestOmniboxNavigationObserver(content::WebContents* web_contents)
      : content::WebContentsObserver(web_contents) {}

  void DidFinishNavigation(
      content::NavigationHandle* navigation_handle) override {
    if (navigation_handle->HasCommitted()) {
      auto* user_data =
          page_load_metrics::NavigationHandleUserData::GetForNavigationHandle(
              *navigation_handle);
      if (user_data) {
        navigation_type_ =
            GetChromeInitiatorLocation(user_data->navigation_type());
      }
    }
  }

  std::optional<ChromeInitiatorLocation> navigation_type_;
};

}  // namespace

IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
                       Navigate_OmniboxDirectUrlInput_AttachesUserData) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  omnibox_view->SetUserText(u"http://www.example.com/");
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());

  auto* match = GetOmniboxController()
                    ->autocomplete_controller()
                    ->result()
                    .default_match();
  ASSERT_NE(match, nullptr);

  TestOmniboxNavigationObserver omnibox_observer(
      browser()->GetTabStripModel()->GetActiveWebContents());
  content::TestNavigationObserver observer(
      browser()->GetTabStripModel()->GetActiveWebContents(), 1);

  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_RETURN, 0));
  observer.Wait();

  ASSERT_TRUE(omnibox_observer.navigation_type_.has_value());
  EXPECT_EQ(omnibox_observer.navigation_type_.value(),
            ChromeInitiatorLocation::kOmniboxDirectUrlInput);
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
                       Navigate_OmniboxDefaultSearchEngine_AttachesUserData) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  omnibox_view->SetUserText(u"search query");
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());

  auto* match = GetOmniboxController()
                    ->autocomplete_controller()
                    ->result()
                    .default_match();
  ASSERT_NE(match, nullptr);

  TestOmniboxNavigationObserver omnibox_observer(
      browser()->GetTabStripModel()->GetActiveWebContents());
  content::TestNavigationObserver observer(
      browser()->GetTabStripModel()->GetActiveWebContents(), 1);

  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_RETURN, 0));
  observer.Wait();

  ASSERT_TRUE(omnibox_observer.navigation_type_.has_value());
  EXPECT_EQ(omnibox_observer.navigation_type_.value(),
            ChromeInitiatorLocation::kOmniboxDefaultSearchEngine);
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, Paste) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
  EXPECT_FALSE(GetOmniboxController()->IsPopupOpen());

  // Paste should yield the expected text and open the popup.
  SetClipboardText(kSearchText);
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_V, kCtrlOrCmdMask));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  EXPECT_TRUE(base::test::RunUntil(
      [&]() { return omnibox_view->GetText() == kSearchText; }));
  EXPECT_TRUE(GetOmniboxController()->IsPopupOpen());

  // Close the popup and select all.
  GetOmniboxPopupCloser()->CloseWithReason(omnibox::PopupCloseReason::kOther);
  omnibox_view->SelectAll(false);
  EXPECT_FALSE(GetOmniboxController()->IsPopupOpen());

  // Pasting the same text again over itself should re-open the popup.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_V, kCtrlOrCmdMask));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  EXPECT_EQ(kSearchText, omnibox_view->GetText());
  EXPECT_TRUE(base::test::RunUntil(
      [&]() { return GetOmniboxController()->IsPopupOpen(); }));
  GetOmniboxPopupCloser()->CloseWithReason(omnibox::PopupCloseReason::kOther);
  EXPECT_FALSE(GetOmniboxController()->IsPopupOpen());

  // Pasting amid text should yield the expected text and re-open the popup.
  omnibox_view->SetWindowTextAndCaretPos(u"abcd", 2, false, false);
  SetClipboardText(u"123");
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_V, kCtrlOrCmdMask));
  EXPECT_TRUE(base::test::RunUntil(
      [&]() { return omnibox_view->GetText() == u"ab123cd"; }));
  EXPECT_TRUE(GetOmniboxController()->IsPopupOpen());

  // Ctrl/Cmd+Alt+V should not paste.
  ASSERT_NO_FATAL_FAILURE(
      SendKey(ui::VKEY_V, kCtrlOrCmdMask | ui::EF_ALT_DOWN));
  EXPECT_EQ(u"ab123cd", omnibox_view->GetText());
  // TODO(msw): Test that AltGr+V does not paste.
}

IN_PROC_BROWSER_TEST_F(OmniboxViewTest, EditSearchEngines) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
  EXPECT_TRUE(chrome::ExecuteCommand(browser(), IDC_EDIT_SEARCH_ENGINES));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  const std::string target_url =
      std::string(chrome::kChromeUISettingsURL) +
      (base::FeatureList::IsEnabled(switches::kSearchSettingsUpdate)
           ? chrome::kSearchSubPage
           : chrome::kSearchEnginesSubPage);
  EXPECT_EQ(ASCIIToUTF16(target_url), omnibox_view->GetText());
  EXPECT_FALSE(GetOmniboxController()->IsPopupOpen());
}

// Flaky test. The below suggestions are in a random order, and the injected
// keys may or may not have registered. Probably https://crbug.com/41337043,
// but I believe the whole input mechanism needs to be re-architected.
// What I'd like to see is, after a sequence of keys is injected, we inject
// an artificial input, and, *only* after that input has been registered,
// do we continue.
IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
                       DISABLED_CtrlArrowAfterArrowSuggestions) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  // Input something to trigger results.
  const ui::KeyboardCode kKeys[] = {ui::VKEY_B, ui::VKEY_A, ui::VKEY_R,
                                    ui::VKEY_UNKNOWN};
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_TRUE(GetOmniboxController()->IsPopupOpen());

  ASSERT_EQ(u"bar.com/1", omnibox_view->GetText());

  // Arrow down on a suggestion, and omnibox text should be the suggestion.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_DOWN, 0));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  ASSERT_EQ(u"www.bar.com/2", omnibox_view->GetText());

  // Highlight the last 2 words and the omnibox text should not change.
  // Simulating Ctrl-shift-left only once does not seem to highlight anything
  // on Linux.
#if BUILDFLAG(IS_MAC)
  // Mac uses alt-left/right to select a word.
  const int modifiers = ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN;
#else
  const int modifiers = ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN;
#endif
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, modifiers));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, modifiers));
  ASSERT_EQ(u"www.bar.com/2", omnibox_view->GetText());
}

namespace {

// Returns the number of characters currently selected in |omnibox_view|.
size_t GetSelectionSize(OmniboxView* omnibox_view) {
  return omnibox_view->GetSelectionBounds().length();
}

}  // namespace

// Test that if the Omnibox has focus, and had everything selected before a
// non-user-initiated update, then it retains the selection after the update.
// Flaky. See https://crbug.com/41337043.
IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_SelectAllStaysAfterUpdate) {
  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  std::u16string url_a(u"http://www.a.com/");
  std::u16string url_b(u"http://www.b.com/");
  chrome::FocusLocationBar(browser());

  SetTestToolbarPermanentText(url_a);
  EXPECT_EQ(url_a, omnibox_view->GetText());
  EXPECT_TRUE(omnibox_view->IsSelectAll());

  // Updating while selected should retain SelectAll().
  SetTestToolbarPermanentText(url_b);
  EXPECT_EQ(url_b, omnibox_view->GetText());
  EXPECT_TRUE(omnibox_view->IsSelectAll());

  // Select nothing, then update. Should gain SelectAll().
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_RIGHT, 0));
  SetTestToolbarPermanentText(url_a);
  EXPECT_EQ(url_a, omnibox_view->GetText());
  EXPECT_TRUE(omnibox_view->IsSelectAll());

  // Test behavior of the "reversed" attribute of OmniboxView::SelectAll().
  SetTestToolbarPermanentText(u"AB");
  // Should be at beginning. Shift+left should do nothing.
  EXPECT_EQ(2u, GetSelectionSize(omnibox_view));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, ui::EF_SHIFT_DOWN));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, ui::EF_SHIFT_DOWN));
  EXPECT_EQ(2u, GetSelectionSize(omnibox_view));
  EXPECT_TRUE(omnibox_view->IsSelectAll());

  SetTestToolbarPermanentText(u"CD");
  EXPECT_EQ(2u, GetSelectionSize(omnibox_view));

  // At the start, so Shift+Left should do nothing.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, ui::EF_SHIFT_DOWN));
  EXPECT_EQ(2u, GetSelectionSize(omnibox_view));

  // And Shift+Right should reduce by one character.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_RIGHT, ui::EF_SHIFT_DOWN));
  EXPECT_EQ(1u, GetSelectionSize(omnibox_view));

  // No go to start and select all to the right (not reversed).
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, 0));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, 0));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_RIGHT, ui::EF_SHIFT_DOWN));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_RIGHT, ui::EF_SHIFT_DOWN));
  SetTestToolbarPermanentText(u"AB");
  EXPECT_EQ(2u, GetSelectionSize(omnibox_view));

  // We reverse select all on Update() so shift-left won't do anything.
  // On Cocoa, shift-left will just anchor it on the left.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, ui::EF_SHIFT_DOWN));
  EXPECT_EQ(2u, GetSelectionSize(omnibox_view));

  // And shift-right should reduce by one character.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_RIGHT, ui::EF_SHIFT_DOWN));
  EXPECT_EQ(1u, GetSelectionSize(omnibox_view));
}

class SiteSearchPolicyOmniboxViewTest
    : public OmniboxViewTest,
      public ::testing::WithParamInterface<std::optional<bool>> {
 public:
  SiteSearchPolicyOmniboxViewTest() = default;
  ~SiteSearchPolicyOmniboxViewTest() override = default;

  base::Value CreateSiteSearchPolicyValue(bool featured) {
    base::DictValue policy_dict =
        base::DictValue()
            .Set(policy::SiteSearchPolicyHandler::kShortcut,
                 kSiteSearchPolicyKeyword)
            .Set(policy::SiteSearchPolicyHandler::kName, kSiteSearchPolicyName)
            .Set(policy::SiteSearchPolicyHandler::kUrl, kSiteSearchPolicyURL)
            .Set(policy::SiteSearchPolicyHandler::kFeatured, featured);
    if (is_allow_user_override().has_value()) {
      policy_dict.Set(policy::SiteSearchPolicyHandler::kAllowUserOverride,
                      is_allow_user_override().value());
    }
    base::ListValue policy_value;
    policy_value.Append(std::move(policy_dict));
    return base::Value(std::move(policy_value));
  }

  std::optional<bool> is_allow_user_override() const { return GetParam(); }
};

// Verifies that keyword search works when `SiteSearchSettings` policy is set.
IN_PROC_BROWSER_TEST_P(SiteSearchPolicyOmniboxViewTest,
                       NonFeaturedPolicyKeyword) {
  policy::PolicyMap policies;
  policies.Set(policy::key::kSiteSearchSettings, policy::POLICY_LEVEL_MANDATORY,
               policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
               CreateSiteSearchPolicyValue(/*featured=*/false), nullptr);
  policy_provider()->UpdateChromePolicy(policies);

  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Check that new entries have been added to TemplateURLService.
  const TemplateURL* turl =
      TemplateURLServiceFactory::GetForProfile(browser()->GetProfile())
          ->GetTemplateURLForKeyword(kSiteSearchPolicyKeyword);
  ASSERT_TRUE(turl);
  EXPECT_EQ(turl->policy_origin(), TemplateURLData::PolicyOrigin::kSiteSearch);
  EXPECT_EQ(turl->short_name(), kSiteSearchPolicyName);
  EXPECT_EQ(turl->url(), kSiteSearchPolicyURL);
  EXPECT_FALSE(turl->featured_by_policy());
  EXPECT_EQ(turl->enforced_by_policy(),
            !is_allow_user_override().value_or(false));

  // Trigger keyword hint mode.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSiteSearchPolicyKeywordKeys));
  EXPECT_TRUE(GetOmniboxEditModel()->is_keyword_hint());
  EXPECT_EQ(GetOmniboxEditModel()->keyword(), kSiteSearchPolicyKeyword);

  // Trigger keyword mode.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_TAB, 0));
  EXPECT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  EXPECT_EQ(GetOmniboxEditModel()->keyword(), kSiteSearchPolicyKeyword);

  // Input something as search text and perform a search.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  EXPECT_TRUE(GetOmniboxController()->IsPopupOpen());

  EXPECT_EQ(GetOmniboxController()
                ->autocomplete_controller()
                ->result()
                .default_match()
                ->destination_url.spec(),
            kSiteSearchPolicyTextURL);
}

// Verifies that keyword search works when `SiteSearchSettings` policy defines
// a featured search engine.
IN_PROC_BROWSER_TEST_P(SiteSearchPolicyOmniboxViewTest, FeaturedPolicyKeyword) {
  policy::PolicyMap policies;
  policies.Set(policy::key::kSiteSearchSettings, policy::POLICY_LEVEL_MANDATORY,
               policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
               CreateSiteSearchPolicyValue(/*featured=*/true), nullptr);
  policy_provider()->UpdateChromePolicy(policies);

  OmniboxView* omnibox_view;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Check that new entries have been added to TemplateURLService.
  const TemplateURL* turl =
      TemplateURLServiceFactory::GetForProfile(browser()->GetProfile())
          ->GetTemplateURLForKeyword(kSiteSearchPolicyKeywordWithAtPrefix);
  ASSERT_TRUE(turl);
  EXPECT_EQ(turl->policy_origin(), TemplateURLData::PolicyOrigin::kSiteSearch);
  EXPECT_EQ(turl->short_name(), kSiteSearchPolicyName);
  EXPECT_EQ(turl->url(), kSiteSearchPolicyURL);
  EXPECT_TRUE(turl->featured_by_policy());
  EXPECT_EQ(turl->enforced_by_policy(),
            !is_allow_user_override().value_or(false));

  // Type the keyword.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_2, ui::EF_SHIFT_DOWN));
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSiteSearchPolicyKeywordKeys));
  EXPECT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  EXPECT_EQ(GetOmniboxEditModel()->keyword(), u"");

  // Trigger keyword mode.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_TAB, 0));
  EXPECT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  EXPECT_EQ(GetOmniboxEditModel()->keyword(),
            kSiteSearchPolicyKeywordWithAtPrefix);

  // Input something as search text and perform a search.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));  // ABC
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  EXPECT_TRUE(GetOmniboxController()->IsPopupOpen());

  EXPECT_EQ(GetOmniboxController()
                ->autocomplete_controller()
                ->result()
                .default_match()
                ->destination_url.spec(),
            kSiteSearchPolicyTextURL);  // ...?q=ABC
}

// Verifies that featured search engine is shown with starter pack on "@" state
// and that the underlying search works.
IN_PROC_BROWSER_TEST_P(SiteSearchPolicyOmniboxViewTest,
                       FeaturedPolicyKeywordArrowDown) {
  policy::PolicyMap policies;
  policies.Set(policy::key::kSiteSearchSettings, policy::POLICY_LEVEL_MANDATORY,
               policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
               CreateSiteSearchPolicyValue(/*featured=*/true), nullptr);
  policy_provider()->UpdateChromePolicy(policies);

  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Check that new entries have been added to TemplateURLService.
  const TemplateURL* turl =
      TemplateURLServiceFactory::GetForProfile(browser()->GetProfile())
          ->GetTemplateURLForKeyword(kSiteSearchPolicyKeywordWithAtPrefix);
  ASSERT_TRUE(turl);
  EXPECT_EQ(turl->policy_origin(), TemplateURLData::PolicyOrigin::kSiteSearch);
  EXPECT_EQ(turl->short_name(), kSiteSearchPolicyName);
  EXPECT_EQ(turl->url(), kSiteSearchPolicyURL);
  EXPECT_TRUE(turl->featured_by_policy());
  EXPECT_EQ(turl->enforced_by_policy(),
            !is_allow_user_override().value_or(false));

  // Trigger keyword mode.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_2, ui::EF_SHIFT_DOWN));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_DOWN, /*modifiers=*/0));
  EXPECT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  EXPECT_EQ(GetOmniboxEditModel()->keyword(),
            kSiteSearchPolicyKeywordWithAtPrefix);

  // Input something as search text and perform a search.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  EXPECT_TRUE(GetOmniboxController()->IsPopupOpen());

  EXPECT_EQ(GetOmniboxController()
                ->autocomplete_controller()
                ->result()
                .default_match()
                ->destination_url.spec(),
            kSiteSearchPolicyTextURL);
}

INSTANTIATE_TEST_SUITE_P(All,
                         SiteSearchPolicyOmniboxViewTest,
                         ::testing::Values(std::nullopt, true, false));

class OmniboxViewAiModeTest : public OmniboxViewTest {
 public:
  OmniboxViewAiModeTest() {
    ai_mode_feature_list_.InitAndEnableFeature(
        lens::features::kLensSendUrlsInComposeboxes);
  }

 private:
  base::test::ScopedFeatureList ai_mode_feature_list_;
};

IN_PROC_BROWSER_TEST_F(OmniboxViewAiModeTest,
                       OpenAiModeTriggersContextualization) {
  // Re-add google keyword; otherwise AI flow will hit a `CHECK()`.
  Profile* profile = browser()->GetProfile();
  TemplateURLService* template_url_service =
      TemplateURLServiceFactory::GetForProfile(profile);
  TemplateURLData data;
  data.SetShortName(u"google");
  data.SetKeyword(u"google");
  data.SetURL("http://google.com/search?q={searchTerms}");
  TemplateURL* template_url =
      template_url_service->Add(std::make_unique<TemplateURL>(data));
  template_url_service->SetUserSelectedDefaultSearchProvider(template_url);

  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Set text in the omnibox to be contextualized. Add a URL to trigger real
  // extraction!
  omnibox_view->SetUserText(u"test query http://example.com");

  // Call OpenAiMode, which should trigger QueryContextualizer and navigation.
  GetOmniboxEditModel()->OpenAiMode(
      OmniboxEditModel::AimActivation::kClickOrGesture);

  // Wait for navigation to complete. We expect it to navigate to chess://aim/
  // or similar URL.
  content::TestNavigationObserver observer(
      browser()->GetTabStripModel()->GetActiveWebContents());
  observer.Wait();

  // Verify that the navigation occurred to an 'aim' URL and check session
  // transfer.
  content::WebContents* web_contents =
      browser()->GetTabStripModel()->GetActiveWebContents();
  GURL current_url = web_contents->GetLastCommittedURL();
  EXPECT_TRUE(current_url.spec().find("q=test+query") != std::string::npos);

  // Verify session handle propagation!
  auto* helper =
      ContextualSearchWebContentsHelper::FromWebContents(web_contents);
  ASSERT_TRUE(helper);
  EXPECT_NE(helper->session_handle(), nullptr);
}

class SearchAggregatorPolicyOmniboxViewTest : public OmniboxViewTest {
 public:
  SearchAggregatorPolicyOmniboxViewTest() {
    scoped_feature_list_.InitAndEnableFeature(
        omnibox::kEnableSearchAggregatorPolicy);
  }
  ~SearchAggregatorPolicyOmniboxViewTest() override = default;

  base::Value CreateEnterpriseSearchAggregatorPolicyValue() {
    base::DictValue policy_value;
    policy_value = base::DictValue()
                       .Set(policy::SearchAggregatorPolicyHandler::kIconUrl,
                            kSearchAggregatorPolicyIconUrl)
                       .Set(policy::SearchAggregatorPolicyHandler::kShortcut,
                            kSearchAggregatorPolicyKeyword)
                       .Set(policy::SearchAggregatorPolicyHandler::kName,
                            kSearchAggregatorPolicyName)
                       .Set(policy::SearchAggregatorPolicyHandler::kSearchUrl,
                            kSearchAggregatorPolicySearchUrl)
                       .Set(policy::SearchAggregatorPolicyHandler::kSuggestUrl,
                            kSearchAggregatorPolicySuggestUrl);
    return base::Value(std::move(policy_value));
  }

 private:
  base::test::ScopedFeatureList scoped_feature_list_;
};

// Verifies that keyword search works when
// `EnterpriseSearchAggregatorSettings` policy is set.
IN_PROC_BROWSER_TEST_F(SearchAggregatorPolicyOmniboxViewTest, NonFeatured) {
  policy::PolicyMap policies;
  policies.Set(policy::key::kEnterpriseSearchAggregatorSettings,
               policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
               policy::POLICY_SOURCE_CLOUD,
               CreateEnterpriseSearchAggregatorPolicyValue(), nullptr);
  policy_provider()->UpdateChromePolicy(policies);

  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Check that new entries have been added to TemplateURLService.
  const TemplateURL* turl =
      TemplateURLServiceFactory::GetForProfile(browser()->GetProfile())
          ->GetTemplateURLForKeyword(kSearchAggregatorPolicyKeyword);
  ASSERT_TRUE(turl);
  EXPECT_EQ(turl->policy_origin(),
            TemplateURLData::PolicyOrigin::kSearchAggregator);
  EXPECT_EQ(turl->short_name(), kSearchAggregatorPolicyName);
  EXPECT_EQ(turl->url(), kSearchAggregatorPolicySearchUrl);
  EXPECT_EQ(turl->suggestions_url(), kSearchAggregatorPolicySuggestUrl);
  EXPECT_EQ(turl->favicon_url(), kSearchAggregatorPolicyIconUrl);
  EXPECT_TRUE(turl->enforced_by_policy());
  EXPECT_FALSE(turl->safe_for_autoreplace());
  EXPECT_FALSE(turl->featured_by_policy());

  // Trigger keyword hint mode.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchAggregatorPolicyKeywordKeys));
  EXPECT_TRUE(GetOmniboxEditModel()->is_keyword_hint());
  EXPECT_EQ(GetOmniboxEditModel()->keyword(), kSearchAggregatorPolicyKeyword);

  // Trigger keyword mode.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_TAB, 0));
  EXPECT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  EXPECT_EQ(GetOmniboxEditModel()->keyword(), kSearchAggregatorPolicyKeyword);

  // Input something as search text and perform a search.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  EXPECT_TRUE(GetOmniboxController()->IsPopupOpen());

  EXPECT_EQ(GetOmniboxController()
                ->autocomplete_controller()
                ->result()
                .default_match()
                ->destination_url.spec(),
            kSearchAggregatorPolicyTextURL);
}

// Verifies that featured search engine is shown when
// `EnterpriseSearchAggregatorSettings` policy is set.
IN_PROC_BROWSER_TEST_F(SearchAggregatorPolicyOmniboxViewTest, Featured) {
  policy::PolicyMap policies;
  policies.Set(policy::key::kEnterpriseSearchAggregatorSettings,
               policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
               policy::POLICY_SOURCE_CLOUD,
               CreateEnterpriseSearchAggregatorPolicyValue(), nullptr);
  policy_provider()->UpdateChromePolicy(policies);

  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Check that new entries have been added to TemplateURLService with at
  // prefix.
  const TemplateURL* turl =
      TemplateURLServiceFactory::GetForProfile(browser()->GetProfile())
          ->GetTemplateURLForKeyword(
              kSearchAggregatorPolicyKeywordWithAtPrefix);
  ASSERT_TRUE(turl);
  EXPECT_EQ(turl->policy_origin(),
            TemplateURLData::PolicyOrigin::kSearchAggregator);
  EXPECT_EQ(turl->short_name(), kSearchAggregatorPolicyName);
  EXPECT_EQ(turl->url(), kSearchAggregatorPolicySearchUrl);
  EXPECT_EQ(turl->suggestions_url(), kSearchAggregatorPolicySuggestUrl);
  EXPECT_EQ(turl->favicon_url(), kSearchAggregatorPolicyIconUrl);
  EXPECT_TRUE(turl->enforced_by_policy());
  EXPECT_FALSE(turl->safe_for_autoreplace());
  EXPECT_TRUE(turl->featured_by_policy());

  // Type the keyword.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_2, ui::EF_SHIFT_DOWN));
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchAggregatorPolicyKeywordKeys));
  EXPECT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  EXPECT_EQ(GetOmniboxEditModel()->keyword(), u"");

  // Trigger keyword mode.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_TAB, 0));
  EXPECT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  EXPECT_EQ(GetOmniboxEditModel()->keyword(),
            kSearchAggregatorPolicyKeywordWithAtPrefix);

  // Input something as search text and perform a search.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  EXPECT_TRUE(GetOmniboxController()->IsPopupOpen());

  EXPECT_EQ(GetOmniboxController()
                ->autocomplete_controller()
                ->result()
                .default_match()
                ->destination_url.spec(),
            kSearchAggregatorPolicyTextURL);
}

// Verifies that featured search engine is shown with starter pack on "@"
// state and that the underlying search works when
// `EnterpriseSearchAggregatorSettings` policy is set.
IN_PROC_BROWSER_TEST_F(SearchAggregatorPolicyOmniboxViewTest,
                       FeaturedOnArrowDown) {
  policy::PolicyMap policies;
  policies.Set(policy::key::kEnterpriseSearchAggregatorSettings,
               policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER,
               policy::POLICY_SOURCE_CLOUD,
               CreateEnterpriseSearchAggregatorPolicyValue(), nullptr);
  policy_provider()->UpdateChromePolicy(policies);

  OmniboxView* omnibox_view = nullptr;
  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));

  // Check that new entries have been added to TemplateURLService with at
  // prefix.
  const TemplateURL* turl =
      TemplateURLServiceFactory::GetForProfile(browser()->GetProfile())
          ->GetTemplateURLForKeyword(
              kSearchAggregatorPolicyKeywordWithAtPrefix);
  ASSERT_TRUE(turl);
  EXPECT_EQ(turl->policy_origin(),
            TemplateURLData::PolicyOrigin::kSearchAggregator);
  EXPECT_EQ(turl->short_name(), kSearchAggregatorPolicyName);
  EXPECT_EQ(turl->url(), kSearchAggregatorPolicySearchUrl);
  EXPECT_EQ(turl->suggestions_url(), kSearchAggregatorPolicySuggestUrl);
  EXPECT_EQ(turl->favicon_url(), kSearchAggregatorPolicyIconUrl);
  EXPECT_TRUE(turl->enforced_by_policy());
  EXPECT_FALSE(turl->safe_for_autoreplace());
  EXPECT_TRUE(turl->featured_by_policy());

  // Trigger keyword mode.
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_2, ui::EF_SHIFT_DOWN));
  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_TAB, 0));
  EXPECT_FALSE(GetOmniboxEditModel()->is_keyword_hint());
  EXPECT_EQ(GetOmniboxEditModel()->keyword(),
            kSearchAggregatorPolicyKeywordWithAtPrefix);

  // Input something as search text and perform a search.
  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kSearchTextKeys));
  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
  EXPECT_TRUE(GetOmniboxController()->IsPopupOpen());

  EXPECT_EQ(GetOmniboxController()
                ->autocomplete_controller()
                ->result()
                .default_match()
                ->destination_url.spec(),
            kSearchAggregatorPolicyTextURL);
}
