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/basictypes.h"
8#include "base/logging.h"
9#include "base/message_loop.h"
10#include "base/string_util.h"
11#include "base/utf_string_conversions.h"
12#include "base/values.h"
13#include "chrome/browser/dom_operation_notification_details.h"
14#include "chrome/browser/ui/browser.h"
15#include "chrome/common/chrome_paths.h"
16#include "chrome/test/in_process_browser_test.h"
17#include "chrome/test/ui_test_utils.h"
18#include "content/browser/renderer_host/render_view_host.h"
19#include "content/browser/renderer_host/render_widget_host_view.h"
20#include "content/browser/tab_contents/tab_contents.h"
21#include "content/browser/tab_contents/tab_contents_view.h"
22#include "content/common/notification_registrar.h"
23#include "content/common/notification_service.h"
24#include "net/test/test_server.h"
25#include "ui/base/keycodes/keyboard_codes.h"
26
27namespace {
28
29const char kTestingPage[] = "files/keyevents_test.html";
30const wchar_t kSuppressEventJS[] =
31    L"window.domAutomationController.send(setDefaultAction('%ls', %ls));";
32const wchar_t kGetResultJS[] =
33    L"window.domAutomationController.send(keyEventResult[%d]);";
34const wchar_t kGetResultLengthJS[] =
35    L"window.domAutomationController.send(keyEventResult.length);";
36const wchar_t kGetFocusedElementJS[] =
37    L"window.domAutomationController.send(focusedElement);";
38const wchar_t kSetFocusedElementJS[] =
39    L"window.domAutomationController.send(setFocusedElement('%ls'));";
40const wchar_t kGetTextBoxValueJS[] =
41    L"window.domAutomationController.send("
42    L"document.getElementById('%ls').value);";
43const wchar_t kSetTextBoxValueJS[] =
44    L"window.domAutomationController.send("
45    L"document.getElementById('%ls').value = '%ls');";
46const wchar_t kStartTestJS[] =
47    L"window.domAutomationController.send(startTest(%d));";
48
49// Maximum lenght of the result array in KeyEventTestData structure.
50const size_t kMaxResultLength = 10;
51
52// A structure holding test data of a keyboard event.
53// Each keyboard event may generate multiple result strings representing
54// the result of keydown, keypress, keyup and textInput events.
55// For keydown, keypress and keyup events, the format of the result string is:
56// <type> <keyCode> <charCode> <ctrlKey> <shiftKey> <altKey> <commandKey>
57// where <type> may be 'D' (keydown), 'P' (keypress) or 'U' (keyup).
58// <ctrlKey>, <shiftKey> <altKey> and <commandKey> are boolean value indicating
59// the state of corresponding modifier key.
60// For textInput event, the format is: T <text>, where <text> is the text to be
61// input.
62// Please refer to chrome/test/data/keyevents_test.html for details.
63struct KeyEventTestData {
64  ui::KeyboardCode key;
65  bool ctrl;
66  bool shift;
67  bool alt;
68  bool command;
69
70  bool suppress_keydown;
71  bool suppress_keypress;
72  bool suppress_keyup;
73  bool suppress_textinput;
74
75  int result_length;
76  const char* const result[kMaxResultLength];
77};
78
79const wchar_t* GetBoolString(bool value) {
80  return value ? L"true" : L"false";
81}
82
83// A class to help wait for the finish of a key event test.
84class TestFinishObserver : public NotificationObserver {
85 public:
86  explicit TestFinishObserver(RenderViewHost* render_view_host)
87      : finished_(false), waiting_(false) {
88    registrar_.Add(this, NotificationType::DOM_OPERATION_RESPONSE,
89                   Source<RenderViewHost>(render_view_host));
90  }
91
92  bool WaitForFinish() {
93    if (!finished_) {
94      waiting_ = true;
95      ui_test_utils::RunMessageLoop();
96      waiting_ = false;
97    }
98    return finished_;
99  }
100
101  virtual void Observe(NotificationType type,
102                       const NotificationSource& source,
103                       const NotificationDetails& details) {
104    DCHECK(type == NotificationType::DOM_OPERATION_RESPONSE);
105    Details<DomOperationNotificationDetails> dom_op_details(details);
106    // We might receive responses for other script execution, but we only
107    // care about the test finished message.
108    if (dom_op_details->json() == "\"FINISHED\"") {
109      finished_ = true;
110      if (waiting_)
111        MessageLoopForUI::current()->Quit();
112    }
113  }
114
115 private:
116  bool finished_;
117  bool waiting_;
118  NotificationRegistrar registrar_;
119
120  DISALLOW_COPY_AND_ASSIGN(TestFinishObserver);
121};
122
123class BrowserKeyEventsTest : public InProcessBrowserTest {
124 public:
125  BrowserKeyEventsTest() {
126    set_show_window(true);
127    EnableDOMAutomation();
128  }
129
130  bool IsViewFocused(ViewID vid) {
131    return ui_test_utils::IsViewFocused(browser(), vid);
132  }
133
134  void ClickOnView(ViewID vid) {
135    ui_test_utils::ClickOnView(browser(), vid);
136  }
137
138  // Set the suppress flag of an event specified by |type|. If |suppress| is
139  // true then the web page will suppress all events with |type|. Following
140  // event types are supported: keydown, keypress, keyup and textInput.
141  void SuppressEventByType(int tab_index, const wchar_t* type, bool suppress) {
142    ASSERT_LT(tab_index, browser()->tab_count());
143    bool actual;
144    ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
145        browser()->GetTabContentsAt(tab_index)->render_view_host(),
146        L"",
147        StringPrintf(kSuppressEventJS, type, GetBoolString(!suppress)),
148        &actual));
149    ASSERT_EQ(!suppress, actual);
150  }
151
152  void SuppressEvents(int tab_index, bool keydown, bool keypress,
153                      bool keyup, bool textinput) {
154    ASSERT_NO_FATAL_FAILURE(
155        SuppressEventByType(tab_index, L"keydown", keydown));
156    ASSERT_NO_FATAL_FAILURE(
157        SuppressEventByType(tab_index, L"keypress", keypress));
158    ASSERT_NO_FATAL_FAILURE(
159        SuppressEventByType(tab_index, L"keyup", keyup));
160    ASSERT_NO_FATAL_FAILURE(
161        SuppressEventByType(tab_index, L"textInput", textinput));
162  }
163
164  void SuppressAllEvents(int tab_index, bool suppress) {
165    SuppressEvents(tab_index, suppress, suppress, suppress, suppress);
166  }
167
168  void GetResultLength(int tab_index, int* length) {
169    ASSERT_LT(tab_index, browser()->tab_count());
170    ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractInt(
171        browser()->GetTabContentsAt(tab_index)->render_view_host(),
172        L"", kGetResultLengthJS, length));
173  }
174
175  void CheckResult(int tab_index, int length, const char* const result[]) {
176    ASSERT_LT(tab_index, browser()->tab_count());
177    int actual_length;
178    ASSERT_NO_FATAL_FAILURE(GetResultLength(tab_index, &actual_length));
179    ASSERT_GE(actual_length, length);
180    for (int i = 0; i < actual_length; ++i) {
181      std::string actual;
182      ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString(
183          browser()->GetTabContentsAt(tab_index)->render_view_host(),
184          L"", StringPrintf(kGetResultJS, i), &actual));
185
186      // If more events were received than expected, then the additional events
187      // must be keyup events.
188      if (i < length)
189        ASSERT_STREQ(result[i], actual.c_str());
190      else
191        ASSERT_EQ('U', actual[0]);
192    }
193  }
194
195  void CheckFocusedElement(int tab_index, const wchar_t* focused) {
196    ASSERT_LT(tab_index, browser()->tab_count());
197    std::string actual;
198    ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString(
199        browser()->GetTabContentsAt(tab_index)->render_view_host(),
200        L"", kGetFocusedElementJS, &actual));
201    ASSERT_EQ(WideToUTF8(focused), actual);
202  }
203
204  void SetFocusedElement(int tab_index, const wchar_t* focused) {
205    ASSERT_LT(tab_index, browser()->tab_count());
206    bool actual;
207    ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
208        browser()->GetTabContentsAt(tab_index)->render_view_host(),
209        L"",
210        StringPrintf(kSetFocusedElementJS, focused),
211        &actual));
212    ASSERT_TRUE(actual);
213  }
214
215  void CheckTextBoxValue(int tab_index, const wchar_t* id,
216                         const wchar_t* value) {
217    ASSERT_LT(tab_index, browser()->tab_count());
218    std::string actual;
219    ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString(
220        browser()->GetTabContentsAt(tab_index)->render_view_host(),
221        L"",
222        StringPrintf(kGetTextBoxValueJS, id),
223        &actual));
224    ASSERT_EQ(WideToUTF8(value), actual);
225  }
226
227  void SetTextBoxValue(int tab_index, const wchar_t* id,
228                       const wchar_t* value) {
229    ASSERT_LT(tab_index, browser()->tab_count());
230    std::string actual;
231    ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString(
232        browser()->GetTabContentsAt(tab_index)->render_view_host(),
233        L"",
234        StringPrintf(kSetTextBoxValueJS, id, value),
235        &actual));
236    ASSERT_EQ(WideToUTF8(value), actual);
237  }
238
239  void StartTest(int tab_index, int result_length) {
240    ASSERT_LT(tab_index, browser()->tab_count());
241    bool actual;
242    ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
243        browser()->GetTabContentsAt(tab_index)->render_view_host(),
244        L"", StringPrintf(kStartTestJS, result_length), &actual));
245    ASSERT_TRUE(actual);
246  }
247
248  void TestKeyEvent(int tab_index, const KeyEventTestData& test) {
249    ASSERT_LT(tab_index, browser()->tab_count());
250    ASSERT_EQ(tab_index, browser()->active_index());
251
252    // Inform our testing web page that we are about to start testing a key
253    // event.
254    ASSERT_NO_FATAL_FAILURE(StartTest(tab_index, test.result_length));
255    ASSERT_NO_FATAL_FAILURE(SuppressEvents(
256        tab_index, test.suppress_keydown, test.suppress_keypress,
257        test.suppress_keyup, test.suppress_textinput));
258
259    // We need to create a finish observer before sending the key event,
260    // because the test finished message might be arrived before returning
261    // from the SendKeyPressSync() method.
262    TestFinishObserver finish_observer(
263        browser()->GetTabContentsAt(tab_index)->render_view_host());
264
265    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
266        browser(), test.key, test.ctrl, test.shift, test.alt, test.command));
267    ASSERT_TRUE(finish_observer.WaitForFinish());
268    ASSERT_NO_FATAL_FAILURE(CheckResult(
269        tab_index, test.result_length, test.result));
270  }
271
272  std::string GetTestDataDescription(const KeyEventTestData& data) {
273    std::string desc = StringPrintf(
274        " VKEY:0x%02x, ctrl:%d, shift:%d, alt:%d, command:%d\n"
275        " Suppress: keydown:%d, keypress:%d, keyup:%d, textInput:%d\n"
276        " Expected results(%d):\n",
277        data.key, data.ctrl, data.shift, data.alt, data.command,
278        data.suppress_keydown, data.suppress_keypress, data.suppress_keyup,
279        data.suppress_textinput, data.result_length);
280    for (int i = 0; i < data.result_length; ++i) {
281      desc.append("  ");
282      desc.append(data.result[i]);
283      desc.append("\n");
284    }
285    return desc;
286  }
287};
288
289IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, NormalKeyEvents) {
290  static const KeyEventTestData kTestNoInput[] = {
291    // a
292    { ui::VKEY_A, false, false, false, false,
293      false, false, false, false, 3,
294      { "D 65 0 false false false false",
295        "P 97 97 false false false false",
296        "U 65 0 false false false false" } },
297    // shift-a
298    { ui::VKEY_A, false, true, false, false,
299      false, false, false, false, 5,
300      { "D 16 0 false true false false",
301        "D 65 0 false true false false",
302        "P 65 65 false true false false",
303        "U 65 0 false true false false",
304        "U 16 0 false true false false" } },
305    // a, suppress keydown
306    { ui::VKEY_A, false, false, false, false,
307      true, false, false, false, 2,
308      { "D 65 0 false false false false",
309        "U 65 0 false false false false" } },
310  };
311
312  static const KeyEventTestData kTestWithInput[] = {
313    // a
314    { ui::VKEY_A, false, false, false, false,
315      false, false, false, false, 4,
316      { "D 65 0 false false false false",
317        "P 97 97 false false false false",
318        "T a",
319        "U 65 0 false false false false" } },
320    // shift-a
321    { ui::VKEY_A, false, true, false, false,
322      false, false, false, false, 6,
323      { "D 16 0 false true false false",
324        "D 65 0 false true false false",
325        "P 65 65 false true false false",
326        "T A",
327        "U 65 0 false true false false",
328        "U 16 0 false true false false" } },
329    // a, suppress keydown
330    { ui::VKEY_A, false, false, false, false,
331      true, false, false, false, 2,
332      { "D 65 0 false false false false",
333        "U 65 0 false false false false" } },
334    // a, suppress keypress
335    { ui::VKEY_A, false, false, false, false,
336      false, true, false, false, 3,
337      { "D 65 0 false false false false",
338        "P 97 97 false false false false",
339        "U 65 0 false false false false" } },
340    // a, suppress textInput
341    { ui::VKEY_A, false, false, false, false,
342      false, false, false, true, 4,
343      { "D 65 0 false false false false",
344        "P 97 97 false false false false",
345        "T a",
346        "U 65 0 false false false false" } },
347  };
348
349  ASSERT_TRUE(test_server()->Start());
350
351  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
352  GURL url = test_server()->GetURL(kTestingPage);
353  ui_test_utils::NavigateToURL(browser(), url);
354
355  ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
356  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
357
358  int tab_index = browser()->active_index();
359  for (size_t i = 0; i < arraysize(kTestNoInput); ++i) {
360    EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestNoInput[i]))
361        << "kTestNoInput[" << i << "] failed:\n"
362        << GetTestDataDescription(kTestNoInput[i]);
363  }
364
365  // Input in normal text box.
366  ASSERT_NO_FATAL_FAILURE(SetFocusedElement(tab_index, L"A"));
367  for (size_t i = 0; i < arraysize(kTestWithInput); ++i) {
368    EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestWithInput[i]))
369        << "kTestWithInput[" << i << "] in text box failed:\n"
370        << GetTestDataDescription(kTestWithInput[i]);
371  }
372  EXPECT_NO_FATAL_FAILURE(CheckTextBoxValue(tab_index, L"A", L"aA"));
373
374  // Input in password box.
375  ASSERT_NO_FATAL_FAILURE(SetFocusedElement(tab_index, L"B"));
376  for (size_t i = 0; i < arraysize(kTestWithInput); ++i) {
377    EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestWithInput[i]))
378        << "kTestWithInput[" << i << "] in password box failed:\n"
379        << GetTestDataDescription(kTestWithInput[i]);
380  }
381  EXPECT_NO_FATAL_FAILURE(CheckTextBoxValue(tab_index, L"B", L"aA"));
382}
383
384#if defined(OS_WIN) || defined(OS_LINUX)
385IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, CtrlKeyEvents) {
386  static const KeyEventTestData kTestCtrlF = {
387    ui::VKEY_F, true, false, false, false,
388    false, false, false, false, 2,
389    { "D 17 0 true false false false",
390      "D 70 0 true false false false" }
391  };
392
393  static const KeyEventTestData kTestCtrlFSuppressKeyDown = {
394    ui::VKEY_F, true, false, false, false,
395    true, false, false, false, 4,
396    { "D 17 0 true false false false",
397      "D 70 0 true false false false",
398      "U 70 0 true false false false",
399      "U 17 0 true false false false" }
400  };
401
402  // Ctrl+Z doesn't bind to any accelerators, which then should generate a
403  // keypress event with charCode=26.
404  static const KeyEventTestData kTestCtrlZ = {
405    ui::VKEY_Z, true, false, false, false,
406    false, false, false, false, 5,
407    { "D 17 0 true false false false",
408      "D 90 0 true false false false",
409      "P 26 26 true false false false",
410      "U 90 0 true false false false",
411      "U 17 0 true false false false" }
412  };
413
414  static const KeyEventTestData kTestCtrlZSuppressKeyDown = {
415    ui::VKEY_Z, true, false, false, false,
416    true, false, false, false, 4,
417    { "D 17 0 true false false false",
418      "D 90 0 true false false false",
419      "U 90 0 true false false false",
420      "U 17 0 true false false false" }
421  };
422
423  // Ctrl+Enter shall generate a keypress event with charCode=10 (LF).
424  static const KeyEventTestData kTestCtrlEnter = {
425    ui::VKEY_RETURN, true, false, false, false,
426    false, false, false, false, 5,
427    { "D 17 0 true false false false",
428      "D 13 0 true false false false",
429      "P 10 10 true false false false",
430      "U 13 0 true false false false",
431      "U 17 0 true false false false" }
432  };
433
434  ASSERT_TRUE(test_server()->Start());
435
436  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
437  GURL url = test_server()->GetURL(kTestingPage);
438  ui_test_utils::NavigateToURL(browser(), url);
439
440  ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
441  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
442
443  int tab_index = browser()->active_index();
444  // Press Ctrl+F, which will make the Find box open and request focus.
445  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCtrlF));
446  EXPECT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
447
448  // Press Escape to close the Find box and move the focus back to the web page.
449  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
450      browser(), ui::VKEY_ESCAPE, false, false, false, false));
451  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
452
453  // Press Ctrl+F with keydown suppressed shall not open the find box.
454  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCtrlFSuppressKeyDown));
455  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
456
457  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCtrlZ));
458  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCtrlZSuppressKeyDown));
459  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCtrlEnter));
460}
461#elif defined(OS_MACOSX)
462IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, CommandKeyEvents) {
463  static const KeyEventTestData kTestCmdF = {
464    ui::VKEY_F, false, false, false, true,
465    false, false, false, false, 2,
466    { "D 91 0 false false false true",
467      "D 70 0 false false false true" }
468  };
469
470  // On Mac we don't send key up events when command modifier is down.
471  static const KeyEventTestData kTestCmdFSuppressKeyDown = {
472    ui::VKEY_F, false, false, false, true,
473    true, false, false, false, 3,
474    { "D 91 0 false false false true",
475      "D 70 0 false false false true",
476      "U 91 0 false false false true" }
477  };
478
479  ASSERT_TRUE(test_server()->Start());
480
481  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
482  GURL url = test_server()->GetURL(kTestingPage);
483  ui_test_utils::NavigateToURL(browser(), url);
484
485  ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
486  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
487
488  int tab_index = browser()->active_index();
489  // Press Cmd+F, which will make the Find box open and request focus.
490  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCmdF));
491  EXPECT_TRUE(IsViewFocused(VIEW_ID_FIND_IN_PAGE_TEXT_FIELD));
492
493  // Press Escape to close the Find box and move the focus back to the web page.
494  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
495      browser(), ui::VKEY_ESCAPE, false, false, false, false));
496  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
497
498  // Press Cmd+F with keydown suppressed shall not open the find box.
499  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCmdFSuppressKeyDown));
500  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
501}
502#endif
503
504IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, AccessKeys) {
505#if defined(OS_MACOSX)
506  // On Mac, access keys use ctrl+alt modifiers.
507  static const KeyEventTestData kTestAccessA = {
508    ui::VKEY_A, true, false, true, false,
509    false, false, false, false, 6,
510    { "D 17 0 true false false false",
511      "D 18 0 true false true false",
512      "D 65 0 true false true false",
513      "U 65 0 true false true false",
514      "U 18 0 true false true false",
515      "U 17 0 true false false false" }
516  };
517
518  static const KeyEventTestData kTestAccessDSuppress = {
519    ui::VKEY_D, true, false, true, false,
520    true, true, true, false, 6,
521    { "D 17 0 true false false false",
522      "D 18 0 true false true false",
523      "D 68 0 true false true false",
524      "U 68 0 true false true false",
525      "U 18 0 true false true false",
526      "U 17 0 true false false false" }
527  };
528
529  static const KeyEventTestData kTestAccess1 = {
530    ui::VKEY_1, true, false, true, false,
531    false, false, false, false, 6,
532    { "D 17 0 true false false false",
533      "D 18 0 true false true false",
534      "D 49 0 true false true false",
535      "U 49 0 true false true false",
536      "U 18 0 true false true false",
537      "U 17 0 true false false false" }
538  };
539#else
540  static const KeyEventTestData kTestAccessA = {
541    ui::VKEY_A, false, false, true, false,
542    false, false, false, false, 4,
543    { "D 18 0 false false true false",
544      "D 65 0 false false true false",
545      "U 65 0 false false true false",
546      "U 18 0 false false true false" }
547  };
548
549  static const KeyEventTestData kTestAccessD = {
550    ui::VKEY_D, false, false, true, false,
551    false, false, false, false, 2,
552    { "D 18 0 false false true false",
553      "D 68 0 false false true false" }
554  };
555
556  static const KeyEventTestData kTestAccessDSuppress = {
557    ui::VKEY_D, false, false, true, false,
558    true, true, true, false, 4,
559    { "D 18 0 false false true false",
560      "D 68 0 false false true false",
561      "U 68 0 false false true false",
562      "U 18 0 false false true false" }
563  };
564
565  static const KeyEventTestData kTestAccess1 = {
566    ui::VKEY_1, false, false, true, false,
567    false, false, false, false, 4,
568    { "D 18 0 false false true false",
569      "D 49 0 false false true false",
570      "U 49 0 false false true false",
571      "U 18 0 false false true false" }
572  };
573#endif
574
575  ASSERT_TRUE(test_server()->Start());
576
577  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
578  GURL url = test_server()->GetURL(kTestingPage);
579  ui_test_utils::NavigateToURL(browser(), url);
580
581  ui_test_utils::RunAllPendingInMessageLoop();
582  ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
583  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
584
585  int tab_index = browser()->active_index();
586  // Make sure no element is focused.
587  EXPECT_NO_FATAL_FAILURE(CheckFocusedElement(tab_index, L""));
588  // Alt+A should focus the element with accesskey = "A".
589  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestAccessA));
590  EXPECT_NO_FATAL_FAILURE(CheckFocusedElement(tab_index, L"A"));
591
592  // Blur the focused element.
593  EXPECT_NO_FATAL_FAILURE(SetFocusedElement(tab_index, L""));
594  // Make sure no element is focused.
595  EXPECT_NO_FATAL_FAILURE(CheckFocusedElement(tab_index, L""));
596
597#if !defined(OS_MACOSX)
598  // Alt+D should move the focus to the location entry.
599  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestAccessD));
600
601  // TODO(isherman): This is an experimental change to help diagnose
602  // http://crbug.com/55713
603  ui_test_utils::RunAllPendingInMessageLoop();
604
605  EXPECT_TRUE(IsViewFocused(VIEW_ID_LOCATION_BAR));
606  // No element should be focused, as Alt+D was handled by the browser.
607  EXPECT_NO_FATAL_FAILURE(CheckFocusedElement(tab_index, L""));
608
609  // Move the focus back to the web page.
610  ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
611  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
612
613  // Make sure no element is focused.
614  EXPECT_NO_FATAL_FAILURE(CheckFocusedElement(tab_index, L""));
615#endif
616
617  // If the keydown event is suppressed, then Alt+D should be handled as an
618  // accesskey rather than an accelerator key. Activation of an accesskey is not
619  // a part of the default action of the key event, so it should not be
620  // suppressed at all.
621  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestAccessDSuppress));
622  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
623  EXPECT_NO_FATAL_FAILURE(CheckFocusedElement(tab_index, L"D"));
624
625  // Blur the focused element.
626  EXPECT_NO_FATAL_FAILURE(SetFocusedElement(tab_index, L""));
627  // Make sure no element is focused.
628  EXPECT_NO_FATAL_FAILURE(CheckFocusedElement(tab_index, L""));
629  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestAccess1));
630#if defined(TOOLKIT_GTK)
631  // On GTK, alt-0..9 are assigned as tab selection accelerators, so they can
632  // not be used as accesskeys.
633  EXPECT_NO_FATAL_FAILURE(CheckFocusedElement(tab_index, L""));
634#else
635  EXPECT_NO_FATAL_FAILURE(CheckFocusedElement(tab_index, L"1"));
636#endif
637}
638
639// Disabled, http://crbug.com/69475.
640IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, DISABLED_ReservedAccelerators) {
641  ASSERT_TRUE(test_server()->Start());
642
643  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
644  GURL url = test_server()->GetURL(kTestingPage);
645  ui_test_utils::NavigateToURL(browser(), url);
646
647  ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
648  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
649
650  ASSERT_EQ(1, browser()->tab_count());
651
652  static const KeyEventTestData kTestCtrlOrCmdT = {
653#if defined(OS_MACOSX)
654    ui::VKEY_T, false, false, false, true,
655    true, false, false, false, 1,
656    { "D 91 0 false false false true" }
657#else
658    ui::VKEY_T, true, false, false, false,
659    true, false, false, false, 1,
660    { "D 17 0 true false false false" }
661#endif
662  };
663
664  ui_test_utils::WindowedNotificationObserver wait_for_new_tab(
665      NotificationType::TAB_PARENTED,
666      NotificationService::AllSources());
667
668  // Press Ctrl/Cmd+T, which will open a new tab. It cannot be suppressed.
669  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(0, kTestCtrlOrCmdT));
670
671  ASSERT_NO_FATAL_FAILURE(
672      wait_for_new_tab.WaitFor(Source<NavigationController>(
673      &browser()->GetTabContentsAt(1)->controller())));
674
675  int result_length;
676  ASSERT_NO_FATAL_FAILURE(GetResultLength(0, &result_length));
677  EXPECT_EQ(1, result_length);
678
679  EXPECT_EQ(2, browser()->tab_count());
680  ASSERT_EQ(1, browser()->active_index());
681
682  // Because of issue http://crbug.com/65375, switching back to the first tab
683  // may cause the focus to be grabbed by omnibox. So instead, we load our
684  // testing page in the newly created tab and try Cmd-W here.
685  ui_test_utils::NavigateToURL(browser(), url);
686
687  // Make sure the focus is in the testing page.
688  ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
689  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
690
691  // Reserved accelerators can't be suppressed.
692  ASSERT_NO_FATAL_FAILURE(SuppressAllEvents(1, true));
693
694  ui_test_utils::WindowedNotificationObserver wait_for_tab_closed(
695      NotificationType::TAB_CLOSED, Source<NavigationController>(
696          &browser()->GetTabContentsAt(1)->controller()));
697
698  // Press Ctrl/Cmd+W, which will close the tab.
699#if defined(OS_MACOSX)
700  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
701      browser(), ui::VKEY_W, false, false, false, true));
702#else
703  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
704      browser(), ui::VKEY_W, true, false, false, false));
705#endif
706
707  ASSERT_NO_FATAL_FAILURE(wait_for_tab_closed.Wait());
708
709  EXPECT_EQ(1, browser()->tab_count());
710}
711
712#if defined(OS_MACOSX)
713IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, EditorKeyBindings) {
714  static const KeyEventTestData kTestCtrlA = {
715    ui::VKEY_A, true, false, false, false,
716    false, false, false, false, 4,
717    { "D 17 0 true false false false",
718      "D 65 0 true false false false",
719      "U 65 0 true false false false",
720      "U 17 0 true false false false" }
721  };
722
723  static const KeyEventTestData kTestCtrlF = {
724    ui::VKEY_F, true, false, false, false,
725    false, false, false, false, 4,
726    { "D 17 0 true false false false",
727      "D 70 0 true false false false",
728      "U 70 0 true false false false",
729      "U 17 0 true false false false" }
730  };
731
732  static const KeyEventTestData kTestCtrlK = {
733    ui::VKEY_K, true, false, false, false,
734    false, false, false, false, 4,
735    { "D 17 0 true false false false",
736      "D 75 0 true false false false",
737      "U 75 0 true false false false",
738      "U 17 0 true false false false" }
739  };
740
741  ASSERT_TRUE(test_server()->Start());
742
743  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
744  GURL url = test_server()->GetURL(kTestingPage);
745  ui_test_utils::NavigateToURL(browser(), url);
746
747  ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
748  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
749
750  int tab_index = browser()->active_index();
751  ASSERT_NO_FATAL_FAILURE(SetFocusedElement(tab_index, L"A"));
752  ASSERT_NO_FATAL_FAILURE(SetTextBoxValue(tab_index, L"A", L"Hello"));
753  // Move the caret to the beginning of the line.
754  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCtrlA));
755  // Forward one character
756  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCtrlF));
757  // Delete to the end of the line.
758  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCtrlK));
759  EXPECT_NO_FATAL_FAILURE(CheckTextBoxValue(tab_index, L"A", L"H"));
760}
761#endif
762
763IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, PageUpDownKeys) {
764  static const KeyEventTestData kTestPageUp = {
765    ui::VKEY_PRIOR, false, false, false, false,
766    false, false, false, false, 2,
767    { "D 33 0 false false false false",
768      "U 33 0 false false false false" }
769  };
770
771  static const KeyEventTestData kTestPageDown = {
772    ui::VKEY_NEXT, false, false, false, false,
773    false, false, false, false, 2,
774    { "D 34 0 false false false false",
775      "U 34 0 false false false false" }
776  };
777
778  ASSERT_TRUE(test_server()->Start());
779
780  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
781  GURL url = test_server()->GetURL(kTestingPage);
782  ui_test_utils::NavigateToURL(browser(), url);
783
784  ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
785  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
786
787  int tab_index = browser()->active_index();
788  ASSERT_NO_FATAL_FAILURE(SetFocusedElement(tab_index, L"A"));
789  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestPageUp));
790  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestPageDown));
791  EXPECT_NO_FATAL_FAILURE(CheckTextBoxValue(tab_index, L"A", L""));
792}
793
794#if defined(OS_WIN) || defined(TOOLKIT_VIEWS)
795IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, FocusMenuBarByAltKey) {
796  static const KeyEventTestData kTestAltKey = {
797    ui::VKEY_MENU, false, false, false, false,
798    false, false, false, false, 2,
799    { "D 18 0 false false true false",
800      "U 18 0 false false true false" }
801  };
802
803  static const KeyEventTestData kTestAltKeySuppress = {
804    ui::VKEY_MENU, false, false, false, false,
805    true, false, false, false, 2,
806    { "D 18 0 false false true false",
807      "U 18 0 false false true false" }
808  };
809
810  static const KeyEventTestData kTestCtrlAltKey = {
811    ui::VKEY_MENU, true, false, false, false,
812    false, false, false, false, 4,
813    { "D 17 0 true false false false",
814      "D 18 0 true false true false",
815      "U 18 0 true false true false",
816      "U 17 0 true false false false" }
817  };
818
819  ASSERT_TRUE(test_server()->Start());
820
821  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
822  GURL url = test_server()->GetURL(kTestingPage);
823  ui_test_utils::NavigateToURL(browser(), url);
824
825  ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
826  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
827
828  int tab_index = browser()->active_index();
829  // Press and release Alt key to focus wrench menu button.
830  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestAltKey));
831  EXPECT_TRUE(IsViewFocused(VIEW_ID_APP_MENU));
832
833  ASSERT_NO_FATAL_FAILURE(ClickOnView(VIEW_ID_TAB_CONTAINER));
834  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
835
836  // Alt key can be suppressed.
837  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestAltKeySuppress));
838  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
839
840  // Ctrl+Alt should have no effect.
841  EXPECT_NO_FATAL_FAILURE(TestKeyEvent(tab_index, kTestCtrlAltKey));
842  ASSERT_TRUE(IsViewFocused(VIEW_ID_TAB_CONTAINER_FOCUS_VIEW));
843}
844#endif
845
846}  // namespace
847