1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "build/build_config.h"
6
7#include "base/file_util.h"
8#include "base/format_macros.h"
9#include "base/message_loop.h"
10#include "base/path_service.h"
11#include "base/string_number_conversions.h"
12#include "base/string_util.h"
13#include "base/utf_string_conversions.h"
14#include "chrome/browser/tabs/tab_strip_model.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/browser/ui/browser_window.h"
17#include "chrome/browser/ui/view_ids.h"
18#include "chrome/common/chrome_paths.h"
19#include "chrome/common/url_constants.h"
20#include "chrome/test/in_process_browser_test.h"
21#include "chrome/test/ui_test_utils.h"
22#include "content/browser/renderer_host/render_view_host.h"
23#include "content/browser/renderer_host/render_widget_host_view.h"
24#include "content/browser/tab_contents/interstitial_page.h"
25#include "content/browser/tab_contents/tab_contents.h"
26#include "content/browser/tab_contents/tab_contents_view.h"
27#include "net/test/test_server.h"
28
29#if defined(TOOLKIT_VIEWS) || defined(OS_WIN)
30#include "views/focus/focus_manager.h"
31#include "views/view.h"
32#include "views/window/window.h"
33#endif
34
35#if defined(TOOLKIT_VIEWS)
36#include "chrome/browser/ui/views/frame/browser_view.h"
37#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
38#include "chrome/browser/ui/views/tab_contents/tab_contents_container.h"
39#endif
40
41#if defined(TOOLKIT_USES_GTK)
42#include "chrome/browser/ui/gtk/view_id_util.h"
43#endif
44
45#if defined(OS_WIN)
46#include <Psapi.h>
47#include <windows.h>
48#endif
49
50#if defined(OS_LINUX)
51#define MAYBE_FocusTraversal FocusTraversal
52#define MAYBE_FocusTraversalOnInterstitial FocusTraversalOnInterstitial
53// TODO(jcampan): http://crbug.com/23683
54#define MAYBE_TabsRememberFocusFindInPage FAILS_TabsRememberFocusFindInPage
55#elif defined(OS_MACOSX)
56// TODO(suzhe): http://crbug.com/60973 (following two tests)
57#define MAYBE_FocusTraversal DISABLED_FocusTraversal
58#define MAYBE_FocusTraversalOnInterstitial DISABLED_FocusTraversalOnInterstitial
59// TODO(suzhe): http://crbug.com/49737
60#define MAYBE_TabsRememberFocusFindInPage FAILS_TabsRememberFocusFindInPage
61#elif defined(OS_WIN)
62// Disabled, http://crbug.com/62543.
63#define MAYBE_FocusTraversal DISABLED_FocusTraversal
64// Disabled, http://crbug.com/62544.
65#define MAYBE_FocusTraversalOnInterstitial DISABLED_FocusTraversalOnInterstitial
66// Flaky, http://crbug.com/62537.
67#define MAYBE_TabsRememberFocusFindInPage FLAKY_TabsRememberFocusFindInPage
68#endif
69
70namespace {
71
72// The delay waited in some cases where we don't have a notifications for an
73// action we take.
74const int kActionDelayMs = 500;
75
76const char kSimplePage[] = "files/focus/page_with_focus.html";
77const char kStealFocusPage[] = "files/focus/page_steals_focus.html";
78const char kTypicalPage[] = "files/focus/typical_page.html";
79const char kTypicalPageName[] = "typical_page.html";
80
81// Test to make sure Chrome is in the foreground as we start testing. This is
82// required for tests that synthesize input to the Chrome window.
83bool ChromeInForeground() {
84#if defined(OS_WIN)
85  HWND window = ::GetForegroundWindow();
86  std::wstring caption;
87  std::wstring filename;
88  int len = ::GetWindowTextLength(window) + 1;
89  ::GetWindowText(window, WriteInto(&caption, len), len);
90  bool chrome_window_in_foreground =
91      EndsWith(caption, L" - Google Chrome", true) ||
92      EndsWith(caption, L" - Chromium", true);
93  if (!chrome_window_in_foreground) {
94    DWORD process_id;
95    int thread_id = ::GetWindowThreadProcessId(window, &process_id);
96
97    base::ProcessHandle process;
98    if (base::OpenProcessHandleWithAccess(process_id,
99                                          PROCESS_QUERY_LIMITED_INFORMATION,
100                                          &process)) {
101      len = MAX_PATH;
102      if (!GetProcessImageFileName(process, WriteInto(&filename, len), len)) {
103        int error = GetLastError();
104        filename = std::wstring(L"Unable to read filename for process id '" +
105                                base::IntToString16(process_id) +
106                                L"' (error ") +
107                                base::IntToString16(error) + L")";
108      }
109      base::CloseProcessHandle(process);
110    }
111  }
112  EXPECT_TRUE(chrome_window_in_foreground)
113      << "Chrome must be in the foreground when running interactive tests\n"
114      << "Process in foreground: " << filename.c_str() << "\n"
115      << "Window: " << window << "\n"
116      << "Caption: " << caption.c_str();
117  return chrome_window_in_foreground;
118#else
119  // Windows only at the moment.
120  return true;
121#endif
122}
123
124class BrowserFocusTest : public InProcessBrowserTest {
125 public:
126  BrowserFocusTest() {
127    set_show_window(true);
128    EnableDOMAutomation();
129  }
130
131  bool IsViewFocused(ViewID vid) {
132    return ui_test_utils::IsViewFocused(browser(), vid);
133  }
134
135  void ClickOnView(ViewID vid) {
136    ui_test_utils::ClickOnView(browser(), vid);
137  }
138};
139
140class TestInterstitialPage : public InterstitialPage {
141 public:
142  TestInterstitialPage(TabContents* tab, bool new_navigation, const GURL& url)
143      : InterstitialPage(tab, new_navigation, url) {
144    FilePath file_path;
145    bool r = PathService::Get(chrome::DIR_TEST_DATA, &file_path);
146    EXPECT_TRUE(r);
147    file_path = file_path.AppendASCII("focus");
148    file_path = file_path.AppendASCII(kTypicalPageName);
149    r = file_util::ReadFileToString(file_path, &html_contents_);
150    EXPECT_TRUE(r);
151  }
152
153  virtual std::string GetHTMLContents() {
154    return html_contents_;
155  }
156
157  // Exposing render_view_host() and tab() to be public; they are declared as
158  // protected in the superclass.
159  virtual RenderViewHost* render_view_host() {
160    return InterstitialPage::render_view_host();
161  }
162
163  virtual TabContents* tab() {
164    return InterstitialPage::tab();
165  }
166
167  bool HasFocus() {
168    return render_view_host()->view()->HasFocus();
169  }
170
171 protected:
172  virtual void FocusedNodeChanged(bool is_editable_node) {
173    NotificationService::current()->Notify(
174        NotificationType::FOCUS_CHANGED_IN_PAGE,
175        Source<TabContents>(tab()),
176        Details<const bool>(&is_editable_node));
177  }
178
179 private:
180  std::string html_contents_;
181};
182
183IN_PROC_BROWSER_TEST_F(BrowserFocusTest, ClickingMovesFocus) {
184  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
185#if defined(OS_POSIX)
186  // It seems we have to wait a little bit for the widgets to spin up before
187  // we can start clicking on them.
188  MessageLoop::current()->PostDelayedTask(FROM_HERE,
189                                          new MessageLoop::QuitTask(),
190                                          kActionDelayMs);
191  ui_test_utils::RunMessageLoop();
192#endif  // defined(OS_POSIX)
193
194  ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
195
196  ClickOnView(VIEW_ID_TAB_CONTAINER);
197  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
198
199  ClickOnView(VIEW_ID_LOCATION_BAR);
200  ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
201}
202
203// Flaky, http://crbug.com/69034.
204IN_PROC_BROWSER_TEST_F(BrowserFocusTest, FLAKY_BrowsersRememberFocus) {
205  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
206  ASSERT_TRUE(test_server()->Start());
207
208  // First we navigate to our test page.
209  GURL url = test_server()->GetURL(kSimplePage);
210  ui_test_utils::NavigateToURL(browser(), url);
211
212  gfx::NativeWindow window = browser()->window()->GetNativeHandle();
213
214  // The focus should be on the Tab contents.
215  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
216  // Now hide the window, show it again, the focus should not have changed.
217  ui_test_utils::HideNativeWindow(window);
218  ui_test_utils::ShowAndFocusNativeWindow(window);
219  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
220
221  browser()->FocusLocationBar();
222  ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
223  // Hide the window, show it again, the focus should not have changed.
224  ui_test_utils::HideNativeWindow(window);
225  ui_test_utils::ShowAndFocusNativeWindow(window);
226  ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
227
228  // The rest of this test does not make sense on Linux because the behavior
229  // of Activate() is not well defined and can vary by window manager.
230#if defined(OS_WIN)
231  // Open a new browser window.
232  Browser* browser2 = Browser::Create(browser()->profile());
233  ASSERT_TRUE(browser2);
234  browser2->tabstrip_model()->delegate()->AddBlankTab(true);
235  browser2->window()->Show();
236  ui_test_utils::NavigateToURL(browser2, url);
237
238  HWND hwnd2 = reinterpret_cast<HWND>(browser2->window()->GetNativeHandle());
239  BrowserView* browser_view2 =
240      BrowserView::GetBrowserViewForNativeWindow(hwnd2);
241  ASSERT_TRUE(browser_view2);
242  views::FocusManager* focus_manager2 =
243      views::FocusManager::GetFocusManagerForNativeView(hwnd2);
244  ASSERT_TRUE(focus_manager2);
245  EXPECT_EQ(browser_view2->GetTabContentsContainerView(),
246            focus_manager2->GetFocusedView());
247
248  // Switch to the 1st browser window, focus should still be on the location
249  // bar and the second browser should have nothing focused.
250  browser()->window()->Activate();
251  ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
252  EXPECT_EQ(NULL, focus_manager2->GetFocusedView());
253
254  // Switch back to the second browser, focus should still be on the page.
255  browser2->window()->Activate();
256  EXPECT_EQ(NULL,
257            views::FocusManager::GetFocusManagerForNativeView(
258                browser()->window()->GetNativeHandle())->GetFocusedView());
259  EXPECT_EQ(browser_view2->GetTabContentsContainerView(),
260            focus_manager2->GetFocusedView());
261
262  // Close the 2nd browser to avoid a DCHECK().
263  browser_view2->Close();
264#endif
265}
266
267// Tabs remember focus.
268// Disabled, http://crbug.com/62542.
269IN_PROC_BROWSER_TEST_F(BrowserFocusTest, DISABLED_TabsRememberFocus) {
270  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
271  ASSERT_TRUE(test_server()->Start());
272
273  // First we navigate to our test page.
274  GURL url = test_server()->GetURL(kSimplePage);
275  ui_test_utils::NavigateToURL(browser(), url);
276
277  // Create several tabs.
278  for (int i = 0; i < 4; ++i)
279    browser()->AddSelectedTabWithURL(url, PageTransition::TYPED);
280
281  // Alternate focus for the tab.
282  const bool kFocusPage[3][5] = {
283    { true, true, true, true, false },
284    { false, false, false, false, false },
285    { false, true, false, true, false }
286  };
287
288  for (int i = 1; i < 3; i++) {
289    for (int j = 0; j < 5; j++) {
290      // Activate the tab.
291      browser()->ActivateTabAt(j, true);
292
293      // Activate the location bar or the page.
294      if (kFocusPage[i][j]) {
295        browser()->GetTabContentsAt(j)->view()->Focus();
296      } else {
297        browser()->FocusLocationBar();
298      }
299    }
300
301    // Now come back to the tab and check the right view is focused.
302    for (int j = 0; j < 5; j++) {
303      // Activate the tab.
304      browser()->ActivateTabAt(j, true);
305
306      ViewID vid = kFocusPage[i][j] ? VIEW_ID_TAB_CONTAINER_FOCUS_VIEW :
307                                      VIEW_ID_LOCATION_BAR;
308      ASSERT_TRUE(IsViewFocused(vid));
309    }
310
311    browser()->ActivateTabAt(0, true);
312    // Try the above, but with ctrl+tab. Since tab normally changes focus,
313    // this has regressed in the past. Loop through several times to be sure.
314    for (int j = 0; j < 15; j++) {
315      ViewID vid = kFocusPage[i][j % 5] ? VIEW_ID_TAB_CONTAINER_FOCUS_VIEW :
316                                          VIEW_ID_LOCATION_BAR;
317      ASSERT_TRUE(IsViewFocused(vid));
318
319      ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
320          browser(), ui::VKEY_TAB, true, false, false, false));
321    }
322
323    // As above, but with ctrl+shift+tab.
324    browser()->ActivateTabAt(4, true);
325    for (int j = 14; j >= 0; --j) {
326      ViewID vid = kFocusPage[i][j % 5] ? VIEW_ID_TAB_CONTAINER_FOCUS_VIEW :
327                                          VIEW_ID_LOCATION_BAR;
328      ASSERT_TRUE(IsViewFocused(vid));
329
330      ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
331          browser(), ui::VKEY_TAB, true, true, false, false));
332    }
333  }
334}
335
336// Tabs remember focus with find-in-page box.
337IN_PROC_BROWSER_TEST_F(BrowserFocusTest, MAYBE_TabsRememberFocusFindInPage) {
338  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
339  ASSERT_TRUE(test_server()->Start());
340
341  // First we navigate to our test page.
342  GURL url = test_server()->GetURL(kSimplePage);
343  ui_test_utils::NavigateToURL(browser(), url);
344
345  browser()->Find();
346  ui_test_utils::FindInPage(browser()->GetSelectedTabContentsWrapper(),
347                            ASCIIToUTF16("a"), true, false, NULL);
348  ASSERT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
349
350  // Focus the location bar.
351  browser()->FocusLocationBar();
352
353  // Create a 2nd tab.
354  browser()->AddSelectedTabWithURL(url, PageTransition::TYPED);
355
356  // Focus should be on the recently opened tab page.
357  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
358
359  // Select 1st tab, focus should still be on the location-bar.
360  // (bug http://crbug.com/23296)
361  browser()->ActivateTabAt(0, true);
362  ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
363
364  // Now open the find box again, switch to another tab and come back, the focus
365  // should return to the find box.
366  browser()->Find();
367  ASSERT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
368  browser()->ActivateTabAt(1, true);
369  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
370  browser()->ActivateTabAt(0, true);
371  ASSERT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
372}
373
374// Background window does not steal focus.
375// Flaky, http://crbug.com/62538.
376IN_PROC_BROWSER_TEST_F(BrowserFocusTest,
377                       FLAKY_BackgroundBrowserDontStealFocus) {
378  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
379  ASSERT_TRUE(test_server()->Start());
380
381  // Open a new browser window.
382  Browser* browser2 = Browser::Create(browser()->profile());
383  ASSERT_TRUE(browser2);
384  browser2->tabstrip_model()->delegate()->AddBlankTab(true);
385  browser2->window()->Show();
386
387  Browser* focused_browser = NULL;
388  Browser* unfocused_browser = NULL;
389#if defined(USE_X11)
390  // On X11, calling Activate() is not guaranteed to move focus, so we have
391  // to figure out which browser does have focus.
392  if (browser2->window()->IsActive()) {
393    focused_browser = browser2;
394    unfocused_browser = browser();
395  } else if (browser()->window()->IsActive()) {
396    focused_browser = browser();
397    unfocused_browser = browser2;
398  } else {
399    FAIL() << "Could not determine which browser has focus";
400  }
401#elif defined(OS_WIN)
402  focused_browser = browser();
403  unfocused_browser = browser2;
404#elif defined(OS_MACOSX)
405  // On Mac, the newly created window always gets the focus.
406  focused_browser = browser2;
407  unfocused_browser = browser();
408#endif
409
410  GURL steal_focus_url = test_server()->GetURL(kStealFocusPage);
411  ui_test_utils::NavigateToURL(unfocused_browser, steal_focus_url);
412
413  // Activate the first browser.
414  focused_browser->window()->Activate();
415
416  ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(
417      unfocused_browser->GetSelectedTabContents()->render_view_host(), L"",
418      L"stealFocus();"));
419
420  // Make sure the first browser is still active.
421  EXPECT_TRUE(focused_browser->window()->IsActive());
422}
423
424// Page cannot steal focus when focus is on location bar.
425IN_PROC_BROWSER_TEST_F(BrowserFocusTest, LocationBarLockFocus) {
426  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
427  ASSERT_TRUE(test_server()->Start());
428
429  // Open the page that steals focus.
430  GURL url = test_server()->GetURL(kStealFocusPage);
431  ui_test_utils::NavigateToURL(browser(), url);
432
433  browser()->FocusLocationBar();
434
435  ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(
436      browser()->GetSelectedTabContents()->render_view_host(), L"",
437      L"stealFocus();"));
438
439  // Make sure the location bar is still focused.
440  ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
441}
442
443// Focus traversal on a regular page.
444// Note that this test relies on a notification from the renderer that the
445// focus has changed in the page.  The notification in the renderer may change
446// at which point this test would fail (see comment in
447// RenderWidget::didFocus()).
448IN_PROC_BROWSER_TEST_F(BrowserFocusTest, MAYBE_FocusTraversal) {
449  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
450  ASSERT_TRUE(test_server()->Start());
451
452  // First we navigate to our test page.
453  GURL url = test_server()->GetURL(kTypicalPage);
454  ui_test_utils::NavigateToURL(browser(), url);
455
456  browser()->FocusLocationBar();
457
458  const char* kTextElementID = "textEdit";
459  const char* kExpElementIDs[] = {
460    "",  // Initially no element in the page should be focused
461         // (the location bar is focused).
462    kTextElementID, "searchButton", "luckyButton", "googleLink", "gmailLink",
463    "gmapLink"
464  };
465
466  // Test forward focus traversal.
467  for (int i = 0; i < 3; ++i) {
468    SCOPED_TRACE(StringPrintf("outer loop: %d", i));
469    // Location bar should be focused.
470    ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
471
472    // Move the caret to the end, otherwise the next Tab key may not move focus.
473    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
474        browser(), ui::VKEY_END, false, false, false, false));
475
476    // Now let's press tab to move the focus.
477    for (size_t j = 0; j < arraysize(kExpElementIDs); ++j) {
478      SCOPED_TRACE(StringPrintf("inner loop %" PRIuS, j));
479      // Let's make sure the focus is on the expected element in the page.
480      std::string actual;
481      ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString(
482          browser()->GetSelectedTabContents()->render_view_host(),
483          L"",
484          L"window.domAutomationController.send(getFocusedElement());",
485          &actual));
486      ASSERT_STREQ(kExpElementIDs[j], actual.c_str());
487
488      if (j < arraysize(kExpElementIDs) - 1) {
489        // If the next element is the kTextElementID, we expect to be
490        // notified we have switched to an editable node.
491        bool is_editable_node =
492            (strcmp(kTextElementID, kExpElementIDs[j + 1]) == 0);
493        Details<bool> details(&is_editable_node);
494
495        ASSERT_TRUE(ui_test_utils::SendKeyPressAndWaitWithDetails(
496            browser(), ui::VKEY_TAB, false, false, false, false,
497            NotificationType::FOCUS_CHANGED_IN_PAGE,
498            NotificationSource(Source<TabContents>(
499                browser()->GetSelectedTabContents())),
500            details));
501      } else {
502        // On the last tab key press, the focus returns to the browser.
503        ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
504            browser(), ui::VKEY_TAB, false, false, false, false,
505            NotificationType::FOCUS_RETURNED_TO_BROWSER,
506            NotificationSource(Source<Browser>(browser()))));
507      }
508    }
509
510    // At this point the renderer has sent us a message asking to advance the
511    // focus (as the end of the focus loop was reached in the renderer).
512    // We need to run the message loop to process it.
513    ui_test_utils::RunAllPendingInMessageLoop();
514  }
515
516  // Now let's try reverse focus traversal.
517  for (int i = 0; i < 3; ++i) {
518    SCOPED_TRACE(StringPrintf("outer loop: %d", i));
519    // Location bar should be focused.
520    ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
521
522    // Move the caret to the end, otherwise the next Tab key may not move focus.
523    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
524        browser(), ui::VKEY_END, false, false, false, false));
525
526    // Now let's press shift-tab to move the focus in reverse.
527    for (size_t j = 0; j < arraysize(kExpElementIDs); ++j) {
528      SCOPED_TRACE(StringPrintf("inner loop: %" PRIuS, j));
529      const char* next_element =
530          kExpElementIDs[arraysize(kExpElementIDs) - 1 - j];
531
532      if (j < arraysize(kExpElementIDs) - 1) {
533        // If the next element is the kTextElementID, we expect to be
534        // notified we have switched to an editable node.
535        bool is_editable_node = (strcmp(kTextElementID, next_element) == 0);
536        Details<bool> details(&is_editable_node);
537
538        ASSERT_TRUE(ui_test_utils::SendKeyPressAndWaitWithDetails(
539            browser(), ui::VKEY_TAB, false, true, false, false,
540            NotificationType::FOCUS_CHANGED_IN_PAGE,
541            NotificationSource(Source<TabContents>(
542                browser()->GetSelectedTabContents())),
543            details));
544      } else {
545        // On the last tab key press, the focus returns to the browser.
546        ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
547            browser(), ui::VKEY_TAB, false, true, false, false,
548            NotificationType::FOCUS_RETURNED_TO_BROWSER,
549            NotificationSource(Source<Browser>(browser()))));
550      }
551
552      // Let's make sure the focus is on the expected element in the page.
553      std::string actual;
554      ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString(
555          browser()->GetSelectedTabContents()->render_view_host(),
556          L"",
557          L"window.domAutomationController.send(getFocusedElement());",
558          &actual));
559      ASSERT_STREQ(next_element, actual.c_str());
560    }
561
562    // At this point the renderer has sent us a message asking to advance the
563    // focus (as the end of the focus loop was reached in the renderer).
564    // We need to run the message loop to process it.
565    ui_test_utils::RunAllPendingInMessageLoop();
566  }
567}
568
569// Focus traversal while an interstitial is showing.
570IN_PROC_BROWSER_TEST_F(BrowserFocusTest, MAYBE_FocusTraversalOnInterstitial) {
571  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
572  ASSERT_TRUE(test_server()->Start());
573
574  // First we navigate to our test page.
575  GURL url = test_server()->GetURL(kSimplePage);
576  ui_test_utils::NavigateToURL(browser(), url);
577
578  // Focus should be on the page.
579  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
580
581  // Let's show an interstitial.
582  TestInterstitialPage* interstitial_page =
583      new TestInterstitialPage(browser()->GetSelectedTabContents(),
584                               true, GURL("http://interstitial.com"));
585  interstitial_page->Show();
586  // Give some time for the interstitial to show.
587  MessageLoop::current()->PostDelayedTask(FROM_HERE,
588                                          new MessageLoop::QuitTask(),
589                                          1000);
590  ui_test_utils::RunMessageLoop();
591
592  browser()->FocusLocationBar();
593
594  const char* kExpElementIDs[] = {
595    "",  // Initially no element in the page should be focused
596         // (the location bar is focused).
597    "textEdit", "searchButton", "luckyButton", "googleLink", "gmailLink",
598    "gmapLink"
599  };
600
601  // Test forward focus traversal.
602  for (int i = 0; i < 2; ++i) {
603    // Location bar should be focused.
604    ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
605
606    // Move the caret to the end, otherwise the next Tab key may not move focus.
607    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
608        browser(), ui::VKEY_END, false, false, false, false));
609
610    // Now let's press tab to move the focus.
611    for (size_t j = 0; j < 7; ++j) {
612      // Let's make sure the focus is on the expected element in the page.
613      std::string actual;
614      ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString(
615          interstitial_page->render_view_host(), L"",
616          L"window.domAutomationController.send(getFocusedElement());",
617          &actual));
618      ASSERT_STREQ(kExpElementIDs[j], actual.c_str());
619
620      NotificationType::Type notification_type;
621      NotificationSource notification_source =
622          NotificationService::AllSources();
623      if (j < arraysize(kExpElementIDs) - 1) {
624        notification_type = NotificationType::FOCUS_CHANGED_IN_PAGE;
625        notification_source = Source<TabContents>(
626            interstitial_page->tab());
627      } else {
628        // On the last tab key press, the focus returns to the browser.
629        notification_type = NotificationType::FOCUS_RETURNED_TO_BROWSER;
630        notification_source = Source<Browser>(browser());
631      }
632
633      ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
634          browser(), ui::VKEY_TAB, false, false, false, false,
635          notification_type, notification_source));
636    }
637
638    // At this point the renderer has sent us a message asking to advance the
639    // focus (as the end of the focus loop was reached in the renderer).
640    // We need to run the message loop to process it.
641    ui_test_utils::RunAllPendingInMessageLoop();
642  }
643
644  // Now let's try reverse focus traversal.
645  for (int i = 0; i < 2; ++i) {
646    // Location bar should be focused.
647    ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
648
649    // Move the caret to the end, otherwise the next Tab key may not move focus.
650    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
651        browser(), ui::VKEY_END, false, false, false, false));
652
653    // Now let's press shift-tab to move the focus in reverse.
654    for (size_t j = 0; j < 7; ++j) {
655      NotificationType::Type notification_type;
656      NotificationSource notification_source =
657          NotificationService::AllSources();
658      if (j < arraysize(kExpElementIDs) - 1) {
659        notification_type = NotificationType::FOCUS_CHANGED_IN_PAGE;
660        notification_source = Source<TabContents>(
661            interstitial_page->tab());
662      } else {
663        // On the last tab key press, the focus returns to the browser.
664        notification_type = NotificationType::FOCUS_RETURNED_TO_BROWSER;
665        notification_source = Source<Browser>(browser());
666      }
667
668      ASSERT_TRUE(ui_test_utils::SendKeyPressAndWait(
669          browser(), ui::VKEY_TAB, false, true, false, false,
670          notification_type, notification_source));
671
672      // Let's make sure the focus is on the expected element in the page.
673      std::string actual;
674      ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString(
675          interstitial_page->render_view_host(), L"",
676          L"window.domAutomationController.send(getFocusedElement());",
677          &actual));
678      ASSERT_STREQ(kExpElementIDs[6 - j], actual.c_str());
679    }
680
681    // At this point the renderer has sent us a message asking to advance the
682    // focus (as the end of the focus loop was reached in the renderer).
683    // We need to run the message loop to process it.
684    ui_test_utils::RunAllPendingInMessageLoop();
685  }
686}
687
688// Focus stays on page with interstitials.
689IN_PROC_BROWSER_TEST_F(BrowserFocusTest, InterstitialFocus) {
690  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
691  ASSERT_TRUE(test_server()->Start());
692
693  // First we navigate to our test page.
694  GURL url = test_server()->GetURL(kSimplePage);
695  ui_test_utils::NavigateToURL(browser(), url);
696
697  // Page should have focus.
698  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
699  EXPECT_TRUE(browser()->GetSelectedTabContents()->render_view_host()->view()->
700      HasFocus());
701
702  // Let's show an interstitial.
703  TestInterstitialPage* interstitial_page =
704      new TestInterstitialPage(browser()->GetSelectedTabContents(),
705                               true, GURL("http://interstitial.com"));
706  interstitial_page->Show();
707  // Give some time for the interstitial to show.
708  MessageLoop::current()->PostDelayedTask(FROM_HERE,
709                                          new MessageLoop::QuitTask(),
710                                          1000);
711  ui_test_utils::RunMessageLoop();
712
713  // The interstitial should have focus now.
714  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
715  EXPECT_TRUE(interstitial_page->HasFocus());
716
717  // Hide the interstitial.
718  interstitial_page->DontProceed();
719
720  // Focus should be back on the original page.
721  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
722}
723
724// Make sure Find box can request focus, even when it is already open.
725IN_PROC_BROWSER_TEST_F(BrowserFocusTest, FindFocusTest) {
726  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
727  ASSERT_TRUE(test_server()->Start());
728
729  // Open some page (any page that doesn't steal focus).
730  GURL url = test_server()->GetURL(kTypicalPage);
731  ui_test_utils::NavigateToURL(browser(), url);
732
733  EXPECT_TRUE(ChromeInForeground());
734
735#if defined(OS_MACOSX)
736  // Press Cmd+F, which will make the Find box open and request focus.
737  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
738      browser(), ui::VKEY_F, false, false, false, true));
739#else
740  // Press Ctrl+F, which will make the Find box open and request focus.
741  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
742      browser(), ui::VKEY_F, true, false, false, false));
743#endif
744
745  // Ideally, we wouldn't sleep here and instead would intercept the
746  // RenderViewHostDelegate::HandleKeyboardEvent() callback.  To do that, we
747  // could create a RenderViewHostDelegate wrapper and hook-it up by either:
748  // - creating a factory used to create the delegate
749  // - making the test a private and overwriting the delegate member directly.
750  MessageLoop::current()->PostDelayedTask(
751      FROM_HERE, new MessageLoop::QuitTask(), kActionDelayMs);
752  ui_test_utils::RunMessageLoop();
753
754  ASSERT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
755
756  browser()->FocusLocationBar();
757  ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
758
759  // Now press Ctrl+F again and focus should move to the Find box.
760#if defined(OS_MACOSX)
761  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
762      browser(), ui::VKEY_F, false, false, false, true));
763#else
764  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
765      browser(), ui::VKEY_F, true, false, false, false));
766#endif
767  ASSERT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
768
769  // Set focus to the page.
770  ClickOnView(VIEW_ID_TAB_CONTAINER);
771  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
772
773  // Now press Ctrl+F again and focus should move to the Find box.
774#if defined(OS_MACOSX)
775  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
776      browser(), ui::VKEY_F, false, false, false, true));
777#else
778  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
779      browser(), ui::VKEY_F, true, false, false, false));
780#endif
781
782  // See remark above on why we wait.
783  MessageLoop::current()->PostDelayedTask(
784      FROM_HERE, new MessageLoop::QuitTask(), kActionDelayMs);
785  ui_test_utils::RunMessageLoop();
786  ASSERT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
787}
788
789// Makes sure the focus is in the right location when opening the different
790// types of tabs.
791// Flaky, http://crbug.com/62539.
792IN_PROC_BROWSER_TEST_F(BrowserFocusTest, FLAKY_TabInitialFocus) {
793  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
794
795  // Open the history tab, focus should be on the tab contents.
796  browser()->ShowHistoryTab();
797  ASSERT_NO_FATAL_FAILURE(ui_test_utils::WaitForLoadStop(
798      browser()->GetSelectedTabContents()));
799  EXPECT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
800
801  // Open the new tab, focus should be on the location bar.
802  browser()->NewTab();
803  ASSERT_NO_FATAL_FAILURE(ui_test_utils::WaitForLoadStop(
804      browser()->GetSelectedTabContents()));
805  EXPECT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
806
807  // Open the download tab, focus should be on the tab contents.
808  browser()->ShowDownloadsTab();
809  ASSERT_NO_FATAL_FAILURE(ui_test_utils::WaitForLoadStop(
810      browser()->GetSelectedTabContents()));
811  EXPECT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
812
813  // Open about:blank, focus should be on the location bar.
814  browser()->AddSelectedTabWithURL(GURL(chrome::kAboutBlankURL),
815                                   PageTransition::LINK);
816  ASSERT_NO_FATAL_FAILURE(ui_test_utils::WaitForLoadStop(
817      browser()->GetSelectedTabContents()));
818  EXPECT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
819}
820
821// Tests that focus goes where expected when using reload.
822IN_PROC_BROWSER_TEST_F(BrowserFocusTest, FocusOnReload) {
823  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
824  ASSERT_TRUE(test_server()->Start());
825
826  // Open the new tab, reload.
827  browser()->NewTab();
828  ui_test_utils::RunAllPendingInMessageLoop();
829
830  browser()->Reload(CURRENT_TAB);
831  ASSERT_TRUE(ui_test_utils::WaitForNavigationInCurrentTab(browser()));
832  // Focus should stay on the location bar.
833  ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
834
835  // Open a regular page, focus the location bar, reload.
836  ui_test_utils::NavigateToURL(browser(), test_server()->GetURL(kSimplePage));
837  browser()->FocusLocationBar();
838  ASSERT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
839  browser()->Reload(CURRENT_TAB);
840  ASSERT_TRUE(ui_test_utils::WaitForNavigationInCurrentTab(browser()));
841
842  // Focus should now be on the tab contents.
843  browser()->ShowDownloadsTab();
844  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
845}
846
847// Tests that focus goes where expected when using reload on a crashed tab.
848IN_PROC_BROWSER_TEST_F(BrowserFocusTest, DISABLED_FocusOnReloadCrashedTab) {
849  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
850  ASSERT_TRUE(test_server()->Start());
851
852  // Open a regular page, crash, reload.
853  ui_test_utils::NavigateToURL(browser(), test_server()->GetURL(kSimplePage));
854  ui_test_utils::CrashTab(browser()->GetSelectedTabContents());
855  browser()->Reload(CURRENT_TAB);
856  ASSERT_TRUE(ui_test_utils::WaitForNavigationInCurrentTab(browser()));
857
858  // Focus should now be on the tab contents.
859  browser()->ShowDownloadsTab();
860  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
861}
862
863}  // namespace
864