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
70 namespace {
71
72 // The delay waited in some cases where we don't have a notifications for an
73 // action we take.
74 const int kActionDelayMs = 500;
75
76 const char kSimplePage[] = "files/focus/page_with_focus.html";
77 const char kStealFocusPage[] = "files/focus/page_steals_focus.html";
78 const char kTypicalPage[] = "files/focus/typical_page.html";
79 const 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.
ChromeInForeground()83 bool 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
124 class BrowserFocusTest : public InProcessBrowserTest {
125 public:
BrowserFocusTest()126 BrowserFocusTest() {
127 set_show_window(true);
128 EnableDOMAutomation();
129 }
130
IsViewFocused(ViewID vid)131 bool IsViewFocused(ViewID vid) {
132 return ui_test_utils::IsViewFocused(browser(), vid);
133 }
134
ClickOnView(ViewID vid)135 void ClickOnView(ViewID vid) {
136 ui_test_utils::ClickOnView(browser(), vid);
137 }
138 };
139
140 class TestInterstitialPage : public InterstitialPage {
141 public:
TestInterstitialPage(TabContents * tab,bool new_navigation,const GURL & url)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
GetHTMLContents()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.
render_view_host()159 virtual RenderViewHost* render_view_host() {
160 return InterstitialPage::render_view_host();
161 }
162
tab()163 virtual TabContents* tab() {
164 return InterstitialPage::tab();
165 }
166
HasFocus()167 bool HasFocus() {
168 return render_view_host()->view()->HasFocus();
169 }
170
171 protected:
FocusedNodeChanged(bool is_editable_node)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
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,ClickingMovesFocus)183 IN_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.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,FLAKY_BrowsersRememberFocus)204 IN_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.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,DISABLED_TabsRememberFocus)269 IN_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.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,MAYBE_TabsRememberFocusFindInPage)337 IN_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.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,FLAKY_BackgroundBrowserDontStealFocus)376 IN_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.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,LocationBarLockFocus)425 IN_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()).
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,MAYBE_FocusTraversal)448 IN_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.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,MAYBE_FocusTraversalOnInterstitial)570 IN_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.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,InterstitialFocus)689 IN_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.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,FindFocusTest)725 IN_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.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,FLAKY_TabInitialFocus)792 IN_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.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,FocusOnReload)822 IN_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.
IN_PROC_BROWSER_TEST_F(BrowserFocusTest,DISABLED_FocusOnReloadCrashedTab)848 IN_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