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 "base/command_line.h"
6#include "chrome/browser/extensions/active_tab_permission_granter.h"
7#include "chrome/browser/extensions/api/commands/command_service.h"
8#include "chrome/browser/extensions/browser_action_test_util.h"
9#include "chrome/browser/extensions/extension_action.h"
10#include "chrome/browser/extensions/extension_action_manager.h"
11#include "chrome/browser/extensions/extension_apitest.h"
12#include "chrome/browser/extensions/tab_helper.h"
13#include "chrome/browser/sessions/session_tab_helper.h"
14#include "chrome/browser/ui/browser.h"
15#include "chrome/browser/ui/tabs/tab_strip_model.h"
16#include "chrome/test/base/interactive_test_utils.h"
17#include "chrome/test/base/ui_test_utils.h"
18#include "content/public/browser/notification_service.h"
19#include "content/public/browser/web_contents.h"
20#include "content/public/test/browser_test_utils.h"
21#include "extensions/browser/extension_registry.h"
22#include "extensions/common/extension.h"
23#include "extensions/common/feature_switch.h"
24#include "extensions/common/manifest_constants.h"
25#include "extensions/common/permissions/permissions_data.h"
26#include "extensions/test/result_catcher.h"
27
28using content::WebContents;
29
30namespace extensions {
31
32namespace {
33// This extension ID is used for tests require a stable ID over multiple
34// extension installs.
35const char kId[] = "pgoakhfeplldmjheffidklpoklkppipp";
36
37// Default keybinding to use for emulating user-defined shortcut overrides. The
38// test extensions use Alt+Shift+F and Alt+Shift+H.
39const char kAltShiftG[] = "Alt+Shift+G";
40}
41
42class CommandsApiTest : public ExtensionApiTest {
43 public:
44  CommandsApiTest() {}
45  virtual ~CommandsApiTest() {}
46
47 protected:
48  BrowserActionTestUtil GetBrowserActionsBar() {
49    return BrowserActionTestUtil(browser());
50  }
51
52  bool IsGrantedForTab(const Extension* extension,
53                       const content::WebContents* web_contents) {
54    return extension->permissions_data()->HasAPIPermissionForTab(
55        SessionTabHelper::IdForTab(web_contents), APIPermission::kTab);
56  }
57
58#if defined(OS_CHROMEOS)
59  void RunChromeOSConversionTest(const std::string& extension_path) {
60    // Setup the environment.
61    ASSERT_TRUE(test_server()->Start());
62    ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
63    ASSERT_TRUE(RunExtensionTest(extension_path)) << message_;
64    ui_test_utils::NavigateToURL(
65        browser(), test_server()->GetURL("files/extensions/test_file.txt"));
66
67    ResultCatcher catcher;
68
69    // Send all expected keys (Search+Shift+{Left, Up, Right, Down}).
70    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
71        browser(), ui::VKEY_LEFT, false, true, false, true));
72    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
73        browser(), ui::VKEY_UP, false, true, false, true));
74    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
75        browser(), ui::VKEY_RIGHT, false, true, false, true));
76    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
77        browser(), ui::VKEY_DOWN, false, true, false, true));
78
79    ASSERT_TRUE(catcher.GetNextResult());
80  }
81#endif  // OS_CHROMEOS
82};
83
84// Test the basic functionality of the Keybinding API:
85// - That pressing the shortcut keys should perform actions (activate the
86//   browser action or send an event).
87// - Note: Page action keybindings are tested in PageAction test below.
88// - The shortcut keys taken by one extension are not overwritten by the last
89//   installed extension.
90IN_PROC_BROWSER_TEST_F(CommandsApiTest, Basic) {
91  ASSERT_TRUE(test_server()->Start());
92  ASSERT_TRUE(RunExtensionTest("keybinding/basics")) << message_;
93  const Extension* extension = GetSingleLoadedExtension();
94  ASSERT_TRUE(extension) << message_;
95
96  // Load this extension, which uses the same keybindings but sets the page
97  // to different colors. This is so we can see that it doesn't interfere. We
98  // don't test this extension in any other way (it should otherwise be
99  // immaterial to this test).
100  ASSERT_TRUE(RunExtensionTest("keybinding/conflicting")) << message_;
101
102  // Test that there are two browser actions in the toolbar.
103  ASSERT_EQ(2, GetBrowserActionsBar().NumberOfBrowserActions());
104
105  ui_test_utils::NavigateToURL(browser(),
106      test_server()->GetURL("files/extensions/test_file.txt"));
107
108  // activeTab shouldn't have been granted yet.
109  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
110  ASSERT_TRUE(tab);
111
112  EXPECT_FALSE(IsGrantedForTab(extension, tab));
113
114  // Activate the shortcut (Ctrl+Shift+F).
115  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
116      browser(), ui::VKEY_F, true, true, false, false));
117
118  // activeTab should now be granted.
119  EXPECT_TRUE(IsGrantedForTab(extension, tab));
120
121  // Verify the command worked.
122  bool result = false;
123  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
124      tab,
125      "setInterval(function(){"
126      "  if(document.body.bgColor == 'red'){"
127      "    window.domAutomationController.send(true)}}, 100)",
128      &result));
129  ASSERT_TRUE(result);
130
131  // Activate the shortcut (Ctrl+Shift+Y).
132  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
133      browser(), ui::VKEY_Y, true, true, false, false));
134
135  result = false;
136  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
137      tab,
138      "setInterval(function(){"
139      "  if(document.body.bgColor == 'blue'){"
140      "    window.domAutomationController.send(true)}}, 100)",
141      &result));
142  ASSERT_TRUE(result);
143}
144
145// Flaky on linux and chromeos, http://crbug.com/165825
146#if defined(OS_MACOSX) || defined(OS_WIN)
147#define MAYBE_PageAction PageAction
148#else
149#define MAYBE_PageAction DISABLED_PageAction
150#endif
151IN_PROC_BROWSER_TEST_F(CommandsApiTest, MAYBE_PageAction) {
152  ASSERT_TRUE(test_server()->Start());
153  ASSERT_TRUE(RunExtensionTest("keybinding/page_action")) << message_;
154  const Extension* extension = GetSingleLoadedExtension();
155  ASSERT_TRUE(extension) << message_;
156
157  {
158    // Load a page, the extension will detect the navigation and request to show
159    // the page action icon.
160    ResultCatcher catcher;
161    ui_test_utils::NavigateToURL(browser(),
162        test_server()->GetURL("files/extensions/test_file.txt"));
163    ASSERT_TRUE(catcher.GetNextResult());
164  }
165
166  // Make sure it appears and is the right one.
167  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1));
168  int tab_id = SessionTabHelper::FromWebContents(
169      browser()->tab_strip_model()->GetActiveWebContents())->session_id().id();
170  ExtensionAction* action =
171      ExtensionActionManager::Get(browser()->profile())->
172      GetPageAction(*extension);
173  ASSERT_TRUE(action);
174  EXPECT_EQ("Make this page red", action->GetTitle(tab_id));
175
176  // Activate the shortcut (Alt+Shift+F).
177  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
178      browser(), ui::VKEY_F, false, true, true, false));
179
180  // Verify the command worked (the page action turns the page red).
181  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
182  bool result = false;
183  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
184      tab,
185      "setInterval(function(){"
186      "  if(document.body.bgColor == 'red'){"
187      "    window.domAutomationController.send(true)}}, 100)",
188      &result));
189  ASSERT_TRUE(result);
190}
191
192// This test validates that the getAll query API function returns registered
193// commands as well as synthesized ones and that inactive commands (like the
194// synthesized ones are in nature) have no shortcuts.
195IN_PROC_BROWSER_TEST_F(CommandsApiTest, SynthesizedCommand) {
196  ASSERT_TRUE(test_server()->Start());
197  ASSERT_TRUE(RunExtensionTest("keybinding/synthesized")) << message_;
198}
199
200// This test validates that an extension cannot request a shortcut that is
201// already in use by Chrome.
202IN_PROC_BROWSER_TEST_F(CommandsApiTest, DontOverwriteSystemShortcuts) {
203  ASSERT_TRUE(test_server()->Start());
204
205  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
206
207  ASSERT_TRUE(RunExtensionTest("keybinding/dont_overwrite_system")) << message_;
208
209  ui_test_utils::NavigateToURL(browser(),
210      test_server()->GetURL("files/extensions/test_file.txt"));
211
212  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
213  ASSERT_TRUE(tab);
214
215  // Activate the shortcut (Alt+Shift+F) to make the page blue.
216  {
217    ResultCatcher catcher;
218    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
219        browser(), ui::VKEY_F, false, true, true, false));
220    ASSERT_TRUE(catcher.GetNextResult());
221  }
222
223  bool result = false;
224  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
225      tab,
226      "setInterval(function() {"
227      "  if (document.body.bgColor == 'blue') {"
228      "    window.domAutomationController.send(true)}}, 100)",
229      &result));
230  ASSERT_TRUE(result);
231
232  // Activate the bookmark shortcut (Ctrl+D) to make the page green (should not
233  // work without requesting via chrome_settings_overrides).
234#if defined(OS_MACOSX)
235    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
236        browser(), ui::VKEY_D, false, false, false, true));
237#else
238    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
239        browser(), ui::VKEY_D, true, false, false, false));
240#endif
241
242  // The page should still be blue.
243  result = false;
244  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
245      tab,
246      "setInterval(function() {"
247      "  if (document.body.bgColor == 'blue') {"
248      "    window.domAutomationController.send(true)}}, 100)",
249      &result));
250  ASSERT_TRUE(result);
251
252  // Activate the shortcut (Ctrl+F) to make the page red (should not work).
253  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
254      browser(), ui::VKEY_F, true, false, false, false));
255
256  // The page should still be blue.
257  result = false;
258  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
259      tab,
260      "setInterval(function() {"
261      "  if (document.body.bgColor == 'blue') {"
262      "    window.domAutomationController.send(true)}}, 100)",
263      &result));
264  ASSERT_TRUE(result);
265}
266
267// This test validates that an extension can override the Chrome bookmark
268// shortcut if it has requested to do so.
269IN_PROC_BROWSER_TEST_F(CommandsApiTest, OverwriteBookmarkShortcut) {
270  ASSERT_TRUE(test_server()->Start());
271
272  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
273
274  // This functionality requires a feature flag.
275  CommandLine::ForCurrentProcess()->AppendSwitchASCII(
276      "--enable-override-bookmarks-ui",
277      "1");
278
279  ASSERT_TRUE(RunExtensionTest("keybinding/overwrite_bookmark_shortcut"))
280      << message_;
281
282  ui_test_utils::NavigateToURL(browser(),
283      test_server()->GetURL("files/extensions/test_file.txt"));
284
285  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
286  ASSERT_TRUE(tab);
287
288  // Activate the shortcut (Ctrl+D) to make the page green.
289  {
290    ResultCatcher catcher;
291#if defined(OS_MACOSX)
292    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
293        browser(), ui::VKEY_D, false, false, false, true));
294#else
295    ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
296        browser(), ui::VKEY_D, true, false, false, false));
297#endif
298    ASSERT_TRUE(catcher.GetNextResult());
299  }
300
301  bool result = false;
302  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
303      tab,
304      "setInterval(function() {"
305      "  if (document.body.bgColor == 'green') {"
306      "    window.domAutomationController.send(true)}}, 100)",
307      &result));
308  ASSERT_TRUE(result);
309}
310
311// This test validates that an extension override of the Chrome bookmark
312// shortcut does not supersede the same keybinding by web pages.
313IN_PROC_BROWSER_TEST_F(CommandsApiTest,
314                       OverwriteBookmarkShortcutDoesNotOverrideWebKeybinding) {
315  ASSERT_TRUE(test_server()->Start());
316
317  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
318
319  // This functionality requires a feature flag.
320  CommandLine::ForCurrentProcess()->AppendSwitchASCII(
321      "--enable-override-bookmarks-ui",
322      "1");
323
324  ASSERT_TRUE(RunExtensionTest("keybinding/overwrite_bookmark_shortcut"))
325      << message_;
326
327  ui_test_utils::NavigateToURL(browser(),
328      test_server()->GetURL(
329          "files/extensions/test_file_with_ctrl-d_keybinding.html"));
330
331  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
332  ASSERT_TRUE(tab);
333
334  // Activate the shortcut (Ctrl+D) which should be handled by the page and make
335  // the background color magenta.
336#if defined(OS_MACOSX)
337  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
338      browser(), ui::VKEY_D, false, false, false, true));
339#else
340  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
341      browser(), ui::VKEY_D, true, false, false, false));
342#endif
343
344  bool result = false;
345  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
346      tab,
347      "setInterval(function() {"
348      "  if (document.body.bgColor == 'magenta') {"
349      "    window.domAutomationController.send(true)}}, 100)",
350      &result));
351  ASSERT_TRUE(result);
352}
353
354// This test validates that user-set override of the Chrome bookmark shortcut in
355// an extension that does not request it does supersede the same keybinding by
356// web pages.
357IN_PROC_BROWSER_TEST_F(CommandsApiTest,
358                       OverwriteBookmarkShortcutByUserOverridesWebKeybinding) {
359  ASSERT_TRUE(test_server()->Start());
360
361  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
362
363  // This functionality requires a feature flag.
364  CommandLine::ForCurrentProcess()->AppendSwitchASCII(
365      "--enable-override-bookmarks-ui",
366      "1");
367
368  ASSERT_TRUE(RunExtensionTest("keybinding/basics"))
369      << message_;
370
371  CommandService* command_service = CommandService::Get(browser()->profile());
372
373  const Extension* extension = GetSingleLoadedExtension();
374  // Simulate the user setting the keybinding to Ctrl+D.
375#if defined(OS_MACOSX)
376  const char* hotkey = "Command+D";
377#else
378  const char* hotkey = "Ctrl+D";
379#endif  // defined(OS_MACOSX)
380  command_service->UpdateKeybindingPrefs(
381      extension->id(), manifest_values::kBrowserActionCommandEvent, hotkey);
382
383  ui_test_utils::NavigateToURL(browser(),
384      test_server()->GetURL(
385          "files/extensions/test_file_with_ctrl-d_keybinding.html"));
386
387  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
388  ASSERT_TRUE(tab);
389
390  // Activate the shortcut (Ctrl+D) which should be handled by the extension and
391  // make the background color red.
392#if defined(OS_MACOSX)
393  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
394      browser(), ui::VKEY_D, false, false, false, true));
395#else
396  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
397      browser(), ui::VKEY_D, true, false, false, false));
398#endif
399
400  bool result = false;
401  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
402      tab,
403      "setInterval(function() {"
404      "  if (document.body.bgColor == 'red') {"
405      "    window.domAutomationController.send(true)}}, 100)",
406      &result));
407  ASSERT_TRUE(result);
408}
409
410#if defined(OS_WIN)
411// Currently this feature is implemented on Windows only.
412#define MAYBE_AllowDuplicatedMediaKeys AllowDuplicatedMediaKeys
413#else
414#define MAYBE_AllowDuplicatedMediaKeys DISABLED_AllowDuplicatedMediaKeys
415#endif
416
417// Test that media keys go to all extensions that register for them.
418IN_PROC_BROWSER_TEST_F(CommandsApiTest, MAYBE_AllowDuplicatedMediaKeys) {
419  ResultCatcher catcher;
420  ASSERT_TRUE(RunExtensionTest("keybinding/non_global_media_keys_0"))
421      << message_;
422  ASSERT_TRUE(catcher.GetNextResult());
423  ASSERT_TRUE(RunExtensionTest("keybinding/non_global_media_keys_1"))
424      << message_;
425  ASSERT_TRUE(catcher.GetNextResult());
426
427  // Activate the Media Stop key.
428  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
429      browser(), ui::VKEY_MEDIA_STOP, false, false, false, false));
430
431  // We should get two success result.
432  ASSERT_TRUE(catcher.GetNextResult());
433  ASSERT_TRUE(catcher.GetNextResult());
434}
435
436IN_PROC_BROWSER_TEST_F(CommandsApiTest, ShortcutAddedOnUpdate) {
437  base::ScopedTempDir scoped_temp_dir;
438  EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
439  base::FilePath pem_path = test_data_dir_.
440      AppendASCII("keybinding").AppendASCII("keybinding.pem");
441  base::FilePath path_v1_unassigned = PackExtensionWithOptions(
442      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
443                    .AppendASCII("v1_unassigned"),
444      scoped_temp_dir.path().AppendASCII("v1_unassigned.crx"),
445      pem_path,
446      base::FilePath());
447  base::FilePath path_v2 = PackExtensionWithOptions(
448      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
449                    .AppendASCII("v2"),
450      scoped_temp_dir.path().AppendASCII("v2.crx"),
451      pem_path,
452      base::FilePath());
453
454  ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile());
455  CommandService* command_service = CommandService::Get(browser()->profile());
456
457  // Install v1 of the extension without keybinding assigned.
458  ASSERT_TRUE(InstallExtension(path_v1_unassigned, 1));
459  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
460              NULL);
461
462  // Verify it is set to nothing.
463  ui::Accelerator accelerator = command_service->FindCommandByName(
464      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
465  EXPECT_EQ(ui::VKEY_UNKNOWN, accelerator.key_code());
466
467  // Update to version 2 with keybinding.
468  EXPECT_TRUE(UpdateExtension(kId, path_v2, 0));
469  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
470              NULL);
471
472  // Verify it has a command of Alt+Shift+F.
473  accelerator = command_service->FindCommandByName(
474      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
475  EXPECT_EQ(ui::VKEY_F, accelerator.key_code());
476  EXPECT_FALSE(accelerator.IsCtrlDown());
477  EXPECT_TRUE(accelerator.IsShiftDown());
478  EXPECT_TRUE(accelerator.IsAltDown());
479}
480
481IN_PROC_BROWSER_TEST_F(CommandsApiTest, ShortcutChangedOnUpdate) {
482  base::ScopedTempDir scoped_temp_dir;
483  EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
484  base::FilePath pem_path = test_data_dir_.
485      AppendASCII("keybinding").AppendASCII("keybinding.pem");
486  base::FilePath path_v1 = PackExtensionWithOptions(
487      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
488                    .AppendASCII("v1"),
489      scoped_temp_dir.path().AppendASCII("v1.crx"),
490      pem_path,
491      base::FilePath());
492  base::FilePath path_v2_reassigned = PackExtensionWithOptions(
493      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
494                    .AppendASCII("v2_reassigned"),
495      scoped_temp_dir.path().AppendASCII("v2_reassigned.crx"),
496      pem_path,
497      base::FilePath());
498
499  ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile());
500  CommandService* command_service = CommandService::Get(browser()->profile());
501
502  // Install v1 of the extension.
503  ASSERT_TRUE(InstallExtension(path_v1, 1));
504  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
505              NULL);
506
507  // Verify it has a command of Alt+Shift+F.
508  ui::Accelerator accelerator = command_service->FindCommandByName(
509      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
510  EXPECT_EQ(ui::VKEY_F, accelerator.key_code());
511  EXPECT_FALSE(accelerator.IsCtrlDown());
512  EXPECT_TRUE(accelerator.IsShiftDown());
513  EXPECT_TRUE(accelerator.IsAltDown());
514
515  // Update to version 2 with different keybinding assigned.
516  EXPECT_TRUE(UpdateExtension(kId, path_v2_reassigned, 0));
517  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
518              NULL);
519
520  // Verify it has a command of Alt+Shift+H.
521  accelerator = command_service->FindCommandByName(
522      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
523  EXPECT_EQ(ui::VKEY_H, accelerator.key_code());
524  EXPECT_FALSE(accelerator.IsCtrlDown());
525  EXPECT_TRUE(accelerator.IsShiftDown());
526  EXPECT_TRUE(accelerator.IsAltDown());
527}
528
529IN_PROC_BROWSER_TEST_F(CommandsApiTest, ShortcutRemovedOnUpdate) {
530  base::ScopedTempDir scoped_temp_dir;
531  EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
532  base::FilePath pem_path = test_data_dir_.
533      AppendASCII("keybinding").AppendASCII("keybinding.pem");
534  base::FilePath path_v1 = PackExtensionWithOptions(
535      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
536                    .AppendASCII("v1"),
537      scoped_temp_dir.path().AppendASCII("v1.crx"),
538      pem_path,
539      base::FilePath());
540  base::FilePath path_v2_unassigned = PackExtensionWithOptions(
541      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
542                    .AppendASCII("v2_unassigned"),
543      scoped_temp_dir.path().AppendASCII("v2_unassigned.crx"),
544      pem_path,
545      base::FilePath());
546
547  ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile());
548  CommandService* command_service = CommandService::Get(browser()->profile());
549
550  // Install v1 of the extension.
551  ASSERT_TRUE(InstallExtension(path_v1, 1));
552  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
553              NULL);
554
555  // Verify it has a command of Alt+Shift+F.
556  ui::Accelerator accelerator = command_service->FindCommandByName(
557      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
558  EXPECT_EQ(ui::VKEY_F, accelerator.key_code());
559  EXPECT_FALSE(accelerator.IsCtrlDown());
560  EXPECT_TRUE(accelerator.IsShiftDown());
561  EXPECT_TRUE(accelerator.IsAltDown());
562
563  // Update to version 2 without keybinding assigned.
564  EXPECT_TRUE(UpdateExtension(kId, path_v2_unassigned, 0));
565  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
566              NULL);
567
568  // Verify the keybinding gets set to nothing.
569  accelerator = command_service->FindCommandByName(
570      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
571  EXPECT_EQ(ui::VKEY_UNKNOWN, accelerator.key_code());
572}
573
574IN_PROC_BROWSER_TEST_F(CommandsApiTest,
575                       ShortcutAddedOnUpdateAfterBeingAssignedByUser) {
576  base::ScopedTempDir scoped_temp_dir;
577  EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
578  base::FilePath pem_path = test_data_dir_.
579      AppendASCII("keybinding").AppendASCII("keybinding.pem");
580  base::FilePath path_v1_unassigned = PackExtensionWithOptions(
581      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
582                    .AppendASCII("v1_unassigned"),
583      scoped_temp_dir.path().AppendASCII("v1_unassigned.crx"),
584      pem_path,
585      base::FilePath());
586  base::FilePath path_v2 = PackExtensionWithOptions(
587      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
588                    .AppendASCII("v2"),
589      scoped_temp_dir.path().AppendASCII("v2.crx"),
590      pem_path,
591      base::FilePath());
592
593  ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile());
594  CommandService* command_service = CommandService::Get(browser()->profile());
595
596  // Install v1 of the extension without keybinding assigned.
597  ASSERT_TRUE(InstallExtension(path_v1_unassigned, 1));
598  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
599              NULL);
600
601  // Verify it is set to nothing.
602  ui::Accelerator accelerator = command_service->FindCommandByName(
603      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
604  EXPECT_EQ(ui::VKEY_UNKNOWN, accelerator.key_code());
605
606  // Simulate the user setting the keybinding to Alt+Shift+G.
607  command_service->UpdateKeybindingPrefs(
608      kId, manifest_values::kBrowserActionCommandEvent, kAltShiftG);
609
610  // Update to version 2 with keybinding.
611  EXPECT_TRUE(UpdateExtension(kId, path_v2, 0));
612  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
613              NULL);
614
615  // Verify the previously-set keybinding is still set.
616  accelerator = command_service->FindCommandByName(
617      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
618  EXPECT_EQ(ui::VKEY_G, accelerator.key_code());
619  EXPECT_FALSE(accelerator.IsCtrlDown());
620  EXPECT_TRUE(accelerator.IsShiftDown());
621  EXPECT_TRUE(accelerator.IsAltDown());
622}
623
624IN_PROC_BROWSER_TEST_F(CommandsApiTest,
625                       ShortcutChangedOnUpdateAfterBeingReassignedByUser) {
626  base::ScopedTempDir scoped_temp_dir;
627  EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
628  base::FilePath pem_path = test_data_dir_.
629      AppendASCII("keybinding").AppendASCII("keybinding.pem");
630  base::FilePath path_v1 = PackExtensionWithOptions(
631      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
632                    .AppendASCII("v1"),
633      scoped_temp_dir.path().AppendASCII("v1.crx"),
634      pem_path,
635      base::FilePath());
636  base::FilePath path_v2_reassigned = PackExtensionWithOptions(
637      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
638                    .AppendASCII("v2_reassigned"),
639      scoped_temp_dir.path().AppendASCII("v2_reassigned.crx"),
640      pem_path,
641      base::FilePath());
642
643  ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile());
644  CommandService* command_service = CommandService::Get(browser()->profile());
645
646  // Install v1 of the extension.
647  ASSERT_TRUE(InstallExtension(path_v1, 1));
648  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
649              NULL);
650
651  // Verify it has a command of Alt+Shift+F.
652  ui::Accelerator accelerator = command_service->FindCommandByName(
653      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
654  EXPECT_EQ(ui::VKEY_F, accelerator.key_code());
655  EXPECT_FALSE(accelerator.IsCtrlDown());
656  EXPECT_TRUE(accelerator.IsShiftDown());
657  EXPECT_TRUE(accelerator.IsAltDown());
658
659  // Simulate the user setting the keybinding to Alt+Shift+G.
660  command_service->UpdateKeybindingPrefs(
661      kId, manifest_values::kBrowserActionCommandEvent, kAltShiftG);
662
663  // Update to version 2 with different keybinding assigned.
664  EXPECT_TRUE(UpdateExtension(kId, path_v2_reassigned, 0));
665  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
666              NULL);
667
668  // Verify it has a command of Alt+Shift+G.
669  accelerator = command_service->FindCommandByName(
670      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
671  EXPECT_EQ(ui::VKEY_G, accelerator.key_code());
672  EXPECT_FALSE(accelerator.IsCtrlDown());
673  EXPECT_TRUE(accelerator.IsShiftDown());
674  EXPECT_TRUE(accelerator.IsAltDown());
675}
676
677IN_PROC_BROWSER_TEST_F(CommandsApiTest,
678                       ShortcutRemovedOnUpdateAfterBeingReassignedByUser) {
679  base::ScopedTempDir scoped_temp_dir;
680  EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
681  base::FilePath pem_path = test_data_dir_.
682      AppendASCII("keybinding").AppendASCII("keybinding.pem");
683  base::FilePath path_v1 = PackExtensionWithOptions(
684      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
685                    .AppendASCII("v1"),
686      scoped_temp_dir.path().AppendASCII("v1.crx"),
687      pem_path,
688      base::FilePath());
689  base::FilePath path_v2_unassigned = PackExtensionWithOptions(
690      test_data_dir_.AppendASCII("keybinding").AppendASCII("update")
691                    .AppendASCII("v2_unassigned"),
692      scoped_temp_dir.path().AppendASCII("v2_unassigned.crx"),
693      pem_path,
694      base::FilePath());
695
696  ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile());
697  CommandService* command_service = CommandService::Get(browser()->profile());
698
699  // Install v1 of the extension.
700  ASSERT_TRUE(InstallExtension(path_v1, 1));
701  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
702              NULL);
703
704  // Verify it has a command of Alt+Shift+F.
705  ui::Accelerator accelerator = command_service->FindCommandByName(
706      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
707  EXPECT_EQ(ui::VKEY_F, accelerator.key_code());
708  EXPECT_FALSE(accelerator.IsCtrlDown());
709  EXPECT_TRUE(accelerator.IsShiftDown());
710  EXPECT_TRUE(accelerator.IsAltDown());
711
712  // Simulate the user reassigning the keybinding to Alt+Shift+G.
713  command_service->UpdateKeybindingPrefs(
714      kId, manifest_values::kBrowserActionCommandEvent, kAltShiftG);
715
716  // Update to version 2 without keybinding assigned.
717  EXPECT_TRUE(UpdateExtension(kId, path_v2_unassigned, 0));
718  EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) !=
719              NULL);
720
721  // Verify the keybinding is still set.
722  accelerator = command_service->FindCommandByName(
723      kId, manifest_values::kBrowserActionCommandEvent).accelerator();
724  EXPECT_EQ(ui::VKEY_G, accelerator.key_code());
725  EXPECT_FALSE(accelerator.IsCtrlDown());
726  EXPECT_TRUE(accelerator.IsShiftDown());
727  EXPECT_TRUE(accelerator.IsAltDown());
728}
729
730//
731#if defined(OS_CHROMEOS) && !defined(NDEBUG)
732// TODO(dtseng): Test times out on Chrome OS debug. See http://crbug.com/412456.
733#define MAYBE_ContinuePropagation DISABLED_ContinuePropagation
734#else
735#define MAYBE_ContinuePropagation ContinuePropagation
736#endif
737
738IN_PROC_BROWSER_TEST_F(CommandsApiTest, MAYBE_ContinuePropagation) {
739  // Setup the environment.
740  ASSERT_TRUE(test_server()->Start());
741  ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
742  ASSERT_TRUE(RunExtensionTest("keybinding/continue_propagation")) << message_;
743  ui_test_utils::NavigateToURL(
744      browser(), test_server()->GetURL("files/extensions/test_file.txt"));
745
746  ResultCatcher catcher;
747
748  // Activate the shortcut (Ctrl+Shift+F). The page should capture the
749  // keystroke and not the extension since |onCommand| has no event listener
750  // initially.
751  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
752      browser(), ui::VKEY_F, true, true, false, false));
753  ASSERT_TRUE(catcher.GetNextResult());
754
755  // Now, the extension should have added an |onCommand| event listener.
756  // Send the same key, but the |onCommand| listener should now receive it.
757  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
758      browser(), ui::VKEY_F, true, true, false, false));
759  ASSERT_TRUE(catcher.GetNextResult());
760
761  // The extension should now have removed its |onCommand| event listener.
762  // Finally, the page should again receive the key.
763  ASSERT_TRUE(ui_test_utils::SendKeyPressSync(
764      browser(), ui::VKEY_F, true, true, false, false));
765  ASSERT_TRUE(catcher.GetNextResult());
766}
767
768// Test is only applicable on Chrome OS.
769#if defined(OS_CHROMEOS)
770// http://crbug.com/410534
771#if defined(USE_OZONE)
772#define MAYBE_ChromeOSConversions DISABLED_ChromeOSConversions
773#else
774#define MAYBE_ChromeOSConversions ChromeOSConversions
775#endif
776IN_PROC_BROWSER_TEST_F(CommandsApiTest, MAYBE_ChromeOSConversions) {
777  RunChromeOSConversionTest("keybinding/chromeos_conversions");
778}
779#endif  // OS_CHROMEOS
780
781}  // namespace extensions
782