blob: 90174f2451996a5906f47b4a4479017b19065740 [file] [log] [blame]
// 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 <stddef.h>
#include <array>
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sharing_hub/sharing_hub_features.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_element_identifiers.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/find_bar/find_bar_host_unittest_util.h"
#include "chrome/browser/ui/frame/window_frame_util.h"
#include "chrome/browser/ui/location_bar/location_bar.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/view_ids.h"
#include "chrome/browser/ui/views/find_bar_view.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/frame/contents_web_view.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/interaction/interactive_browser_test.h"
#include "components/omnibox/browser/autocomplete_match_type.h"
#include "components/omnibox/browser/omnibox_client.h"
#include "components/omnibox/browser/omnibox_controller.h"
#include "components/omnibox/browser/omnibox_view.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.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/focus_changed_observer.h"
#include "content/public/test/test_navigation_observer.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "third_party/blink/public/common/switches.h"
#include "ui/base/test/ui_controls.h"
#include "ui/base/ui_base_features.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/interaction/view_focus_observer.h"
#include "ui/views/test/widget_activation_waiter.h"
#include "ui/views/widget/widget.h"
#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "base/test/scoped_feature_list.h"
#endif
namespace {
constexpr char kGetFocusedElementJS[] = "getFocusedElement();";
// Listens to UI and DOM element focus changes.
class FocusChangeObserver : public views::FocusChangeListener,
public content::WebContentsObserver {
public:
FocusChangeObserver(views::FocusManager* focus_manager,
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents) {
focus_manager_observation_.Observe(focus_manager);
}
void WaitForFocusChange() { run_loop_.Run(); }
// FocusChangeListener:
void OnDidChangeFocus(views::View* focused_before,
views::View* focused_now) override {
if (focused_now) {
SCOPED_TRACE(base::StrCat(
{"View with ID=", base::NumberToString(focused_now->GetID()),
" is focused now."}));
}
run_loop_.Quit();
}
// WebContentsObserver:
void OnFocusChangedInPage(content::FocusedNodeDetails* details) override {
SCOPED_TRACE(base::StrCat(
{"Page element with id=",
content::EvalJs(web_contents(), kGetFocusedElementJS).ExtractString(),
" is focused now."}));
run_loop_.Quit();
}
private:
base::ScopedObservation<views::FocusManager, views::FocusChangeListener>
focus_manager_observation_{this};
base::RunLoop run_loop_;
};
} // namespace
namespace {
using content::RenderViewHost;
using content::WebContents;
const char kSimplePage[] = "/focus/page_with_focus.html";
const char kStealFocusPage[] = "/focus/page_steals_focus.html";
const char kTypicalPage[] = "/focus/typical_page.html";
class BrowserFocusBasicTest : public InProcessBrowserTest {
public:
BrowserFocusBasicTest() {
// interactive_ui_tests set `ui_test_utils::BringBrowserWindowToFront()` for
// the setup function, which interferes with what the test wants to test so
// unset it.
set_global_browser_set_up_function(nullptr);
#if BUILDFLAG(IS_WIN)
// For CHROME_HEADLESS, which is currently used for browser tests, native
// window occlusion is turned off. Turn it on to match the production
// environment.
base::FieldTrialParams field_trial_params{
{ features::kApplyNativeOcclusionToCompositorType.name,
features::kApplyNativeOcclusionToCompositorTypeRelease }};
scoped_feature_list_.InitWithFeaturesAndParameters(
/*enabled_features=*/
{{features::kApplyNativeOcclusionToCompositor, field_trial_params},
{ features::kAlwaysTrackNativeWindowOcclusionForTest,
{} }},
/*disabled_features=*/{});
#endif
}
views::Widget* GetWidgetForBrowser(Browser* browser) {
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
CHECK(browser_view);
views::Widget* widget = browser_view->GetWidget();
CHECK(widget);
return widget;
}
bool IsBrowserActive(Browser* browser) {
return GetWidgetForBrowser(browser)->IsActive();
}
private:
#if BUILDFLAG(IS_WIN)
base::test::ScopedFeatureList scoped_feature_list_;
#endif
};
// A basic test to check that a newly opened browser window has focus and the
// focus is on the omnibox.
IN_PROC_BROWSER_TEST_F(BrowserFocusBasicTest, BrowserFocusedOnCreation) {
// Ensure that the initialization of the browser window is completed.
ui_test_utils::CreateAsyncWidgetRequestWaiter(*browser()).Wait();
// Widget activation happens asynchronously after window creation on some
// platforms like Linux so absorb the difference by waiting for the
// activation.
views::test::WaitForWidgetActive(GetWidgetForBrowser(browser()), true);
// Check that when a browser is created, it's active.
EXPECT_TRUE(IsBrowserActive(browser()));
EXPECT_TRUE(ui_test_utils::IsViewFocused(browser(), VIEW_ID_OMNIBOX));
// Use `chrome::OpenEmptyWindow()` instead of directly creating a `Browser`
// instance with `Browser::Create()` and calling `BrowserView::Show()` like
// some tests do because this is what the production code does when opening a
// new window. The difference is that it makes sure that there is at least one
// tab on the window before calling `BrowserView::Show()`.
Browser* browser2 = chrome::OpenEmptyWindow(browser()->profile());
ui_test_utils::CreateAsyncWidgetRequestWaiter(*browser2).Wait();
views::test::WaitForWidgetActive(GetWidgetForBrowser(browser2), true);
EXPECT_TRUE(IsBrowserActive(browser2));
EXPECT_TRUE(ui_test_utils::IsViewFocused(browser2, VIEW_ID_OMNIBOX));
}
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kWebContentsId);
class BrowserFocusTest : public InteractiveBrowserTest {
public:
// InteractiveBrowserTest overrides:
void SetUpOnMainThread() override {
ASSERT_TRUE(embedded_test_server()->Start());
InteractiveBrowserTest::SetUpOnMainThread();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
// Slow bots are flaky due to slower loading interacting with
// deferred commits.
command_line->AppendSwitch(blink::switches::kAllowPreCommitInput);
}
bool IsViewFocused(ViewID vid) {
return ui_test_utils::IsViewFocused(browser(), vid);
}
void ClickOnView(ViewID vid) { ui_test_utils::ClickOnView(browser(), vid); }
void FocusNextElement(bool reverse) {
FocusChangeObserver obs{
GetFocusManager(),
browser()->tab_strip_model()->GetActiveWebContents()};
ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_TAB, false,
reverse, false, false));
obs.WaitForFocusChange();
}
void TestFocusTraversal(bool reverse) {
SCOPED_TRACE(base::StrCat(
{"Started focus traversal, reverse=", base::ToString(reverse)}));
// Move focus one element away from the omnibox.
ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
FocusNextElement(reverse);
EXPECT_FALSE(IsViewFocused(VIEW_ID_OMNIBOX));
// Traverse the whole focus chain until the omnibox is focused again.
size_t c = 0;
while (!IsViewFocused(VIEW_ID_OMNIBOX) && c < kMaxIterations) {
FocusNextElement(reverse);
++c;
}
EXPECT_TRUE(c <= kMaxIterations);
EXPECT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
}
views::FocusManager* GetFocusManager() {
BrowserWindow* browser_window = browser()->window();
DCHECK(browser_window);
gfx::NativeWindow window = browser_window->GetNativeWindow();
DCHECK(window);
views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
DCHECK(widget);
views::FocusManager* focus_manager = widget->GetFocusManager();
DCHECK(focus_manager);
return focus_manager;
}
views::Widget* GetWidgetForBrowser(Browser* browser) {
BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
CHECK(browser_view);
views::Widget* widget = browser_view->GetWidget();
CHECK(widget);
return widget;
}
private:
constexpr static size_t kMaxIterations = 20;
};
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, ClickingMovesFocus) {
RunTestSequence(
InstrumentTab(kWebContentsId),
ObserveState(views::test::kCurrentFocusedViewId,
GetWidgetForBrowser(browser())),
WaitForState(views::test::kCurrentFocusedViewId, kOmniboxElementId),
// Click on the tab container and check that it has focus.
MoveMouseTo(ContentsWebView::kContentsWebViewElementId), ClickMouse(),
WaitForState(views::test::kCurrentFocusedViewId,
ContentsWebView::kContentsWebViewElementId),
// Click on the omnibox and check that it has focus.
MoveMouseTo(kOmniboxElementId), ClickMouse(),
WaitForState(views::test::kCurrentFocusedViewId, kOmniboxElementId));
}
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, BrowsersRememberFocus) {
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
const GURL url = embedded_test_server()->GetURL(kSimplePage);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
gfx::NativeWindow window = browser()->window()->GetNativeWindow();
// The focus should be on the Tab contents.
ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
// Now hide the window, show it again, the focus should not have changed.
ui_test_utils::HideNativeWindow(window);
ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(window));
ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
chrome::FocusLocationBar(browser());
ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
// Hide the window, show it again, the focus should not have changed.
ui_test_utils::HideNativeWindow(window);
ASSERT_TRUE(ui_test_utils::ShowAndFocusNativeWindow(window));
ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
}
// Tabs remember focus.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, TabsRememberFocus) {
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
const GURL url = embedded_test_server()->GetURL(kSimplePage);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
// Create several tabs.
for (int i = 0; i < 4; ++i) {
chrome::AddSelectedTabWithURL(browser(), url, ui::PAGE_TRANSITION_TYPED);
}
// Alternate focus for the tab.
const std::array<std::array<const bool, 5>, 3> kFocusPage = {{
{true, true, true, true, false},
{false, false, false, false, false},
{false, true, false, true, false},
}};
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j++) {
// Activate the tab.
browser()->tab_strip_model()->ActivateTabAt(
j, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
// Activate the location bar or the page.
if (kFocusPage[i][j]) {
browser()->tab_strip_model()->GetWebContentsAt(j)->Focus();
} else {
chrome::FocusLocationBar(browser());
}
}
// Now come back to the tab and check the right view is focused.
for (int j = 0; j < 5; j++) {
// Activate the tab.
browser()->tab_strip_model()->ActivateTabAt(
j, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
ViewID vid = kFocusPage[i][j] ? VIEW_ID_TAB_CONTAINER : VIEW_ID_OMNIBOX;
ASSERT_TRUE(IsViewFocused(vid));
}
browser()->tab_strip_model()->ActivateTabAt(
0, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
// Try the above, but with ctrl+tab. Since tab normally changes focus,
// this has regressed in the past. Loop through several times to be sure.
for (int j = 0; j < 15; j++) {
ViewID vid =
kFocusPage[i][j % 5] ? VIEW_ID_TAB_CONTAINER : VIEW_ID_OMNIBOX;
ASSERT_TRUE(IsViewFocused(vid));
ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_TAB, true,
false, false, false));
}
// As above, but with ctrl+shift+tab.
browser()->tab_strip_model()->ActivateTabAt(
4, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
for (int j = 14; j >= 0; --j) {
ViewID vid =
kFocusPage[i][j % 5] ? VIEW_ID_TAB_CONTAINER : VIEW_ID_OMNIBOX;
ASSERT_TRUE(IsViewFocused(vid));
ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_TAB, true,
true, false, false));
}
}
}
// Tabs remember focus with find-in-page box.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, TabsRememberFocusFindInPage) {
// TODO(crbug.com/40268465): Re-enable when child widget focus manager
// relationship is fixed.
#if BUILDFLAG(IS_MAC)
if (base::mac::MacOSMajorVersion() >= 13) {
GTEST_SKIP() << "Broken on macOS 13: https://crbug.com/1446127";
}
#endif
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
const GURL url = embedded_test_server()->GetURL(kSimplePage);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
chrome::Find(browser());
ui_test_utils::FindInPage(
browser()->tab_strip_model()->GetActiveWebContents(), u"a", true, false,
nullptr, nullptr);
ASSERT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
// Focus the location bar.
chrome::FocusLocationBar(browser());
// Create a 2nd tab.
chrome::AddSelectedTabWithURL(browser(), url, ui::PAGE_TRANSITION_TYPED);
// Focus should be on the recently opened tab page.
ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
// Select 1st tab, focus should still be on the location-bar.
// (bug http://crbug.com/23296)
browser()->tab_strip_model()->ActivateTabAt(
0, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
// Now open the find box again, switch to another tab and come back, the focus
// should return to the find box.
chrome::Find(browser());
ASSERT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
browser()->tab_strip_model()->ActivateTabAt(
1, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
browser()->tab_strip_model()->ActivateTabAt(
0, TabStripUserGestureDetails(
TabStripUserGestureDetails::GestureType::kOther));
ASSERT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
}
// Background window does not steal focus.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, BackgroundBrowserDontStealFocus) {
// Ensure the browser process state is in sync with the WindowServer process.
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
// Open a new browser window.
Browser* background_browser =
Browser::Create(Browser::CreateParams(browser()->profile(), true));
chrome::AddTabAt(background_browser, GURL(), -1, true);
background_browser->window()->Show();
const GURL steal_focus_url = embedded_test_server()->GetURL(kStealFocusPage);
ASSERT_TRUE(
ui_test_utils::NavigateToURL(background_browser, steal_focus_url));
// The navigation will activate |background_browser|. Except, on some
// platforms, that may be asynchronous. Ensure the activation is properly
// reflected in the browser process by activating again.
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(background_browser));
EXPECT_TRUE(background_browser->window()->IsActive());
// Activate the first browser (again). Note BringBrowserWindowToFront() does
// Show() and Focus(), but not Activate(), which is needed for Desktop Linux.
browser()->window()->Activate();
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
EXPECT_TRUE(browser()->window()->IsActive());
ASSERT_TRUE(content::ExecJs(
background_browser->tab_strip_model()->GetActiveWebContents(),
"stealFocus();"));
// Try flushing tasks. Note that on Mac and Desktop Linux, window activation
// is asynchronous. There's no way to guarantee that the WindowServer process
// has actually activated a window without waiting for the activation event.
// But this test is checking that _no_ activation event occurs. So there is
// nothing to wait for. So, assuming the test fails and |unfocused_browser|
// _did_ activate, the expectation below still isn't guaranteed to fail after
// flushing run loops.
content::RunAllTasksUntilIdle();
// Make sure the first browser is still active.
EXPECT_TRUE(browser()->window()->IsActive());
}
// Page cannot steal focus when focus is on location bar.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, LocationBarLockFocus) {
// Open the page that steals focus.
const GURL url = embedded_test_server()->GetURL(kStealFocusPage);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
chrome::FocusLocationBar(browser());
ASSERT_TRUE(content::ExecJs(
browser()->tab_strip_model()->GetActiveWebContents(), "stealFocus();"));
// Make sure the location bar is still focused.
ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
}
// Test forward and reverse focus traversal on a typical page.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, FocusTraversal) {
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
const GURL url = embedded_test_server()->GetURL(kTypicalPage);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
FocusChangeObserver obs{GetFocusManager(),
browser()->tab_strip_model()->GetActiveWebContents()};
chrome::FocusLocationBar(browser());
obs.WaitForFocusChange();
ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
// Simulate ESC being pressed to close the omnibox suggestions popup.
browser()->window()->GetLocationBar()->GetOmniboxView()->CloseOmniboxPopup();
// Loop through the focus chain twice in each direction for good measure.
TestFocusTraversal(false);
TestFocusTraversal(false);
TestFocusTraversal(true);
TestFocusTraversal(true);
}
// Test that find-in-page UI can request focus, even when it is already open.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, FindFocusTest) {
RunTestSequence(
InstrumentTab(kWebContentsId),
ObserveState(views::test::kCurrentFocusedViewId,
GetWidgetForBrowser(browser())),
Do([this]() { chrome::Find(browser()); }),
WaitForState(views::test::kCurrentFocusedViewId, FindBarView::kTextField),
MoveMouseTo(kOmniboxElementId), ClickMouse(),
WaitForState(views::test::kCurrentFocusedViewId, kOmniboxElementId),
Do([this]() { chrome::Find(browser()); }),
WaitForState(views::test::kCurrentFocusedViewId,
FindBarView::kTextField));
}
// Makes sure the focus is in the right location when opening the different
// types of tabs.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, TabInitialFocus) {
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
// Open the history tab, focus should be on the tab contents.
chrome::ShowHistory(browser());
ASSERT_NO_FATAL_FAILURE(EXPECT_TRUE(content::WaitForLoadStop(
browser()->tab_strip_model()->GetActiveWebContents())));
EXPECT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
// Open the new tab, focus should be on the location bar.
chrome::NewTab(browser());
ASSERT_NO_FATAL_FAILURE(EXPECT_TRUE(content::WaitForLoadStop(
browser()->tab_strip_model()->GetActiveWebContents())));
EXPECT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
// Open the download tab, focus should be on the tab contents.
chrome::ShowDownloads(browser());
ASSERT_NO_FATAL_FAILURE(EXPECT_TRUE(content::WaitForLoadStop(
browser()->tab_strip_model()->GetActiveWebContents())));
EXPECT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
// Open about:blank, focus should be on the location bar.
chrome::AddSelectedTabWithURL(browser(), GURL(url::kAboutBlankURL),
ui::PAGE_TRANSITION_LINK);
ASSERT_NO_FATAL_FAILURE(EXPECT_TRUE(content::WaitForLoadStop(
browser()->tab_strip_model()->GetActiveWebContents())));
EXPECT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
}
// Tests that focus goes where expected when using reload.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, FocusOnReload) {
// Open the new tab, reload.
{
auto& contents = chrome::NewTab(browser());
content::WaitForLoadStop(&contents);
}
content::RunAllPendingInMessageLoop();
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
observer.Wait();
}
// Focus should stay on the location bar.
ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
// Open a regular page, focus the location bar, reload.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(kSimplePage)));
chrome::FocusLocationBar(browser());
ASSERT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
observer.Wait();
}
// Focus should now be on the tab contents.
chrome::ShowDownloads(browser());
ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
}
// Tests that focus goes where expected when using reload on a crashed tab.
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
// Hangy, http://crbug.com/50025.
#define MAYBE_FocusOnReloadCrashedTab DISABLED_FocusOnReloadCrashedTab
#else
#define MAYBE_FocusOnReloadCrashedTab FocusOnReloadCrashedTab
#endif
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, MAYBE_FocusOnReloadCrashedTab) {
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
// Open a regular page, crash, reload.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(kSimplePage)));
content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents());
{
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
observer.Wait();
}
// Focus should now be on the tab contents.
chrome::ShowDownloads(browser());
ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
}
// Tests that focus goes to frame after crashed tab.
// TODO(shrikant): Find out where the focus should be deterministically.
// Currently focused_view after crash seem to be non null in debug mode
// (invalidated pointer 0xcccccc).
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, DISABLED_FocusAfterCrashedTab) {
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents());
ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
}
// Tests that when omnibox triggers a navigation, then the focus is moved into
// the current tab.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, NavigateFromOmnibox) {
const GURL url = embedded_test_server()->GetURL("/title1.html");
// Focus the Omnibox.
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
chrome::FocusLocationBar(browser());
OmniboxView* view = browser()->window()->GetLocationBar()->GetOmniboxView();
// Simulate typing a URL into the omnibox.
view->SetUserText(base::UTF8ToUTF16(url.spec()));
EXPECT_TRUE(IsViewFocused(VIEW_ID_OMNIBOX));
EXPECT_FALSE(view->IsSelectAll());
// Simulate pressing Enter and wait until the navigation starts.
content::WebContents* web_contents =
chrome_test_utils::GetActiveWebContents(this);
content::TestNavigationManager nav_manager(web_contents, url);
ASSERT_TRUE(ui_controls::SendKeyPress(browser()->window()->GetNativeWindow(),
ui::VKEY_RETURN, false, false, false,
false));
ASSERT_TRUE(nav_manager.WaitForRequestStart());
// Verify that a navigation has started.
EXPECT_TRUE(web_contents->GetController().GetPendingEntry());
// Verify that the Omnibox text is not selected - this is a regression test
// for https://crbug.com/1048742.
EXPECT_FALSE(view->IsSelectAll());
// Intentionally not asserting anything about IsViewFocused in this
// _intermediate_ state.
// Wait for the navigation to finish and verify final, steady state.
ASSERT_TRUE(nav_manager.WaitForNavigationFinished());
EXPECT_TRUE(nav_manager.was_successful());
EXPECT_EQ(url, web_contents->GetLastCommittedURL());
EXPECT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
EXPECT_FALSE(view->IsSelectAll());
}
// Tests that when a new tab is opened from the omnibox, the focus is moved from
// the omnibox for the current tab.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, NavigateFromOmniboxIntoNewTab) {
GURL url("http://www.google.com/");
GURL url2("http://maps.google.com/");
// Navigate to url.
NavigateParams p(browser(), url, ui::PAGE_TRANSITION_LINK);
p.window_action = NavigateParams::SHOW_WINDOW;
p.disposition = WindowOpenDisposition::CURRENT_TAB;
Navigate(&p);
// Focus the omnibox.
chrome::FocusLocationBar(browser());
OmniboxClient* omnibox_client = browser()
->window()
->GetLocationBar()
->GetOmniboxView()
->controller()
->client();
// Simulate an alt-enter.
omnibox_client->OnAutocompleteAccept(
url2, nullptr, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui::PAGE_TRANSITION_TYPED, AutocompleteMatchType::URL_WHAT_YOU_TYPED,
base::TimeTicks(), false, false, std::u16string(), AutocompleteMatch(),
AutocompleteMatch());
// Make sure the second tab is selected.
EXPECT_EQ(1, browser()->tab_strip_model()->active_index());
// The tab contents should have the focus in the second tab.
EXPECT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER));
// Go back to the first tab. The focus should not be in the omnibox.
chrome::SelectPreviousTab(browser());
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
EXPECT_FALSE(IsViewFocused(VIEW_ID_OMNIBOX));
}
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, OmniboxFocusesOnNewTab) {
RunTestSequence(
InstrumentTab(kWebContentsId),
ObserveState(views::test::kCurrentFocusedViewId,
GetWidgetForBrowser(browser())),
WaitForState(views::test::kCurrentFocusedViewId, kOmniboxElementId));
}
// TODO(crbug.com/370117091): This currently tests the incorrect behavior that
// causes the omnibox to *not* get focus when going back to a new tab page via
// the history. Update this test when the bug is fixed.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, OmniboxFocusStateAcrossHistory) {
RunTestSequence(
InstrumentTab(kWebContentsId),
ObserveState(views::test::kCurrentFocusedViewId,
GetWidgetForBrowser(browser())),
NavigateWebContents(kWebContentsId, GURL(chrome::kChromeUINewTabURL)),
NavigateWebContents(kWebContentsId,
embedded_test_server()->GetURL("/title1.html")),
MoveMouseTo(ContentsWebView::kContentsWebViewElementId), ClickMouse(),
// Navigate back. Check that the location bar is not focused. This should
// focus the location bar, but that is not the current behavior.
PressButton(kToolbarBackButtonElementId),
WaitForWebContentsNavigation(kWebContentsId,
GURL(chrome::kChromeUINewTabURL)),
WaitForState(views::test::kCurrentFocusedViewId,
testing::Ne(kOmniboxElementId)),
// Navigate forward. Should focus the body.
PressButton(kToolbarForwardButtonElementId),
WaitForWebContentsNavigation(
kWebContentsId, embedded_test_server()->GetURL("/title1.html")),
WaitForState(views::test::kCurrentFocusedViewId,
ContentsWebView::kContentsWebViewElementId));
}
// Ensure that crbug.com/567445 does not regress. This test checks that the
// Omnibox does not get focused when loading about:blank in a case where it's
// not the startup URL, e.g. when a page opens a popup to about:blank, with a
// null opener, and then navigates it. This is a potential security issue; see
// comments in |WebContentsImpl::FocusLocationBarByDefault|.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, AboutBlankNavigationLocationTest) {
const GURL url1 = embedded_test_server()->GetURL("/title1.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url1));
TabStripModel* tab_strip = browser()->tab_strip_model();
WebContents* web_contents = tab_strip->GetActiveWebContents();
const GURL url2 = embedded_test_server()->GetURL("/title2.html");
const std::string spoof =
"var w = window.open('about:blank'); w.opener = null;"
"w.document.location = '" +
url2.spec() + "';";
ASSERT_TRUE(content::ExecJs(web_contents, spoof));
EXPECT_EQ(url1, web_contents->GetVisibleURL());
// After running the spoof code, |GetActiveWebContents| returns the new tab,
// not the same as |web_contents|.
ASSERT_NO_FATAL_FAILURE(EXPECT_TRUE(content::WaitForLoadStop(
browser()->tab_strip_model()->GetActiveWebContents())));
EXPECT_FALSE(IsViewFocused(VIEW_ID_OMNIBOX));
}
// Regression test for https://crbug.com/677716. This ensures that the omnibox
// does not get focused if another tab in the same window navigates to the New
// Tab Page, since that can scroll the origin of the selected tab out of view.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, NoFocusForBackgroundNTP) {
// Start at the NTP and navigate to a test page. We will later go back to the
// NTP, which gives the omnibox focus in some cases.
chrome::NewTab(browser());
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
TabStripModel* tab_strip = browser()->tab_strip_model();
WebContents* opener_web_contents = tab_strip->GetActiveWebContents();
// Open a second tab from the test page.
const GURL new_url = embedded_test_server()->GetURL("/title2.html");
const std::string open_script = "window.open('" + new_url.spec() + "');";
content::WebContentsAddedObserver open_observer;
ASSERT_TRUE(content::ExecJs(opener_web_contents, open_script));
WebContents* new_web_contents = open_observer.GetWebContents();
// Tell the first (non-selected) tab to go back. This should not give the
// omnibox focus, since the navigation occurred in a different tab. Otherwise
// the focus may scroll the origin out of view, making a spoof possible.
const std::string go_back_script = "window.opener.history.back();";
content::TestNavigationObserver back_observer(opener_web_contents);
ASSERT_TRUE(content::ExecJs(new_web_contents, go_back_script));
back_observer.Wait();
EXPECT_FALSE(IsViewFocused(VIEW_ID_OMNIBOX));
}
// Tests that the location bar is focusable when showing, which is the case in
// popup windows.
// TODO(crbug.com/40794922): Flaky on Linux.
// TODO(crbug/1520655): Broken since CR2023.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, DISABLED_PopupLocationBar) {
Browser* popup_browser = CreateBrowserForPopup(browser()->profile());
// Make sure the popup is in the front. Otherwise the test is flaky.
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(popup_browser));
ui_test_utils::FocusView(popup_browser, VIEW_ID_TAB_CONTAINER);
EXPECT_TRUE(
ui_test_utils::IsViewFocused(popup_browser, VIEW_ID_TAB_CONTAINER));
ASSERT_TRUE(ui_test_utils::SendKeyPressSync(popup_browser, ui::VKEY_TAB,
false, false, false, false));
ui_test_utils::WaitForViewFocus(popup_browser, VIEW_ID_LOCATION_ICON, true);
ASSERT_TRUE(ui_test_utils::SendKeyPressSync(popup_browser, ui::VKEY_TAB,
false, false, false, false));
ui_test_utils::WaitForViewFocus(popup_browser, VIEW_ID_OMNIBOX, true);
ASSERT_TRUE(ui_test_utils::SendKeyPressSync(popup_browser, ui::VKEY_TAB,
false, false, false, false));
if (sharing_hub::HasPageAction(browser()->profile(), true)) {
ui_test_utils::WaitForViewFocus(popup_browser, VIEW_ID_SHARING_HUB_BUTTON,
true);
ASSERT_TRUE(ui_test_utils::SendKeyPressSync(popup_browser, ui::VKEY_TAB,
false, false, false, false));
}
ui_test_utils::WaitForViewFocus(popup_browser, VIEW_ID_TAB_CONTAINER, true);
}
// Tests that the location bar is not focusable when hidden, which is the case
// in app windows.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest, AppLocationBar) {
Browser* app_browser = CreateBrowserForApp("foo", browser()->profile());
// Make sure the app window is in the front. Otherwise the test is flaky.
ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(app_browser));
ui_test_utils::FocusView(app_browser, VIEW_ID_TAB_CONTAINER);
EXPECT_TRUE(ui_test_utils::IsViewFocused(app_browser, VIEW_ID_TAB_CONTAINER));
ASSERT_TRUE(ui_test_utils::SendKeyPressSync(app_browser, ui::VKEY_TAB, false,
false, false, false));
base::RunLoop().RunUntilIdle();
ui_test_utils::WaitForViewFocus(app_browser, VIEW_ID_TAB_CONTAINER, true);
}
} // namespace