| // 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/containers/contains.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.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/component_loader.h" |
| #include "chrome/browser/extensions/extension_apitest.h" |
| #include "chrome/browser/extensions/extension_service.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/browser_window.h" |
| #include "chrome/browser/ui/extensions/extension_action_test_helper.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/toolbar/toolbar_actions_model.h" |
| #include "chrome/test/base/interactive_test_utils.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/sessions/content/session_tab_helper.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/javascript_test_observer.h" |
| #include "extensions/browser/extension_action.h" |
| #include "extensions/browser/extension_action_manager.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/test_event_router_observer.h" |
| #include "extensions/common/api/extension_action/action_info_test_util.h" |
| #include "extensions/common/extension.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" |
| #include "extensions/test/test_extension_dir.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "third_party/blink/public/common/switches.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"; |
| |
| #if BUILDFLAG(IS_MAC) |
| const char kBookmarkKeybinding[] = "Command+D"; |
| #else |
| const char kBookmarkKeybinding[] = "Ctrl+D"; |
| #endif // BUILDFLAG(IS_MAC) |
| |
| bool SendBookmarkKeyPressSync(Browser* browser) { |
| return ui_test_utils::SendKeyPressSync(browser, ui::VKEY_D, |
| #if BUILDFLAG(IS_MAC) |
| 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(const DomMessageListener&) = delete; |
| DomMessageListener& operator=(const DomMessageListener&) = delete; |
| |
| ~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_; |
| }; |
| |
| 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(); |
| } |
| |
| // Programmatically (from the extension) sets the action of |extension| to be |
| // visible on the tab with the given |tab_id|. Expects the action is *not* |
| // visible to start. |
| void SetActionVisibleOnTab(Profile* profile, |
| const Extension& extension, |
| int tab_id) { |
| ExtensionActionManager* action_manager = ExtensionActionManager::Get(profile); |
| const ExtensionAction* extension_action = |
| action_manager->GetExtensionAction(extension); |
| ASSERT_TRUE(extension_action); |
| EXPECT_FALSE(extension_action->GetIsVisible(tab_id)); |
| |
| constexpr char kScriptTemplate[] = |
| R"(chrome.pageAction.show(%d, () => { |
| domAutomationController.send( |
| chrome.runtime.lastError ? |
| chrome.runtime.lastError.message : |
| 'success'); |
| });)"; |
| |
| std::string set_result = browsertest_util::ExecuteScriptInBackgroundPage( |
| profile, extension.id(), base::StringPrintf(kScriptTemplate, tab_id)); |
| EXPECT_EQ("success", set_result); |
| EXPECT_TRUE(extension_action->GetIsVisible(tab_id)); |
| } |
| |
| // Sends a keypress with the given |keyboard_code| to the specified |extension|. |
| // If |expect_dispatch| is true, expects pageAction.onClicked to be dispatched |
| // to the extension. Otherwise, expects it is not sent. |
| void SendKeyPressToAction(Browser* browser, |
| const Extension& extension, |
| ui::KeyboardCode keyboard_code, |
| const char* event_name, |
| bool expect_dispatch) { |
| ExtensionTestMessageListener click_listener("clicked", |
| false); // Won't reply. |
| click_listener.set_extension_id(extension.id()); |
| |
| Profile* profile = browser->profile(); |
| EventRouter* event_router = EventRouter::Get(profile); |
| TestEventRouterObserver event_tracker(event_router); |
| // Activate the shortcut (Alt+Shift+F). |
| if (!ui_test_utils::SendKeyPressSync(browser, keyboard_code, false, true, |
| true, false)) { |
| ADD_FAILURE() << "Could not send key press!"; |
| return; |
| } |
| base::RunLoop().RunUntilIdle(); |
| // Check that the event was dispatched if and only if we expected it to be. |
| EXPECT_EQ(expect_dispatch, |
| base::Contains(event_tracker.dispatched_events(), event_name)); |
| |
| // Do a round-trip to the extension renderer. This serves as a pseudo- |
| // RunUntilIdle()-type of method for the extension renderer itself, since |
| // test.sendMessage() is FIFO. |
| // This allows us to return the result of click_listener.was_satisfied(), |
| // rather than using WaitUntilSatisfied(), which in turn allows this method |
| // to exercise both the case of expecting dispatch and expecting *not* to |
| // dispatch. |
| constexpr char kScript[] = |
| R"(chrome.test.sendMessage( |
| 'run loop hack', |
| () => { |
| domAutomationController.send('success'); |
| });)"; |
| std::string set_result = browsertest_util::ExecuteScriptInBackgroundPage( |
| profile, extension.id(), kScript); |
| EXPECT_EQ("success", set_result); |
| EXPECT_EQ(expect_dispatch, click_listener.was_satisfied()); |
| } |
| |
| // Given an |action_type|, returns the corresponding command key. |
| const char* GetCommandKeyForActionType(ActionInfo::Type action_type) { |
| const char* command_key = nullptr; |
| switch (action_type) { |
| case ActionInfo::TYPE_BROWSER: |
| command_key = manifest_values::kBrowserActionCommandEvent; |
| break; |
| case ActionInfo::TYPE_PAGE: |
| command_key = manifest_values::kPageActionCommandEvent; |
| break; |
| case ActionInfo::TYPE_ACTION: |
| command_key = manifest_values::kActionCommandEvent; |
| break; |
| } |
| |
| return command_key; |
| } |
| |
| } // namespace |
| |
| class CommandsApiTest : public ExtensionApiTest { |
| public: |
| CommandsApiTest() {} |
| ~CommandsApiTest() override {} |
| |
| void SetUpOnMainThread() override { |
| ExtensionApiTest::SetUpOnMainThread(); |
| #if BUILDFLAG(IS_MAC) |
| // ExtensionKeybindingRegistryViews doesn't get registered until BrowserView |
| // is activated at least once. |
| // TODO(crbug.com/839469): Registry creation should happen independent of |
| // activation. Focus manager lifetime may make this tricky to untangle. |
| // TODO(crbug.com/650859): Reassess after activation is restored in the |
| // focus manager. |
| ui_test_utils::BrowserActivationWaiter waiter(browser()); |
| ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); |
| waiter.WaitForActivation(); |
| ASSERT_TRUE(browser()->window()->IsActive()); |
| #endif |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| ExtensionApiTest::SetUpCommandLine(command_line); |
| // Some builders are flaky due to slower loading interacting with |
| // deferred commits. This primarily impacts chromeos for the test |
| // CommandsApiTest.ContinuePropagation. |
| command_line->AppendSwitch(blink::switches::kAllowPreCommitInput); |
| } |
| |
| protected: |
| bool IsGrantedForTab(const Extension* extension, |
| const content::WebContents* web_contents) { |
| return extension->permissions_data()->HasAPIPermissionForTab( |
| sessions::SessionTabHelper::IdForTab(web_contents).id(), |
| mojom::APIPermissionID::kTab); |
| } |
| |
| // Returns true if the extension with the given |extension_id| has an active |
| // command associated with an action of the given |action_type|. |
| bool HasActiveActionCommand(const ExtensionId& extension_id, |
| ActionInfo::Type action_type) { |
| bool active = false; |
| Command command; |
| CommandService* const command_service = |
| CommandService::Get(browser()->profile()); |
| bool found_command = command_service->GetExtensionActionCommand( |
| extension_id, action_type, CommandService::ALL, &command, &active); |
| return found_command && active; |
| } |
| |
| // Navigates to a test URL and return the ID of the navigated tab. |
| int NavigateToTestURLAndReturnTabId() { |
| EXPECT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), |
| embedded_test_server()->GetURL("/extensions/test_file.txt"))); |
| return sessions::SessionTabHelper::FromWebContents( |
| browser()->tab_strip_model()->GetActiveWebContents()) |
| ->session_id() |
| .id(); |
| } |
| }; |
| |
| class IncognitoCommandsApiTest : public CommandsApiTest, |
| public testing::WithParamInterface<bool> {}; |
| |
| // A parameterized version to allow testing with different action types. |
| class ActionCommandsApiTest |
| : public CommandsApiTest, |
| public testing::WithParamInterface<ActionInfo::Type> {}; |
| |
| // 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(embedded_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_; |
| |
| auto browser_actions_bar = ExtensionActionTestHelper::Create(browser()); |
| // Test that there are two browser actions in the toolbar. |
| ASSERT_EQ(2, browser_actions_bar->NumberOfBrowserActions()); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL("/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, InactivePageActionDoesntTrigger) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| ASSERT_TRUE(RunExtensionTest("keybinding/page_action")) << message_; |
| const Extension* extension = GetSingleLoadedExtension(); |
| ASSERT_TRUE(extension) << message_; |
| |
| const int tab_id = NavigateToTestURLAndReturnTabId(); |
| |
| ExtensionActionManager* action_manager = |
| ExtensionActionManager::Get(profile()); |
| const ExtensionAction* extension_action = |
| action_manager->GetExtensionAction(*extension); |
| ASSERT_TRUE(extension_action); |
| EXPECT_FALSE(extension_action->GetIsVisible(tab_id)); |
| |
| // If the page action is disabled / hidden, the event shouldn't be dispatched. |
| bool expect_dispatch = false; |
| SendKeyPressToAction(browser(), *extension, ui::VKEY_F, |
| "pageAction.onClicked", expect_dispatch); |
| } |
| |
| // Tests that a page action that is unpinned and only shown within the |
| // extensions menu will still properly trigger when the keybinding is used. |
| IN_PROC_BROWSER_TEST_F(CommandsApiTest, UnpinnedPageActionTriggers) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| ASSERT_TRUE(RunExtensionTest("keybinding/page_action")) << message_; |
| const Extension* extension = GetSingleLoadedExtension(); |
| ASSERT_TRUE(extension) << message_; |
| |
| std::unique_ptr<ExtensionActionTestHelper> test_helper = |
| ExtensionActionTestHelper::Create(browser()); |
| RunScheduledLayouts(); |
| EXPECT_EQ(0, test_helper->VisibleBrowserActions()); |
| |
| const int tab_id = NavigateToTestURLAndReturnTabId(); |
| SetActionVisibleOnTab(profile(), *extension, tab_id); |
| |
| ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); |
| |
| constexpr bool kExpectDispatch = true; |
| SendKeyPressToAction(browser(), *extension, ui::VKEY_F, |
| "pageAction.onClicked", kExpectDispatch); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(CommandsApiTest, PageActionKeyUpdated) { |
| ASSERT_TRUE(embedded_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); |
| |
| const int tab_id = NavigateToTestURLAndReturnTabId(); |
| |
| SetActionVisibleOnTab(profile(), *extension, tab_id); |
| ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); |
| |
| bool expect_dispatch = true; |
| SendKeyPressToAction(browser(), *extension, ui::VKEY_G, |
| "pageAction.onClicked", expect_dispatch); |
| } |
| |
| // Verify that keyboard shortcut takes effect without reloading the extension. |
| // Regression test for https://crbug.com/1190476. |
| IN_PROC_BROWSER_TEST_F(CommandsApiTest, ActionKeyUpdated) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| ASSERT_TRUE(RunExtensionTest("keybinding/action")) << message_; |
| const Extension* extension = GetSingleLoadedExtension(); |
| ASSERT_TRUE(extension) << message_; |
| |
| // Simulate the user changing the keybinding. |
| CommandService* command_service = CommandService::Get(browser()->profile()); |
| command_service->UpdateKeybindingPrefs( |
| extension->id(), manifest_values::kActionCommandEvent, "Ctrl+Shift+Y"); |
| |
| // Verify that the action event occurs for the new keyboard shortcut. |
| ResultCatcher catcher; |
| ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_Y, true, true, |
| false, false)); |
| ASSERT_TRUE(catcher.GetNextResult()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(CommandsApiTest, PageActionOverrideChromeShortcut) { |
| ASSERT_TRUE(embedded_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 override the print shortcut. |
| #if BUILDFLAG(IS_MAC) |
| std::string print_shortcut = "Command+P"; |
| #else |
| std::string print_shortcut = "Ctrl+P"; |
| #endif |
| command_service->UpdateKeybindingPrefs( |
| extension->id(), manifest_values::kPageActionCommandEvent, |
| print_shortcut); |
| |
| const int tab_id = NavigateToTestURLAndReturnTabId(); |
| |
| SetActionVisibleOnTab(profile(), *extension, tab_id); |
| ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); |
| |
| ExtensionTestMessageListener test_listener(false); // Won't reply. |
| test_listener.set_extension_id(extension->id()); |
| |
| // Note: The following incantation uses too many custom bits to comfortably |
| // fit into SendKeyPressToAction(); do it manually. |
| bool control_is_modifier = false; |
| bool command_is_modifier = false; |
| #if BUILDFLAG(IS_MAC) |
| command_is_modifier = true; |
| #else |
| control_is_modifier = true; |
| #endif |
| |
| // Activate the omnibox. This checks to ensure that the extension shortcut |
| // still works even if the WebContents isn't focused. |
| ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_L, |
| control_is_modifier, false, false, |
| command_is_modifier)); |
| |
| // Activate the shortcut. |
| ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_P, |
| control_is_modifier, false, false, |
| command_is_modifier)); |
| |
| EXPECT_TRUE(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(embedded_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(embedded_test_server()->Start()); |
| |
| ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); |
| |
| ASSERT_TRUE(RunExtensionTest("keybinding/dont_overwrite_system")) << message_; |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL("/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 Ctrl+F shortcut (shouldn't work). |
| // Since keypresses are sent synchronously, we can check this by first sending |
| // Ctrl+F (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+F, it is safe to say we |
| // won't receive one. |
| 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 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(embedded_test_server()->Start()); |
| |
| ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); |
| |
| 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); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL( |
| "/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 BUILDFLAG(IS_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::ScopedAllowBlockingForTesting allow_blocking; |
| 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.GetPath().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.GetPath().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::ScopedAllowBlockingForTesting allow_blocking; |
| 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.GetPath().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.GetPath().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+J. |
| accelerator = command_service->FindCommandByName( |
| kId, manifest_values::kBrowserActionCommandEvent).accelerator(); |
| EXPECT_EQ(ui::VKEY_J, accelerator.key_code()); |
| EXPECT_FALSE(accelerator.IsCtrlDown()); |
| EXPECT_TRUE(accelerator.IsShiftDown()); |
| EXPECT_TRUE(accelerator.IsAltDown()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(CommandsApiTest, ShortcutRemovedOnUpdate) { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| 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.GetPath().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.GetPath().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::ScopedAllowBlockingForTesting allow_blocking; |
| 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.GetPath().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.GetPath().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::ScopedAllowBlockingForTesting allow_blocking; |
| 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.GetPath().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.GetPath().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::ScopedAllowBlockingForTesting allow_blocking; |
| 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.GetPath().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.GetPath().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::ScopedAllowBlockingForTesting allow_blocking; |
| 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.GetPath().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.GetPath().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 BUILDFLAG(IS_CHROMEOS_ASH) && !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(embedded_test_server()->Start()); |
| ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); |
| ASSERT_TRUE(RunExtensionTest("keybinding/continue_propagation")) << message_; |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL("/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 BUILDFLAG(IS_CHROMEOS) |
| IN_PROC_BROWSER_TEST_F(CommandsApiTest, ChromeOSConversions) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); |
| ASSERT_TRUE(RunExtensionTest("keybinding/chromeos_conversions")) << message_; |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL("/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 // BUILDFLAG(IS_CHROMEOS) |
| |
| // Make sure component extensions retain keybindings after removal then |
| // re-adding. |
| IN_PROC_BROWSER_TEST_F(CommandsApiTest, AddRemoveAddComponentExtension) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| ASSERT_TRUE( |
| RunExtensionTest("keybinding/component", {}, {.load_as_component = true})) |
| << message_; |
| |
| extensions::ExtensionSystem::Get(browser()->profile()) |
| ->extension_service() |
| ->component_loader() |
| ->Remove("pkplfbidichfdicaijlchgnapepdginl"); |
| |
| ASSERT_TRUE( |
| RunExtensionTest("keybinding/component", {}, {.load_as_component = true})) |
| << message_; |
| } |
| |
| // Validate parameters sent along with an extension event, in response to |
| // command being triggered. |
| IN_PROC_BROWSER_TEST_F(CommandsApiTest, TabParameter) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| ASSERT_TRUE(RunExtensionTest("keybinding/tab_parameter")) << message_; |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL("/extensions/test_file.txt"))); |
| const Extension* extension = GetSingleLoadedExtension(); |
| ASSERT_TRUE(extension) << message_; |
| ResultCatcher catcher; |
| EXPECT_TRUE(content::WaitForLoadStop( |
| browser()->tab_strip_model()->GetActiveWebContents())); |
| ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_Y, true, true, |
| false, false)); // Ctrl+Shift+Y |
| EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); |
| } |
| |
| // Disable this test only for Lacros due to flakiness. crbug.com/1154365. |
| #if BUILDFLAG(IS_CHROMEOS_LACROS) |
| #define MAYBE_IncognitoMode DISABLED_IncognitoMode |
| #else |
| #define MAYBE_IncognitoMode IncognitoMode |
| #endif |
| // Test Keybinding in incognito mode. |
| IN_PROC_BROWSER_TEST_P(IncognitoCommandsApiTest, MAYBE_IncognitoMode) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| bool is_incognito_enabled = GetParam(); |
| |
| ASSERT_TRUE(RunExtensionTest("keybinding/basics", {}, |
| {.allow_in_incognito = is_incognito_enabled})) |
| << message_; |
| |
| // Open incognito window and navigate to test page. |
| Browser* incognito_browser = OpenURLOffTheRecord( |
| browser()->profile(), |
| embedded_test_server()->GetURL("/extensions/test_file.html")); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| incognito_browser, |
| embedded_test_server()->GetURL("/extensions/test_file.txt"))); |
| |
| TestEventRouterObserver test_observer( |
| EventRouter::Get(incognito_browser->profile())); |
| |
| // Activate the browser action shortcut (Ctrl+Shift+F). |
| ASSERT_TRUE(ui_test_utils::SendKeyPressSync(incognito_browser, ui::VKEY_F, |
| true, true, false, false)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ(is_incognito_enabled, |
| base::Contains(test_observer.dispatched_events(), |
| "browserAction.onClicked")); |
| |
| test_observer.ClearEvents(); |
| |
| // Activate the command shortcut (Ctrl+Shift+Y). |
| ASSERT_TRUE(ui_test_utils::SendKeyPressSync(incognito_browser, ui::VKEY_Y, |
| true, true, false, false)); |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_EQ( |
| is_incognito_enabled, |
| base::Contains(test_observer.dispatched_events(), "commands.onCommand")); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(ActionCommandsApiTest, |
| TriggeringCommandTriggersListener) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| const ActionInfo::Type action_type = GetParam(); |
| |
| // Load a test extension that has a command that invokes the action, and sends |
| // a message when the action is invoked. |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Extension Action Listener Test", |
| "manifest_version": %d, |
| "version": "0.1", |
| "commands": { |
| "%s": { |
| "suggested_key": { |
| "default": "Alt+Shift+U" |
| } |
| } |
| }, |
| "%s": {}, |
| "background": { %s } |
| } |
| )"; |
| constexpr char kBackgroundScriptTemplate[] = R"( |
| chrome.%s.onClicked.addListener(() => { |
| chrome.test.sendMessage('clicked'); |
| }); |
| chrome.test.sendMessage('ready'); |
| )"; |
| const char* background_specification = |
| action_type == ActionInfo::TYPE_ACTION |
| ? R"("service_worker": "background.js")" |
| : R"("scripts": ["background.js"])"; |
| |
| TestExtensionDir test_dir; |
| test_dir.WriteManifest(base::StringPrintf( |
| kManifestTemplate, GetManifestVersionForActionType(action_type), |
| GetCommandKeyForActionType(action_type), |
| GetManifestKeyForActionType(action_type), background_specification)); |
| test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), |
| base::StringPrintf(kBackgroundScriptTemplate, |
| GetAPINameForActionType(action_type))); |
| |
| ExtensionTestMessageListener listener("ready", /*will_reply=*/false); |
| const Extension* extension = LoadExtension(test_dir.UnpackedPath()); |
| ASSERT_TRUE(extension); |
| ASSERT_TRUE(listener.WaitUntilSatisfied()); |
| ASSERT_TRUE(HasActiveActionCommand(extension->id(), action_type)); |
| |
| const int tab_id = NavigateToTestURLAndReturnTabId(); |
| |
| // If the action is a page action, it's hidden by default. Show it. |
| if (action_type == ActionInfo::TYPE_PAGE) { |
| SetActionVisibleOnTab(profile(), *extension, tab_id); |
| ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); |
| } |
| |
| ExtensionTestMessageListener click_listener("clicked", /*will_reply=*/false); |
| EXPECT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_U, false, |
| true, true, false)); |
| EXPECT_TRUE(click_listener.WaitUntilSatisfied()); |
| } |
| |
| // Tests that triggering a command associated with an action opens an |
| // extension's popup. |
| IN_PROC_BROWSER_TEST_P(ActionCommandsApiTest, TriggeringCommandTriggersPopup) { |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| const ActionInfo::Type action_type = GetParam(); |
| |
| // Load an extension that specifies a command to invoke the action, and has |
| // a default popup. |
| constexpr char kManifestTemplate[] = R"( |
| { |
| "name": "Extension Action Listener Test", |
| "manifest_version": %d, |
| "version": "0.1", |
| "commands": { |
| "%s": { |
| "suggested_key": { |
| "default": "Alt+Shift+U" |
| } |
| } |
| }, |
| "%s": {"default_popup": "popup.html"} |
| } |
| )"; |
| constexpr char kPopupHtml[] = R"( |
| <!doctype html> |
| <html> |
| <script src="popup.js"></script> |
| </html> |
| )"; |
| constexpr char kPopupJs[] = "chrome.test.notifyPass();"; |
| |
| TestExtensionDir test_dir; |
| test_dir.WriteManifest(base::StringPrintf( |
| kManifestTemplate, GetManifestVersionForActionType(action_type), |
| GetCommandKeyForActionType(action_type), |
| GetManifestKeyForActionType(action_type))); |
| test_dir.WriteFile(FILE_PATH_LITERAL("popup.html"), kPopupHtml); |
| test_dir.WriteFile(FILE_PATH_LITERAL("popup.js"), kPopupJs); |
| |
| const Extension* extension = LoadExtension(test_dir.UnpackedPath()); |
| ASSERT_TRUE(extension); |
| ASSERT_TRUE(HasActiveActionCommand(extension->id(), action_type)); |
| |
| const int tab_id = NavigateToTestURLAndReturnTabId(); |
| |
| if (action_type == ActionInfo::TYPE_PAGE) { |
| // Note: We don't use SetActionVisibleOnTab() here because it relies on a |
| // background page, which this extension doesn't have. |
| ExtensionActionManager::Get(profile()) |
| ->GetExtensionAction(*extension) |
| ->SetIsVisible(tab_id, true); |
| ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); |
| } |
| |
| ResultCatcher catcher; |
| // Invoke the action, and wait for the popup to show. |
| ASSERT_TRUE(ui_test_utils::SendKeyPressSync(browser(), ui::VKEY_U, false, |
| true, true, false)); |
| EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); |
| EXPECT_TRUE(ExtensionActionTestHelper::Create(browser())->HasPopup()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| ActionCommandsApiTest, |
| testing::Values(ActionInfo::TYPE_BROWSER, |
| ActionInfo::TYPE_PAGE, |
| ActionInfo::TYPE_ACTION)); |
| |
| INSTANTIATE_TEST_SUITE_P(All, IncognitoCommandsApiTest, testing::Bool()); |
| |
| } // namespace extensions |