// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/command_line.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/extensions/active_tab_permission_granter.h" #include "chrome/browser/extensions/api/commands/command_service.h" #include "chrome/browser/extensions/browser_action_test_util.h" #include "chrome/browser/extensions/component_loader.h" #include "chrome/browser/extensions/extension_action.h" #include "chrome/browser/extensions/extension_action_manager.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/sessions/session_tab_helper.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_command_controller.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/web_contents.h" #include "content/public/test/javascript_test_observer.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/extension.h" #include "extensions/common/feature_switch.h" #include "extensions/common/manifest_constants.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/test/extension_test_message_listener.h" #include "extensions/test/result_catcher.h" using content::WebContents; namespace extensions { namespace { // This extension ID is used for tests require a stable ID over multiple // extension installs. const char kId[] = "pgoakhfeplldmjheffidklpoklkppipp"; // Default keybinding to use for emulating user-defined shortcut overrides. The // test extensions use Alt+Shift+F and Alt+Shift+H. const char kAltShiftG[] = "Alt+Shift+G"; // Name of the command for the "basics" test extension. const char kBasicsShortcutCommandName[] = "toggle-feature"; // Name of the command for the overwrite_bookmark_shortcut test extension. const char kOverwriteBookmarkShortcutCommandName[] = "send message"; #if defined(OS_MACOSX) const char kBookmarkKeybinding[] = "Command+D"; #else const char kBookmarkKeybinding[] = "Ctrl+D"; #endif // defined(OS_MACOSX) bool SendBookmarkKeyPressSync(Browser* browser) { return ui_test_utils::SendKeyPressSync( browser, ui::VKEY_D, #if defined(OS_MACOSX) false, false, false, true #else true, false, false, false #endif ); } // Named command for media key overwrite test. const char kMediaKeyTestCommand[] = "test_mediakeys_update"; // A scoped observer that listens for dom automation messages. class DomMessageListener : public content::TestMessageHandler { public: explicit DomMessageListener(content::WebContents* web_contents); ~DomMessageListener() override; // Wait until a message is received. void Wait(); // Clears and resets the observer. void Clear(); const std::string& message() const { return message_; } private: // content::TestMessageHandler: MessageResponse HandleMessage(const std::string& json) override; void Reset() override; // The message received. Note that this will be JSON, so if it is a string, // it will be wrapped in quotes. std::string message_; content::JavascriptTestObserver observer_; DISALLOW_COPY_AND_ASSIGN(DomMessageListener); }; DomMessageListener::DomMessageListener(content::WebContents* web_contents) : observer_(web_contents, this) { } DomMessageListener::~DomMessageListener() { } void DomMessageListener::Wait() { observer_.Run(); } void DomMessageListener::Clear() { // We don't just call this in DomMessageListener::Reset() because the // JavascriptTestObserver's Reset() method also resets its handler (this). observer_.Reset(); } content::TestMessageHandler::MessageResponse DomMessageListener::HandleMessage( const std::string& json) { message_ = json; return DONE; } void DomMessageListener::Reset() { TestMessageHandler::Reset(); message_.clear(); } } // namespace class CommandsApiTest : public ExtensionApiTest { public: CommandsApiTest() {} ~CommandsApiTest() override {} protected: bool IsGrantedForTab(const Extension* extension, const content::WebContents* web_contents) { return extension->permissions_data()->HasAPIPermissionForTab( SessionTabHelper::IdForTab(web_contents), APIPermission::kTab); } #if defined(OS_CHROMEOS) void RunChromeOSConversionTest(const std::string& extension_path) { // Setup the environment. ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); ASSERT_TRUE(RunExtensionTest(extension_path)) << message_; ui_test_utils::NavigateToURL( browser(), test_server()->GetURL("files/extensions/test_file.txt")); ResultCatcher catcher; // Send all expected keys (Search+Shift+{Left, Up, Right, Down}). ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_LEFT, false, true, false, true)); ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_UP, false, true, false, true)); ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_RIGHT, false, true, false, true)); ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_DOWN, false, true, false, true)); ASSERT_TRUE(catcher.GetNextResult()); } #endif // OS_CHROMEOS }; // Test the basic functionality of the Keybinding API: // - That pressing the shortcut keys should perform actions (activate the // browser action or send an event). // - Note: Page action keybindings are tested in PageAction test below. // - The shortcut keys taken by one extension are not overwritten by the last // installed extension. IN_PROC_BROWSER_TEST_F(CommandsApiTest, Basic) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(RunExtensionTest("keybinding/basics")) << message_; const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; // Load this extension, which uses the same keybindings but sets the page // to different colors. This is so we can see that it doesn't interfere. We // don't test this extension in any other way (it should otherwise be // immaterial to this test). ASSERT_TRUE(RunExtensionTest("keybinding/conflicting")) << message_; BrowserActionTestUtil browser_actions_bar(browser()); // Test that there are two browser actions in the toolbar. ASSERT_EQ(2, browser_actions_bar.NumberOfBrowserActions()); ui_test_utils::NavigateToURL( browser(), test_server()->GetURL("files/extensions/test_file.txt")); // activeTab shouldn't have been granted yet. WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); ASSERT_TRUE(tab); EXPECT_FALSE(IsGrantedForTab(extension, tab)); ExtensionTestMessageListener test_listener(false); // Won't reply. // Activate the browser action shortcut (Ctrl+Shift+F). ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_F, true, true, false, false)); EXPECT_TRUE(test_listener.WaitUntilSatisfied()); // activeTab should now be granted. EXPECT_TRUE(IsGrantedForTab(extension, tab)); // Verify the command worked. EXPECT_EQ(std::string("basics browser action"), test_listener.message()); test_listener.Reset(); // Activate the command shortcut (Ctrl+Shift+Y). ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_Y, true, true, false, false)); EXPECT_TRUE(test_listener.WaitUntilSatisfied()); EXPECT_EQ(std::string(kBasicsShortcutCommandName), test_listener.message()); } IN_PROC_BROWSER_TEST_F(CommandsApiTest, PageAction) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(RunExtensionTest("keybinding/page_action")) << message_; const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; { // Load a page, the extension will detect the navigation and request to show // the page action icon. ResultCatcher catcher; ui_test_utils::NavigateToURL( browser(), test_server()->GetURL("files/extensions/test_file.txt")); ASSERT_TRUE(catcher.GetNextResult()); } // Make sure it appears and is the right one. ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); int tab_id = SessionTabHelper::FromWebContents( browser()->tab_strip_model()->GetActiveWebContents())->session_id().id(); ExtensionAction* action = ExtensionActionManager::Get(browser()->profile())-> GetPageAction(*extension); ASSERT_TRUE(action); EXPECT_EQ("Send message", action->GetTitle(tab_id)); ExtensionTestMessageListener test_listener(false); // Won't reply. test_listener.set_extension_id(extension->id()); // Activate the shortcut (Alt+Shift+F). ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_F, false, true, true, false)); test_listener.WaitUntilSatisfied(); EXPECT_EQ("clicked", test_listener.message()); } IN_PROC_BROWSER_TEST_F(CommandsApiTest, PageActionKeyUpdated) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(RunExtensionTest("keybinding/page_action")) << message_; const Extension* extension = GetSingleLoadedExtension(); ASSERT_TRUE(extension) << message_; CommandService* command_service = CommandService::Get(browser()->profile()); // Simulate the user setting the keybinding to Alt+Shift+G. command_service->UpdateKeybindingPrefs( extension->id(), manifest_values::kPageActionCommandEvent, kAltShiftG); { // Load a page. The extension will detect the navigation and request to show // the page action icon. ResultCatcher catcher; ui_test_utils::NavigateToURL( browser(), test_server()->GetURL("files/extensions/test_file.txt")); ASSERT_TRUE(catcher.GetNextResult()); } ExtensionTestMessageListener test_listener(false); // Won't reply. test_listener.set_extension_id(extension->id()); // Activate the shortcut. ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_G, false, true, true, false)); test_listener.WaitUntilSatisfied(); EXPECT_EQ("clicked", test_listener.message()); } // This test validates that the getAll query API function returns registered // commands as well as synthesized ones and that inactive commands (like the // synthesized ones are in nature) have no shortcuts. IN_PROC_BROWSER_TEST_F(CommandsApiTest, SynthesizedCommand) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(RunExtensionTest("keybinding/synthesized")) << message_; } // This test validates that an extension cannot request a shortcut that is // already in use by Chrome. IN_PROC_BROWSER_TEST_F(CommandsApiTest, DontOverwriteSystemShortcuts) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); ASSERT_TRUE(RunExtensionTest("keybinding/dont_overwrite_system")) << message_; ui_test_utils::NavigateToURL( browser(), test_server()->GetURL("files/extensions/test_file.txt")); // Activate the regular shortcut (Alt+Shift+F). ExtensionTestMessageListener alt_shift_f_listener("alt_shift_f", false); ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_F, false, true, true, false)); EXPECT_TRUE(alt_shift_f_listener.WaitUntilSatisfied()); // Try to activate the bookmark shortcut (Ctrl+D). This should not work // without requesting via chrome_settings_overrides. // // Since keypresses are sent synchronously, we can check this by first sending // Ctrl+D (which shouldn't work), followed by Alt+Shift+F (which should work), // and listening for both. If, by the time we receive the Alt+Shift+F // response, we haven't received a response for Ctrl+D, it is safe to say we // won't receive one. { ExtensionTestMessageListener ctrl_d_listener("ctrl_d", false); alt_shift_f_listener.Reset(); // Send Ctrl+D. ASSERT_TRUE(SendBookmarkKeyPressSync(browser())); // Send Alt+Shift+F. ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_F, false, true, true, false)); EXPECT_TRUE(alt_shift_f_listener.WaitUntilSatisfied()); EXPECT_FALSE(ctrl_d_listener.was_satisfied()); } // Try to activate the Ctrl+F shortcut (shouldn't work). { ExtensionTestMessageListener ctrl_f_listener("ctrl_f", false); alt_shift_f_listener.Reset(); // Send Ctrl+F. ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_F, true, false, false, false)); // Send Alt+Shift+F. ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_F, false, true, true, false)); EXPECT_TRUE(alt_shift_f_listener.WaitUntilSatisfied()); EXPECT_FALSE(ctrl_f_listener.was_satisfied()); } } // This test validates that an extension can remove the Chrome bookmark shortcut // if it has requested to do so. IN_PROC_BROWSER_TEST_F(CommandsApiTest, RemoveBookmarkShortcut) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); // This functionality requires a feature flag. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( "--enable-override-bookmarks-ui", "1"); ASSERT_TRUE(RunExtensionTest("keybinding/remove_bookmark_shortcut")) << message_; EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_BOOKMARK_PAGE)); } // This test validates that an extension cannot remove the Chrome bookmark // shortcut without being given permission with a feature flag. IN_PROC_BROWSER_TEST_F(CommandsApiTest, RemoveBookmarkShortcutWithoutPermission) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); EXPECT_TRUE(RunExtensionTestIgnoreManifestWarnings( "keybinding/remove_bookmark_shortcut")); EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_BOOKMARK_PAGE)); } // This test validates that an extension that removes the Chrome bookmark // shortcut continues to remove the bookmark shortcut with a user-assigned // Ctrl+D shortcut (i.e. it does not trigger the overwrite functionality). IN_PROC_BROWSER_TEST_F(CommandsApiTest, RemoveBookmarkShortcutWithUserKeyBinding) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); // This functionality requires a feature flag. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( "--enable-override-bookmarks-ui", "1"); ASSERT_TRUE(RunExtensionTest("keybinding/remove_bookmark_shortcut")) << message_; // Check that the shortcut is removed. CommandService* command_service = CommandService::Get(browser()->profile()); const Extension* extension = GetSingleLoadedExtension(); // Simulate the user setting a keybinding to Ctrl+D. command_service->UpdateKeybindingPrefs( extension->id(), manifest_values::kBrowserActionCommandEvent, kBookmarkKeybinding); // Force the command enable state to be recalculated. browser()->command_controller()->ExtensionStateChanged(); EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_BOOKMARK_PAGE)); } // This test validates that an extension can override the Chrome bookmark // shortcut if it has requested to do so. IN_PROC_BROWSER_TEST_F(CommandsApiTest, OverwriteBookmarkShortcut) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); // This functionality requires a feature flag. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( "--enable-override-bookmarks-ui", "1"); ASSERT_TRUE(RunExtensionTest("keybinding/overwrite_bookmark_shortcut")) << message_; ui_test_utils::NavigateToURL( browser(), test_server()->GetURL("files/extensions/test_file.txt")); // Activate the shortcut (Ctrl+D) to send a test message. ExtensionTestMessageListener test_listener(false); // Won't reply. ASSERT_TRUE(SendBookmarkKeyPressSync(browser())); EXPECT_TRUE(test_listener.WaitUntilSatisfied()); EXPECT_EQ(std::string(kOverwriteBookmarkShortcutCommandName), test_listener.message()); } // This test validates that an extension that requests to override the Chrome // bookmark shortcut, but does not get the keybinding, does not remove the // bookmark UI. IN_PROC_BROWSER_TEST_F(CommandsApiTest, OverwriteBookmarkShortcutWithoutKeybinding) { // This functionality requires a feature flag. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( "--enable-override-bookmarks-ui", "1"); ASSERT_TRUE(test_server()->Start()); EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_BOOKMARK_PAGE)); ASSERT_TRUE(RunExtensionTest("keybinding/overwrite_bookmark_shortcut")) << message_; const Extension* extension = GetSingleLoadedExtension(); CommandService* command_service = CommandService::Get(browser()->profile()); CommandMap commands; // Verify the expected command is present. EXPECT_TRUE(command_service->GetNamedCommands( extension->id(), CommandService::SUGGESTED, CommandService::ANY_SCOPE, &commands)); EXPECT_EQ(1u, commands.count(kOverwriteBookmarkShortcutCommandName)); // Simulate the user removing the Ctrl+D keybinding from the command. command_service->RemoveKeybindingPrefs( extension->id(), kOverwriteBookmarkShortcutCommandName); // Force the command enable state to be recalculated. browser()->command_controller()->ExtensionStateChanged(); EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_BOOKMARK_PAGE)); } // This test validates that an extension override of the Chrome bookmark // shortcut does not supersede the same keybinding by web pages. IN_PROC_BROWSER_TEST_F(CommandsApiTest, OverwriteBookmarkShortcutDoesNotOverrideWebKeybinding) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); // This functionality requires a feature flag. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( "--enable-override-bookmarks-ui", "1"); ASSERT_TRUE(RunExtensionTest("keybinding/overwrite_bookmark_shortcut")) << message_; ui_test_utils::NavigateToURL( browser(), test_server()->GetURL( "files/extensions/test_file_with_ctrl-d_keybinding.html")); WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); ASSERT_TRUE(tab); // Activate the shortcut (Ctrl+D) which should be handled by the page and send // a test message. DomMessageListener listener(tab); ASSERT_TRUE(SendBookmarkKeyPressSync(browser())); listener.Wait(); EXPECT_EQ(std::string("\"web page received\""), listener.message()); } // This test validates that user-set override of the Chrome bookmark shortcut in // an extension that does not request it does supersede the same keybinding by // web pages. IN_PROC_BROWSER_TEST_F(CommandsApiTest, OverwriteBookmarkShortcutByUserOverridesWebKeybinding) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); // This functionality requires a feature flag. base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( "--enable-override-bookmarks-ui", "1"); ASSERT_TRUE(RunExtensionTest("keybinding/basics")) << message_; CommandService* command_service = CommandService::Get(browser()->profile()); const Extension* extension = GetSingleLoadedExtension(); // Simulate the user setting the keybinding to Ctrl+D. command_service->UpdateKeybindingPrefs( extension->id(), manifest_values::kBrowserActionCommandEvent, kBookmarkKeybinding); ui_test_utils::NavigateToURL( browser(), test_server()->GetURL( "files/extensions/test_file_with_ctrl-d_keybinding.html")); ExtensionTestMessageListener test_listener(false); // Won't reply. // Activate the shortcut (Ctrl+D) which should be handled by the extension. ASSERT_TRUE(SendBookmarkKeyPressSync(browser())); EXPECT_TRUE(test_listener.WaitUntilSatisfied()); EXPECT_EQ(std::string("basics browser action"), test_listener.message()); } #if defined(OS_WIN) // Currently this feature is implemented on Windows only. #define MAYBE_AllowDuplicatedMediaKeys AllowDuplicatedMediaKeys #else #define MAYBE_AllowDuplicatedMediaKeys DISABLED_AllowDuplicatedMediaKeys #endif // Test that media keys go to all extensions that register for them. IN_PROC_BROWSER_TEST_F(CommandsApiTest, MAYBE_AllowDuplicatedMediaKeys) { ResultCatcher catcher; ASSERT_TRUE(RunExtensionTest("keybinding/non_global_media_keys_0")) << message_; ASSERT_TRUE(catcher.GetNextResult()); ASSERT_TRUE(RunExtensionTest("keybinding/non_global_media_keys_1")) << message_; ASSERT_TRUE(catcher.GetNextResult()); // Activate the Media Stop key. ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_MEDIA_STOP, false, false, false, false)); // We should get two success result. ASSERT_TRUE(catcher.GetNextResult()); ASSERT_TRUE(catcher.GetNextResult()); } IN_PROC_BROWSER_TEST_F(CommandsApiTest, ShortcutAddedOnUpdate) { base::ScopedTempDir scoped_temp_dir; EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); base::FilePath pem_path = test_data_dir_. AppendASCII("keybinding").AppendASCII("keybinding.pem"); base::FilePath path_v1_unassigned = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v1_unassigned"), scoped_temp_dir.path().AppendASCII("v1_unassigned.crx"), pem_path, base::FilePath()); base::FilePath path_v2 = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v2"), scoped_temp_dir.path().AppendASCII("v2.crx"), pem_path, base::FilePath()); ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile()); CommandService* command_service = CommandService::Get(browser()->profile()); // Install v1 of the extension without keybinding assigned. ASSERT_TRUE(InstallExtension(path_v1_unassigned, 1)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify it is set to nothing. ui::Accelerator accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_UNKNOWN, accelerator.key_code()); // Update to version 2 with keybinding. EXPECT_TRUE(UpdateExtension(kId, path_v2, 0)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify it has a command of Alt+Shift+F. accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_F, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_TRUE(accelerator.IsShiftDown()); EXPECT_TRUE(accelerator.IsAltDown()); } IN_PROC_BROWSER_TEST_F(CommandsApiTest, ShortcutChangedOnUpdate) { base::ScopedTempDir scoped_temp_dir; EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); base::FilePath pem_path = test_data_dir_. AppendASCII("keybinding").AppendASCII("keybinding.pem"); base::FilePath path_v1 = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v1"), scoped_temp_dir.path().AppendASCII("v1.crx"), pem_path, base::FilePath()); base::FilePath path_v2_reassigned = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v2_reassigned"), scoped_temp_dir.path().AppendASCII("v2_reassigned.crx"), pem_path, base::FilePath()); ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile()); CommandService* command_service = CommandService::Get(browser()->profile()); // Install v1 of the extension. ASSERT_TRUE(InstallExtension(path_v1, 1)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify it has a command of Alt+Shift+F. ui::Accelerator accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_F, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_TRUE(accelerator.IsShiftDown()); EXPECT_TRUE(accelerator.IsAltDown()); // Update to version 2 with different keybinding assigned. EXPECT_TRUE(UpdateExtension(kId, path_v2_reassigned, 0)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify it has a command of Alt+Shift+H. accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_H, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_TRUE(accelerator.IsShiftDown()); EXPECT_TRUE(accelerator.IsAltDown()); } IN_PROC_BROWSER_TEST_F(CommandsApiTest, ShortcutRemovedOnUpdate) { base::ScopedTempDir scoped_temp_dir; EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); base::FilePath pem_path = test_data_dir_. AppendASCII("keybinding").AppendASCII("keybinding.pem"); base::FilePath path_v1 = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v1"), scoped_temp_dir.path().AppendASCII("v1.crx"), pem_path, base::FilePath()); base::FilePath path_v2_unassigned = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v2_unassigned"), scoped_temp_dir.path().AppendASCII("v2_unassigned.crx"), pem_path, base::FilePath()); ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile()); CommandService* command_service = CommandService::Get(browser()->profile()); // Install v1 of the extension. ASSERT_TRUE(InstallExtension(path_v1, 1)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify it has a command of Alt+Shift+F. ui::Accelerator accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_F, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_TRUE(accelerator.IsShiftDown()); EXPECT_TRUE(accelerator.IsAltDown()); // Update to version 2 without keybinding assigned. EXPECT_TRUE(UpdateExtension(kId, path_v2_unassigned, 0)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify the keybinding gets set to nothing. accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_UNKNOWN, accelerator.key_code()); } IN_PROC_BROWSER_TEST_F(CommandsApiTest, ShortcutAddedOnUpdateAfterBeingAssignedByUser) { base::ScopedTempDir scoped_temp_dir; EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); base::FilePath pem_path = test_data_dir_. AppendASCII("keybinding").AppendASCII("keybinding.pem"); base::FilePath path_v1_unassigned = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v1_unassigned"), scoped_temp_dir.path().AppendASCII("v1_unassigned.crx"), pem_path, base::FilePath()); base::FilePath path_v2 = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v2"), scoped_temp_dir.path().AppendASCII("v2.crx"), pem_path, base::FilePath()); ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile()); CommandService* command_service = CommandService::Get(browser()->profile()); // Install v1 of the extension without keybinding assigned. ASSERT_TRUE(InstallExtension(path_v1_unassigned, 1)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify it is set to nothing. ui::Accelerator accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_UNKNOWN, accelerator.key_code()); // Simulate the user setting the keybinding to Alt+Shift+G. command_service->UpdateKeybindingPrefs( kId, manifest_values::kBrowserActionCommandEvent, kAltShiftG); // Update to version 2 with keybinding. EXPECT_TRUE(UpdateExtension(kId, path_v2, 0)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify the previously-set keybinding is still set. accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_G, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_TRUE(accelerator.IsShiftDown()); EXPECT_TRUE(accelerator.IsAltDown()); } IN_PROC_BROWSER_TEST_F(CommandsApiTest, ShortcutChangedOnUpdateAfterBeingReassignedByUser) { base::ScopedTempDir scoped_temp_dir; EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); base::FilePath pem_path = test_data_dir_. AppendASCII("keybinding").AppendASCII("keybinding.pem"); base::FilePath path_v1 = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v1"), scoped_temp_dir.path().AppendASCII("v1.crx"), pem_path, base::FilePath()); base::FilePath path_v2_reassigned = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v2_reassigned"), scoped_temp_dir.path().AppendASCII("v2_reassigned.crx"), pem_path, base::FilePath()); ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile()); CommandService* command_service = CommandService::Get(browser()->profile()); // Install v1 of the extension. ASSERT_TRUE(InstallExtension(path_v1, 1)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify it has a command of Alt+Shift+F. ui::Accelerator accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_F, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_TRUE(accelerator.IsShiftDown()); EXPECT_TRUE(accelerator.IsAltDown()); // Simulate the user setting the keybinding to Alt+Shift+G. command_service->UpdateKeybindingPrefs( kId, manifest_values::kBrowserActionCommandEvent, kAltShiftG); // Update to version 2 with different keybinding assigned. EXPECT_TRUE(UpdateExtension(kId, path_v2_reassigned, 0)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify it has a command of Alt+Shift+G. accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_G, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_TRUE(accelerator.IsShiftDown()); EXPECT_TRUE(accelerator.IsAltDown()); } // Test that Media keys do not overwrite previous settings. IN_PROC_BROWSER_TEST_F(CommandsApiTest, MediaKeyShortcutChangedOnUpdateAfterBeingReassignedByUser) { base::ScopedTempDir scoped_temp_dir; EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); base::FilePath pem_path = test_data_dir_. AppendASCII("keybinding").AppendASCII("keybinding.pem"); base::FilePath path_v1 = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("mk_v1"), scoped_temp_dir.path().AppendASCII("mk_v1.crx"), pem_path, base::FilePath()); base::FilePath path_v2_reassigned = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("mk_v2"), scoped_temp_dir.path().AppendASCII("mk_v2.crx"), pem_path, base::FilePath()); ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile()); CommandService* command_service = CommandService::Get(browser()->profile()); // Install v1 of the extension. ASSERT_TRUE(InstallExtension(path_v1, 1)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify it has a command of MediaPlayPause. ui::Accelerator accelerator = command_service->FindCommandByName( kId, kMediaKeyTestCommand).accelerator(); EXPECT_EQ(ui::VKEY_MEDIA_PLAY_PAUSE, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_FALSE(accelerator.IsShiftDown()); EXPECT_FALSE(accelerator.IsAltDown()); // Simulate the user setting the keybinding to Alt+Shift+G. command_service->UpdateKeybindingPrefs( kId, kMediaKeyTestCommand, kAltShiftG); // Update to version 2 with different keybinding assigned. EXPECT_TRUE(UpdateExtension(kId, path_v2_reassigned, 0)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify it has a command of Alt+Shift+G. accelerator = command_service->FindCommandByName( kId, kMediaKeyTestCommand).accelerator(); EXPECT_EQ(ui::VKEY_G, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_TRUE(accelerator.IsShiftDown()); EXPECT_TRUE(accelerator.IsAltDown()); } IN_PROC_BROWSER_TEST_F(CommandsApiTest, ShortcutRemovedOnUpdateAfterBeingReassignedByUser) { base::ScopedTempDir scoped_temp_dir; EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); base::FilePath pem_path = test_data_dir_. AppendASCII("keybinding").AppendASCII("keybinding.pem"); base::FilePath path_v1 = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v1"), scoped_temp_dir.path().AppendASCII("v1.crx"), pem_path, base::FilePath()); base::FilePath path_v2_unassigned = PackExtensionWithOptions( test_data_dir_.AppendASCII("keybinding").AppendASCII("update") .AppendASCII("v2_unassigned"), scoped_temp_dir.path().AppendASCII("v2_unassigned.crx"), pem_path, base::FilePath()); ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile()); CommandService* command_service = CommandService::Get(browser()->profile()); // Install v1 of the extension. ASSERT_TRUE(InstallExtension(path_v1, 1)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify it has a command of Alt+Shift+F. ui::Accelerator accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_F, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_TRUE(accelerator.IsShiftDown()); EXPECT_TRUE(accelerator.IsAltDown()); // Simulate the user reassigning the keybinding to Alt+Shift+G. command_service->UpdateKeybindingPrefs( kId, manifest_values::kBrowserActionCommandEvent, kAltShiftG); // Update to version 2 without keybinding assigned. EXPECT_TRUE(UpdateExtension(kId, path_v2_unassigned, 0)); EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED) != NULL); // Verify the keybinding is still set. accelerator = command_service->FindCommandByName( kId, manifest_values::kBrowserActionCommandEvent).accelerator(); EXPECT_EQ(ui::VKEY_G, accelerator.key_code()); EXPECT_FALSE(accelerator.IsCtrlDown()); EXPECT_TRUE(accelerator.IsShiftDown()); EXPECT_TRUE(accelerator.IsAltDown()); } // #if defined(OS_CHROMEOS) && !defined(NDEBUG) // TODO(dtseng): Test times out on Chrome OS debug. See http://crbug.com/412456. #define MAYBE_ContinuePropagation DISABLED_ContinuePropagation #else #define MAYBE_ContinuePropagation ContinuePropagation #endif IN_PROC_BROWSER_TEST_F(CommandsApiTest, MAYBE_ContinuePropagation) { // Setup the environment. ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); ASSERT_TRUE(RunExtensionTest("keybinding/continue_propagation")) << message_; ui_test_utils::NavigateToURL( browser(), test_server()->GetURL("files/extensions/test_file.txt")); ResultCatcher catcher; // Activate the shortcut (Ctrl+Shift+F). The page should capture the // keystroke and not the extension since |onCommand| has no event listener // initially. ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_F, true, true, false, false)); ASSERT_TRUE(catcher.GetNextResult()); // Now, the extension should have added an |onCommand| event listener. // Send the same key, but the |onCommand| listener should now receive it. ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_F, true, true, false, false)); ASSERT_TRUE(catcher.GetNextResult()); // The extension should now have removed its |onCommand| event listener. // Finally, the page should again receive the key. ASSERT_TRUE(ui_test_utils::SendKeyPressSync( browser(), ui::VKEY_F, true, true, false, false)); ASSERT_TRUE(catcher.GetNextResult()); } // Test is only applicable on Chrome OS. #if defined(OS_CHROMEOS) // http://crbug.com/410534 #if defined(USE_OZONE) #define MAYBE_ChromeOSConversions DISABLED_ChromeOSConversions #else #define MAYBE_ChromeOSConversions ChromeOSConversions #endif IN_PROC_BROWSER_TEST_F(CommandsApiTest, MAYBE_ChromeOSConversions) { RunChromeOSConversionTest("keybinding/chromeos_conversions"); } #endif // OS_CHROMEOS // Make sure component extensions retain keybindings after removal then // re-adding. IN_PROC_BROWSER_TEST_F(CommandsApiTest, AddRemoveAddComponentExtension) { ASSERT_TRUE(test_server()->Start()); ASSERT_TRUE(RunComponentExtensionTest("keybinding/component")) << message_; extensions::ExtensionSystem::Get(browser()->profile()) ->extension_service() ->component_loader() ->Remove("pkplfbidichfdicaijlchgnapepdginl"); ASSERT_TRUE(RunComponentExtensionTest("keybinding/component")) << message_; } } // namespace extensions