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