Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / views / keyboard_access_browsertest.cc
blob4cdb782809ce24764b93347f4c4430c298902d1e
1 // Copyright (c) 2012 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.
5 // This functionality currently works on Windows and on Linux when
6 // toolkit_views is defined (i.e. for Chrome OS). It's not needed
7 // on the Mac, and it's not yet implemented on Linux.
9 #include "base/memory/weak_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/string_util.h"
12 #include "base/time/time.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_window.h"
16 #include "chrome/browser/ui/tabs/tab_strip_model.h"
17 #include "chrome/browser/ui/views/frame/browser_view.h"
18 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
19 #include "chrome/test/base/in_process_browser_test.h"
20 #include "chrome/test/base/interactive_test_utils.h"
21 #include "chrome/test/base/ui_test_utils.h"
22 #include "ui/base/test/ui_controls.h"
23 #include "ui/events/event_constants.h"
24 #include "ui/events/keycodes/keyboard_codes.h"
25 #include "ui/views/controls/menu/menu_listener.h"
26 #include "ui/views/focus/focus_manager.h"
27 #include "ui/views/view.h"
28 #include "ui/views/widget/widget.h"
30 namespace {
32 // An async version of SendKeyPressSync since we don't get notified when a
33 // menu is showing.
34 void SendKeyPress(Browser* browser, ui::KeyboardCode key) {
35 ASSERT_TRUE(ui_controls::SendKeyPress(
36 browser->window()->GetNativeWindow(), key, false, false, false, false));
39 // Helper class that waits until the focus has changed to a view other
40 // than the one with the provided view id.
41 class ViewFocusChangeWaiter : public views::FocusChangeListener {
42 public:
43 ViewFocusChangeWaiter(views::FocusManager* focus_manager,
44 int previous_view_id)
45 : focus_manager_(focus_manager),
46 previous_view_id_(previous_view_id),
47 weak_factory_(this) {
48 focus_manager_->AddFocusChangeListener(this);
49 // Call the focus change notification once in case the focus has
50 // already changed.
51 OnWillChangeFocus(NULL, focus_manager_->GetFocusedView());
54 virtual ~ViewFocusChangeWaiter() {
55 focus_manager_->RemoveFocusChangeListener(this);
58 void Wait() {
59 content::RunMessageLoop();
62 private:
63 // Inherited from FocusChangeListener
64 virtual void OnWillChangeFocus(views::View* focused_before,
65 views::View* focused_now) OVERRIDE {
68 virtual void OnDidChangeFocus(views::View* focused_before,
69 views::View* focused_now) OVERRIDE {
70 if (focused_now && focused_now->id() != previous_view_id_) {
71 base::MessageLoop::current()->PostTask(FROM_HERE,
72 base::MessageLoop::QuitClosure());
76 views::FocusManager* focus_manager_;
77 int previous_view_id_;
78 base::WeakPtrFactory<ViewFocusChangeWaiter> weak_factory_;
80 DISALLOW_COPY_AND_ASSIGN(ViewFocusChangeWaiter);
83 class SendKeysMenuListener : public views::MenuListener {
84 public:
85 SendKeysMenuListener(ToolbarView* toolbar_view,
86 Browser* browser,
87 bool test_dismiss_menu)
88 : toolbar_view_(toolbar_view), browser_(browser), menu_open_count_(0),
89 test_dismiss_menu_(test_dismiss_menu) {
90 toolbar_view_->AddMenuListener(this);
93 virtual ~SendKeysMenuListener() {
94 if (test_dismiss_menu_)
95 toolbar_view_->RemoveMenuListener(this);
98 int menu_open_count() const {
99 return menu_open_count_;
102 private:
103 // Overridden from views::MenuListener:
104 virtual void OnMenuOpened() OVERRIDE {
105 menu_open_count_++;
106 if (!test_dismiss_menu_) {
107 toolbar_view_->RemoveMenuListener(this);
108 // Press DOWN to select the first item, then RETURN to select it.
109 SendKeyPress(browser_, ui::VKEY_DOWN);
110 SendKeyPress(browser_, ui::VKEY_RETURN);
111 } else {
112 SendKeyPress(browser_, ui::VKEY_ESCAPE);
113 base::MessageLoop::current()->PostDelayedTask(
114 FROM_HERE,
115 base::MessageLoop::QuitClosure(),
116 base::TimeDelta::FromMilliseconds(200));
120 ToolbarView* toolbar_view_;
121 Browser* browser_;
122 // Keeps track of the number of times the menu was opened.
123 int menu_open_count_;
124 // If this is set then on receiving a notification that the menu was opened
125 // we dismiss it by sending the ESC key.
126 bool test_dismiss_menu_;
128 DISALLOW_COPY_AND_ASSIGN(SendKeysMenuListener);
131 class KeyboardAccessTest : public InProcessBrowserTest {
132 public:
133 KeyboardAccessTest() {}
135 // Use the keyboard to select "New Tab" from the app menu.
136 // This test depends on the fact that there is one menu and that
137 // New Tab is the first item in the menu. If the menus change,
138 // this test will need to be changed to reflect that.
140 // If alternate_key_sequence is true, use "Alt" instead of "F10" to
141 // open the menu bar, and "Down" instead of "Enter" to open a menu.
142 // If focus_omnibox is true then the test on startup sets focus to the
143 // omnibox.
144 void TestMenuKeyboardAccess(bool alternate_key_sequence,
145 bool shift,
146 bool focus_omnibox);
148 int GetFocusedViewID() {
149 gfx::NativeWindow window = browser()->window()->GetNativeWindow();
150 views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
151 const views::FocusManager* focus_manager = widget->GetFocusManager();
152 const views::View* focused_view = focus_manager->GetFocusedView();
153 return focused_view ? focused_view->id() : -1;
156 void WaitForFocusedViewIDToChange(int original_view_id) {
157 if (GetFocusedViewID() != original_view_id)
158 return;
159 gfx::NativeWindow window = browser()->window()->GetNativeWindow();
160 views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window);
161 views::FocusManager* focus_manager = widget->GetFocusManager();
162 ViewFocusChangeWaiter waiter(focus_manager, original_view_id);
163 waiter.Wait();
166 #if defined(OS_WIN)
167 // Opens the system menu on Windows with the Alt Space combination and selects
168 // the New Tab option from the menu.
169 void TestSystemMenuWithKeyboard();
170 #endif
172 #if defined(USE_AURA)
173 // Uses the keyboard to select the wrench menu i.e. with the F10 key.
174 // It verifies that the menu when dismissed by sending the ESC key it does
175 // not display twice.
176 void TestMenuKeyboardAccessAndDismiss();
177 #endif
179 DISALLOW_COPY_AND_ASSIGN(KeyboardAccessTest);
182 void KeyboardAccessTest::TestMenuKeyboardAccess(bool alternate_key_sequence,
183 bool shift,
184 bool focus_omnibox) {
185 // Navigate to a page in the first tab, which makes sure that focus is
186 // set to the browser window.
187 ui_test_utils::NavigateToURL(browser(), GURL("about:"));
189 // The initial tab index should be 0.
190 ASSERT_EQ(0, browser()->tab_strip_model()->active_index());
192 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
194 // Get the focused view ID, then press a key to activate the
195 // page menu, then wait until the focused view changes.
196 int original_view_id = GetFocusedViewID();
198 content::WindowedNotificationObserver new_tab_observer(
199 chrome::NOTIFICATION_TAB_ADDED,
200 content::Source<content::WebContentsDelegate>(browser()));
202 BrowserView* browser_view = reinterpret_cast<BrowserView*>(
203 browser()->window());
204 ToolbarView* toolbar_view = browser_view->GetToolbarView();
205 SendKeysMenuListener menu_listener(toolbar_view, browser(), false);
207 if (focus_omnibox)
208 browser()->window()->GetLocationBar()->FocusLocation(false);
210 #if defined(OS_CHROMEOS)
211 // Chrome OS doesn't have a way to just focus the wrench menu, so we use Alt+F
212 // to bring up the menu.
213 ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
214 browser(), ui::VKEY_F, false, shift, true, false));
215 #else
216 ui::KeyboardCode menu_key =
217 alternate_key_sequence ? ui::VKEY_MENU : ui::VKEY_F10;
218 ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
219 browser(), menu_key, false, shift, false, false));
220 #endif
222 if (shift) {
223 // Verify Chrome does not move the view focus. We should not move the view
224 // focus when typing a menu key with modifier keys, such as shift keys or
225 // control keys.
226 int new_view_id = GetFocusedViewID();
227 ASSERT_EQ(original_view_id, new_view_id);
228 return;
231 WaitForFocusedViewIDToChange(original_view_id);
233 // See above comment. Since we already brought up the menu, no need to do this
234 // on ChromeOS.
235 #if !defined(OS_CHROMEOS)
236 if (alternate_key_sequence)
237 SendKeyPress(browser(), ui::VKEY_DOWN);
238 else
239 SendKeyPress(browser(), ui::VKEY_RETURN);
240 #endif
242 // Wait for the new tab to appear.
243 new_tab_observer.Wait();
245 // Make sure that the new tab index is 1.
246 ASSERT_EQ(1, browser()->tab_strip_model()->active_index());
249 #if defined(OS_WIN)
251 // This CBT hook is set for the duration of the TestSystemMenuWithKeyboard test
252 LRESULT CALLBACK SystemMenuTestCBTHook(int n_code,
253 WPARAM w_param,
254 LPARAM l_param) {
255 // Look for the system menu window getting created or becoming visible and
256 // then select the New Tab option from the menu.
257 if (n_code == HCBT_ACTIVATE || n_code == HCBT_CREATEWND) {
258 wchar_t class_name[MAX_PATH] = {0};
259 GetClassName(reinterpret_cast<HWND>(w_param),
260 class_name,
261 arraysize(class_name));
262 if (LowerCaseEqualsASCII(class_name, "#32768")) {
263 // Select the New Tab option and then send the enter key to execute it.
264 ::PostMessage(reinterpret_cast<HWND>(w_param), WM_CHAR, 'T', 0);
265 ::PostMessage(reinterpret_cast<HWND>(w_param), WM_KEYDOWN, VK_RETURN, 0);
266 ::PostMessage(reinterpret_cast<HWND>(w_param), WM_KEYUP, VK_RETURN, 0);
269 return ::CallNextHookEx(0, n_code, w_param, l_param);
272 void KeyboardAccessTest::TestSystemMenuWithKeyboard() {
273 // Navigate to a page in the first tab, which makes sure that focus is
274 // set to the browser window.
275 ui_test_utils::NavigateToURL(browser(), GURL("about:"));
277 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
279 content::WindowedNotificationObserver new_tab_observer(
280 chrome::NOTIFICATION_TAB_ADDED,
281 content::Source<content::WebContentsDelegate>(browser()));
282 // Sending the Alt space keys to the browser will bring up the system menu
283 // which runs a model loop. We set a CBT hook to look for the menu and send
284 // keystrokes to it.
285 HHOOK cbt_hook = ::SetWindowsHookEx(WH_CBT,
286 SystemMenuTestCBTHook,
287 NULL,
288 ::GetCurrentThreadId());
289 ASSERT_TRUE(cbt_hook != NULL);
291 bool ret = ui_test_utils::SendKeyPressSync(
292 browser(), ui::VKEY_SPACE, false, false, true, false);
293 EXPECT_TRUE(ret);
295 if (ret) {
296 // Wait for the new tab to appear.
297 new_tab_observer.Wait();
298 // Make sure that the new tab index is 1.
299 ASSERT_EQ(1, browser()->tab_strip_model()->active_index());
301 ::UnhookWindowsHookEx(cbt_hook);
303 #endif
305 #if defined(USE_AURA)
306 void KeyboardAccessTest::TestMenuKeyboardAccessAndDismiss() {
307 ui_test_utils::NavigateToURL(browser(), GURL("about:"));
309 ASSERT_EQ(0, browser()->tab_strip_model()->active_index());
311 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
313 int original_view_id = GetFocusedViewID();
315 BrowserView* browser_view = reinterpret_cast<BrowserView*>(
316 browser()->window());
317 ToolbarView* toolbar_view = browser_view->GetToolbarView();
318 SendKeysMenuListener menu_listener(toolbar_view, browser(), true);
320 browser()->window()->GetLocationBar()->FocusLocation(false);
322 ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
323 browser(), ui::VKEY_F10, false, false, false, false));
325 WaitForFocusedViewIDToChange(original_view_id);
327 SendKeyPress(browser(), ui::VKEY_DOWN);
328 content::RunMessageLoop();
329 ASSERT_EQ(1, menu_listener.menu_open_count());
331 #endif
333 // http://crbug.com/62310.
334 #if defined(OS_CHROMEOS)
335 #define MAYBE_TestMenuKeyboardAccess DISABLED_TestMenuKeyboardAccess
336 #else
337 #define MAYBE_TestMenuKeyboardAccess TestMenuKeyboardAccess
338 #endif
340 IN_PROC_BROWSER_TEST_F(KeyboardAccessTest, MAYBE_TestMenuKeyboardAccess) {
341 TestMenuKeyboardAccess(false, false, false);
344 // http://crbug.com/62310.
345 #if defined(OS_CHROMEOS)
346 #define MAYBE_TestAltMenuKeyboardAccess DISABLED_TestAltMenuKeyboardAccess
347 #else
348 #define MAYBE_TestAltMenuKeyboardAccess TestAltMenuKeyboardAccess
349 #endif
351 IN_PROC_BROWSER_TEST_F(KeyboardAccessTest, MAYBE_TestAltMenuKeyboardAccess) {
352 TestMenuKeyboardAccess(true, false, false);
355 // If this flakes, use http://crbug.com/62311.
356 #if defined(OS_WIN)
357 #define MAYBE_TestShiftAltMenuKeyboardAccess DISABLED_TestShiftAltMenuKeyboardAccess
358 #else
359 #define MAYBE_TestShiftAltMenuKeyboardAccess TestShiftAltMenuKeyboardAccess
360 #endif
361 IN_PROC_BROWSER_TEST_F(KeyboardAccessTest,
362 MAYBE_TestShiftAltMenuKeyboardAccess) {
363 TestMenuKeyboardAccess(true, true, false);
366 #if defined(OS_WIN)
367 IN_PROC_BROWSER_TEST_F(KeyboardAccessTest,
368 DISABLED_TestAltMenuKeyboardAccessFocusOmnibox) {
369 TestMenuKeyboardAccess(true, false, true);
372 IN_PROC_BROWSER_TEST_F(KeyboardAccessTest,
373 DISABLED_TestSystemMenuWithKeyboard) {
374 TestSystemMenuWithKeyboard();
376 #endif
378 #if !defined(OS_WIN) && defined(USE_AURA)
379 IN_PROC_BROWSER_TEST_F(KeyboardAccessTest, TestMenuKeyboardOpenDismiss) {
380 TestMenuKeyboardAccessAndDismiss();
382 #endif
384 // Test that JavaScript cannot intercept reserved keyboard accelerators like
385 // ctrl-t to open a new tab or ctrl-f4 to close a tab.
386 // TODO(isherman): This test times out on ChromeOS. We should merge it with
387 // BrowserKeyEventsTest.ReservedAccelerators, but just disable for now.
388 // If this flakes, use http://crbug.com/62311.
389 IN_PROC_BROWSER_TEST_F(KeyboardAccessTest, ReserveKeyboardAccelerators) {
390 const std::string kBadPage =
391 "<html><script>"
392 "document.onkeydown = function() {"
393 " event.preventDefault();"
394 " return false;"
396 "</script></html>";
397 GURL url("data:text/html," + kBadPage);
398 ui_test_utils::NavigateToURLWithDisposition(
399 browser(), url, NEW_FOREGROUND_TAB,
400 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
402 ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
403 browser(), ui::VKEY_TAB, true, false, false, false));
404 ASSERT_EQ(0, browser()->tab_strip_model()->active_index());
406 ui_test_utils::NavigateToURLWithDisposition(
407 browser(), url, NEW_FOREGROUND_TAB,
408 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
409 ASSERT_EQ(2, browser()->tab_strip_model()->active_index());
411 ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
412 browser(), ui::VKEY_W, true, false, false, false));
413 ASSERT_EQ(0, browser()->tab_strip_model()->active_index());
416 #if defined(OS_WIN) // These keys are Windows-only.
417 IN_PROC_BROWSER_TEST_F(KeyboardAccessTest, BackForwardKeys) {
418 // Navigate to create some history.
419 ui_test_utils::NavigateToURL(browser(), GURL("chrome://version/"));
420 ui_test_utils::NavigateToURL(browser(), GURL("chrome://about/"));
422 base::string16 before_back;
423 ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &before_back));
425 // Navigate back.
426 ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
427 browser(), ui::VKEY_BROWSER_BACK, false, false, false, false));
429 base::string16 after_back;
430 ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &after_back));
432 EXPECT_NE(before_back, after_back);
434 // And then forward.
435 ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
436 browser(), ui::VKEY_BROWSER_FORWARD, false, false, false, false));
438 base::string16 after_forward;
439 ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &after_forward));
441 EXPECT_EQ(before_back, after_forward);
443 #endif
445 } // namespace