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 "chrome/browser/extensions/active_tab_permission_granter.h" 6 #include "chrome/browser/extensions/api/commands/command_service.h" 7 #include "chrome/browser/extensions/browser_action_test_util.h" 8 #include "chrome/browser/extensions/extension_action.h" 9 #include "chrome/browser/extensions/extension_action_manager.h" 10 #include "chrome/browser/extensions/extension_apitest.h" 11 #include "chrome/browser/extensions/tab_helper.h" 12 #include "chrome/browser/sessions/session_tab_helper.h" 13 #include "chrome/browser/ui/browser.h" 14 #include "chrome/browser/ui/tabs/tab_strip_model.h" 15 #include "chrome/test/base/interactive_test_utils.h" 16 #include "chrome/test/base/ui_test_utils.h" 17 #include "content/public/browser/notification_service.h" 18 #include "content/public/browser/web_contents.h" 19 #include "content/public/test/browser_test_utils.h" 20 #include "extensions/common/extension.h" 21 #include "extensions/common/feature_switch.h" 22 #include "extensions/common/permissions/permissions_data.h" 23 24 using content::WebContents; 25 26 namespace extensions { 27 28 class CommandsApiTest : public ExtensionApiTest { 29 public: 30 CommandsApiTest() {} 31 virtual ~CommandsApiTest() {} 32 33 protected: 34 BrowserActionTestUtil GetBrowserActionsBar() { 35 return BrowserActionTestUtil(browser()); 36 } 37 38 bool IsGrantedForTab(const Extension* extension, 39 const content::WebContents* web_contents) { 40 return PermissionsData::HasAPIPermissionForTab( 41 extension, 42 SessionID::IdForTab(web_contents), 43 APIPermission::kTab); 44 } 45 }; 46 47 class ScriptBadgesCommandsApiTest : public ExtensionApiTest { 48 public: 49 ScriptBadgesCommandsApiTest() { 50 // We cannot add this to CommandsApiTest because then PageActions get 51 // treated like BrowserActions and the PageAction test starts failing. 52 FeatureSwitch::script_badges()->SetOverrideValue( 53 FeatureSwitch::OVERRIDE_ENABLED); 54 } 55 virtual ~ScriptBadgesCommandsApiTest() {} 56 }; 57 58 // Test the basic functionality of the Keybinding API: 59 // - That pressing the shortcut keys should perform actions (activate the 60 // browser action or send an event). 61 // - Note: Page action keybindings are tested in PageAction test below. 62 // - The shortcut keys taken by one extension are not overwritten by the last 63 // installed extension. 64 IN_PROC_BROWSER_TEST_F(CommandsApiTest, Basic) { 65 ASSERT_TRUE(test_server()->Start()); 66 ASSERT_TRUE(RunExtensionTest("keybinding/basics")) << message_; 67 const Extension* extension = GetSingleLoadedExtension(); 68 ASSERT_TRUE(extension) << message_; 69 70 // Load this extension, which uses the same keybindings but sets the page 71 // to different colors. This is so we can see that it doesn't interfere. We 72 // don't test this extension in any other way (it should otherwise be 73 // immaterial to this test). 74 ASSERT_TRUE(RunExtensionTest("keybinding/conflicting")) << message_; 75 76 // Test that there are two browser actions in the toolbar. 77 ASSERT_EQ(2, GetBrowserActionsBar().NumberOfBrowserActions()); 78 79 ui_test_utils::NavigateToURL(browser(), 80 test_server()->GetURL("files/extensions/test_file.txt")); 81 82 // activeTab shouldn't have been granted yet. 83 WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); 84 ASSERT_TRUE(tab); 85 86 EXPECT_FALSE(IsGrantedForTab(extension, tab)); 87 88 // Activate the shortcut (Ctrl+Shift+F). 89 ASSERT_TRUE(ui_test_utils::SendKeyPressSync( 90 browser(), ui::VKEY_F, true, true, false, false)); 91 92 // activeTab should now be granted. 93 EXPECT_TRUE(IsGrantedForTab(extension, tab)); 94 95 // Verify the command worked. 96 bool result = false; 97 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 98 tab, 99 "setInterval(function(){" 100 " if(document.body.bgColor == 'red'){" 101 " window.domAutomationController.send(true)}}, 100)", 102 &result)); 103 ASSERT_TRUE(result); 104 105 // Activate the shortcut (Ctrl+Shift+Y). 106 ASSERT_TRUE(ui_test_utils::SendKeyPressSync( 107 browser(), ui::VKEY_Y, true, true, false, false)); 108 109 result = false; 110 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 111 tab, 112 "setInterval(function(){" 113 " if(document.body.bgColor == 'blue'){" 114 " window.domAutomationController.send(true)}}, 100)", 115 &result)); 116 ASSERT_TRUE(result); 117 } 118 119 // Flaky on linux and chromeos, http://crbug.com/165825 120 #if defined(OS_MACOSX) || defined(OS_WIN) 121 #define MAYBE_PageAction PageAction 122 #else 123 #define MAYBE_PageAction DISABLED_PageAction 124 #endif 125 IN_PROC_BROWSER_TEST_F(CommandsApiTest, MAYBE_PageAction) { 126 ASSERT_TRUE(test_server()->Start()); 127 ASSERT_TRUE(RunExtensionTest("keybinding/page_action")) << message_; 128 const Extension* extension = GetSingleLoadedExtension(); 129 ASSERT_TRUE(extension) << message_; 130 131 { 132 // Load a page, the extension will detect the navigation and request to show 133 // the page action icon. 134 ResultCatcher catcher; 135 ui_test_utils::NavigateToURL(browser(), 136 test_server()->GetURL("files/extensions/test_file.txt")); 137 ASSERT_TRUE(catcher.GetNextResult()); 138 } 139 140 // Make sure it appears and is the right one. 141 ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); 142 int tab_id = SessionTabHelper::FromWebContents( 143 browser()->tab_strip_model()->GetActiveWebContents())->session_id().id(); 144 ExtensionAction* action = 145 ExtensionActionManager::Get(browser()->profile())-> 146 GetPageAction(*extension); 147 ASSERT_TRUE(action); 148 EXPECT_EQ("Make this page red", action->GetTitle(tab_id)); 149 150 // Activate the shortcut (Alt+Shift+F). 151 ASSERT_TRUE(ui_test_utils::SendKeyPressSync( 152 browser(), ui::VKEY_F, false, true, true, false)); 153 154 // Verify the command worked (the page action turns the page red). 155 WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); 156 bool result = false; 157 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 158 tab, 159 "setInterval(function(){" 160 " if(document.body.bgColor == 'red'){" 161 " window.domAutomationController.send(true)}}, 100)", 162 &result)); 163 ASSERT_TRUE(result); 164 } 165 166 // Checked-in in a disabled state, because the necessary functionality to 167 // automatically verify that the test works hasn't been implemented for the 168 // script badges yet (see http://crbug.com/140016). The test results, can be 169 // verified manually by running the test and verifying that the synthesized 170 // popup for script badges appear. When bug 140016 has been fixed, the popup 171 // code can signal to the test that the test passed. 172 // TODO(finnur): Enable this test once the bug is fixed. 173 IN_PROC_BROWSER_TEST_F(ScriptBadgesCommandsApiTest, DISABLED_ScriptBadge) { 174 ASSERT_TRUE(test_server()->Start()); 175 ASSERT_TRUE(RunExtensionTest("keybinding/script_badge")) << message_; 176 const Extension* extension = GetSingleLoadedExtension(); 177 ASSERT_TRUE(extension) << message_; 178 179 { 180 ResultCatcher catcher; 181 // Tell the extension to update the script badge state. 182 ui_test_utils::NavigateToURL( 183 browser(), GURL(extension->GetResourceURL("show.html"))); 184 ASSERT_TRUE(catcher.GetNextResult()); 185 } 186 187 { 188 ResultCatcher catcher; 189 // Activate the shortcut (Ctrl+Shift+F). 190 ASSERT_TRUE(ui_test_utils::SendKeyPressSync( 191 browser(), ui::VKEY_F, true, true, false, false)); 192 ASSERT_TRUE(catcher.GetNextResult()); 193 } 194 } 195 196 // This test validates that the getAll query API function returns registered 197 // commands as well as synthesized ones and that inactive commands (like the 198 // synthesized ones are in nature) have no shortcuts. 199 IN_PROC_BROWSER_TEST_F(CommandsApiTest, SynthesizedCommand) { 200 ASSERT_TRUE(test_server()->Start()); 201 ASSERT_TRUE(RunExtensionTest("keybinding/synthesized")) << message_; 202 } 203 204 // This test validates that an extension cannot request a shortcut that is 205 // already in use by Chrome. 206 IN_PROC_BROWSER_TEST_F(CommandsApiTest, DontOverwriteSystemShortcuts) { 207 ASSERT_TRUE(test_server()->Start()); 208 209 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); 210 211 ASSERT_TRUE(RunExtensionTest("keybinding/dont_overwrite_system")) << message_; 212 213 ui_test_utils::NavigateToURL(browser(), 214 test_server()->GetURL("files/extensions/test_file.txt")); 215 216 WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); 217 ASSERT_TRUE(tab); 218 219 // Activate the shortcut (Alt+Shift+F) to make page blue. 220 { 221 ResultCatcher catcher; 222 ASSERT_TRUE(ui_test_utils::SendKeyPressSync( 223 browser(), ui::VKEY_F, false, true, true, false)); 224 ASSERT_TRUE(catcher.GetNextResult()); 225 } 226 227 bool result = false; 228 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 229 tab, 230 "setInterval(function() {" 231 " if (document.body.bgColor == 'blue') {" 232 " window.domAutomationController.send(true)}}, 100)", 233 &result)); 234 ASSERT_TRUE(result); 235 236 // Activate the shortcut (Ctrl+F) to make page red (should not work). 237 ASSERT_TRUE(ui_test_utils::SendKeyPressSync( 238 browser(), ui::VKEY_F, true, false, false, false)); 239 240 // The page should still be blue. 241 result = false; 242 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 243 tab, 244 "setInterval(function() {" 245 " if (document.body.bgColor == 'blue') {" 246 " window.domAutomationController.send(true)}}, 100)", 247 &result)); 248 ASSERT_TRUE(result); 249 } 250 251 #if defined(OS_WIN) 252 // Currently this feature is implemented on Windows only. 253 #define MAYBE_AllowDuplicatedMediaKeys AllowDuplicatedMediaKeys 254 #else 255 #define MAYBE_AllowDuplicatedMediaKeys DISABLED_AllowDuplicatedMediaKeys 256 #endif 257 258 // Test that media keys go to all extensions that register for them. 259 IN_PROC_BROWSER_TEST_F(CommandsApiTest, MAYBE_AllowDuplicatedMediaKeys) { 260 ResultCatcher catcher; 261 ASSERT_TRUE(RunExtensionTest("keybinding/non_global_media_keys_0")) 262 << message_; 263 ASSERT_TRUE(catcher.GetNextResult()); 264 ASSERT_TRUE(RunExtensionTest("keybinding/non_global_media_keys_1")) 265 << message_; 266 ASSERT_TRUE(catcher.GetNextResult()); 267 268 // Activate the Media Stop key. 269 ASSERT_TRUE(ui_test_utils::SendKeyPressSync( 270 browser(), ui::VKEY_MEDIA_STOP, false, false, false, false)); 271 272 // We should get two success result. 273 ASSERT_TRUE(catcher.GetNextResult()); 274 ASSERT_TRUE(catcher.GetNextResult()); 275 } 276 277 // This test validates that update (including removal) of keybinding preferences 278 // works correctly. 279 IN_PROC_BROWSER_TEST_F(CommandsApiTest, UpdateKeybindingPrefsTest) { 280 #if defined(OS_MACOSX) 281 // Send "Tab" on OS X to move the focus, otherwise the omnibox will intercept 282 // the key presses we send below. 283 ASSERT_TRUE(ui_test_utils::SendKeyPressSync( 284 browser(), ui::VKEY_TAB, false, false, false, false)); 285 #endif 286 ResultCatcher catcher; 287 ASSERT_TRUE(RunExtensionTest("keybinding/command_update")); 288 ASSERT_TRUE(catcher.GetNextResult()); 289 const Extension* extension = GetSingleLoadedExtension(); 290 291 CommandService* command_service = CommandService::Get(browser()->profile()); 292 extensions::CommandMap named_commands; 293 command_service->GetNamedCommands(extension->id(), 294 extensions::CommandService::ACTIVE_ONLY, 295 extensions::CommandService::ANY_SCOPE, 296 &named_commands); 297 EXPECT_EQ(3u, named_commands.size()); 298 299 const char kCommandNameC[] = "command_C"; 300 command_service->RemoveKeybindingPrefs(extension->id(), kCommandNameC); 301 command_service->GetNamedCommands(extension->id(), 302 extensions::CommandService::ACTIVE_ONLY, 303 extensions::CommandService::ANY_SCOPE, 304 &named_commands); 305 EXPECT_EQ(2u, named_commands.size()); 306 307 // Send "Alt+C", it shouldn't work because it has been removed. 308 ASSERT_TRUE(ui_test_utils::SendKeyPressSync( 309 browser(), ui::VKEY_C, false, false, true, false)); 310 311 const char kCommandNameB[] = "command_B"; 312 const char kKeyStroke[] = "Alt+A"; 313 command_service->UpdateKeybindingPrefs(extension->id(), 314 kCommandNameB, 315 kKeyStroke); 316 command_service->GetNamedCommands(extension->id(), 317 extensions::CommandService::ACTIVE_ONLY, 318 extensions::CommandService::ANY_SCOPE, 319 &named_commands); 320 EXPECT_EQ(1u, named_commands.size()); 321 322 // Activate the shortcut (Alt+A). 323 ASSERT_TRUE(ui_test_utils::SendKeyPressSync( 324 browser(), ui::VKEY_A, false, false, true, false)); 325 ASSERT_TRUE(catcher.GetNextResult()) << message_; 326 } 327 328 } // namespace extensions 329