1 // Copyright (c) 2013 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 <memory>
6 #include <utility>
7 
8 #include "base/threading/thread_restrictions.h"
9 #include "base/values.h"
10 #include "build/build_config.h"
11 #include "chrome/browser/extensions/api/commands/command_service.h"
12 #include "chrome/browser/extensions/extension_apitest.h"
13 #include "chrome/common/pref_names.h"
14 #include "components/prefs/scoped_user_pref_update.h"
15 #include "content/public/test/browser_test.h"
16 #include "content/public/test/test_utils.h"
17 #include "extensions/browser/extension_registry.h"
18 #include "extensions/common/api/extension_action/action_info.h"
19 #include "extensions/common/manifest_constants.h"
20 
21 namespace {
22 const char kBasicBrowserActionKeybinding[] = "Ctrl+Shift+F";
23 const char kBasicNamedKeybinding[] = "Ctrl+Shift+Y";
24 const char kBasicAlternateKeybinding[] = "Ctrl+Shift+G";
25 const char kBasicNamedCommand[] = "toggle-feature";
26 
27 // Get another command platform, whcih is used for simulating a command has been
28 // assigned with a shortcut on another platform.
GetAnotherCommandPlatform()29 std::string GetAnotherCommandPlatform() {
30 #if defined(OS_WIN)
31   return extensions::manifest_values::kKeybindingPlatformMac;
32 #elif defined(OS_MAC)
33   return extensions::manifest_values::kKeybindingPlatformChromeOs;
34 #elif defined(OS_CHROMEOS)
35   return extensions::manifest_values::kKeybindingPlatformLinux;
36 #elif defined(OS_LINUX)
37   return extensions::manifest_values::kKeybindingPlatformWin;
38 #else
39   return "";
40 #endif
41 }
42 
43 }  // namespace
44 
45 namespace extensions {
46 
47 typedef ExtensionApiTest CommandServiceTest;
48 
IN_PROC_BROWSER_TEST_F(CommandServiceTest,RemoveShortcutSurvivesUpdate)49 IN_PROC_BROWSER_TEST_F(CommandServiceTest, RemoveShortcutSurvivesUpdate) {
50   base::ScopedAllowBlockingForTesting allow_blocking;
51   base::ScopedTempDir scoped_temp_dir;
52   EXPECT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
53   base::FilePath pem_path = test_data_dir_.
54       AppendASCII("keybinding").AppendASCII("keybinding.pem");
55   base::FilePath path_v1 =
56       PackExtensionWithOptions(test_data_dir_.AppendASCII("keybinding")
57                                    .AppendASCII("update")
58                                    .AppendASCII("v1"),
59                                scoped_temp_dir.GetPath().AppendASCII("v1.crx"),
60                                pem_path, base::FilePath());
61   base::FilePath path_v2 =
62       PackExtensionWithOptions(test_data_dir_.AppendASCII("keybinding")
63                                    .AppendASCII("update")
64                                    .AppendASCII("v2"),
65                                scoped_temp_dir.GetPath().AppendASCII("v2.crx"),
66                                pem_path, base::FilePath());
67 
68   ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile());
69   CommandService* command_service = CommandService::Get(browser()->profile());
70 
71   const char kId[] = "pgoakhfeplldmjheffidklpoklkppipp";
72 
73   // Install v1 of the extension.
74   ASSERT_TRUE(InstallExtension(path_v1, 1));
75   EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED));
76 
77   // Verify it has a command of Alt+Shift+F.
78   ui::Accelerator accelerator = command_service->FindCommandByName(
79       kId, manifest_values::kBrowserActionCommandEvent).accelerator();
80   EXPECT_EQ(ui::VKEY_F, accelerator.key_code());
81   EXPECT_FALSE(accelerator.IsCtrlDown());
82   EXPECT_TRUE(accelerator.IsShiftDown());
83   EXPECT_TRUE(accelerator.IsAltDown());
84 
85   // Remove the keybinding.
86   command_service->RemoveKeybindingPrefs(
87       kId, manifest_values::kBrowserActionCommandEvent);
88 
89   // Verify it got removed.
90   accelerator = command_service->FindCommandByName(
91       kId, manifest_values::kBrowserActionCommandEvent).accelerator();
92   EXPECT_EQ(ui::VKEY_UNKNOWN, accelerator.key_code());
93 
94   // Update to version 2.
95   EXPECT_TRUE(UpdateExtension(kId, path_v2, 0));
96   EXPECT_TRUE(registry->GetExtensionById(kId, ExtensionRegistry::ENABLED));
97 
98   // Verify it is still set to nothing.
99   accelerator = command_service->FindCommandByName(
100       kId, manifest_values::kBrowserActionCommandEvent).accelerator();
101   EXPECT_EQ(ui::VKEY_UNKNOWN, accelerator.key_code());
102 }
103 
IN_PROC_BROWSER_TEST_F(CommandServiceTest,RemoveKeybindingPrefsShouldBePlatformSpecific)104 IN_PROC_BROWSER_TEST_F(CommandServiceTest,
105                        RemoveKeybindingPrefsShouldBePlatformSpecific) {
106   base::FilePath extension_dir =
107       test_data_dir_.AppendASCII("keybinding").AppendASCII("basics");
108   const Extension* extension = InstallExtension(extension_dir, 1);
109   ASSERT_TRUE(extension);
110 
111   DictionaryPrefUpdate updater(browser()->profile()->GetPrefs(),
112                                prefs::kExtensionCommands);
113   base::DictionaryValue* bindings = updater.Get();
114 
115   // Simulate command |toggle-feature| has been assigned with a shortcut on
116   // another platform.
117   std::string anotherPlatformKey = GetAnotherCommandPlatform() + ":Alt+G";
118   const char kNamedCommandName[] = "toggle-feature";
119   auto keybinding = std::make_unique<base::DictionaryValue>();
120   keybinding->SetString("extension", extension->id());
121   keybinding->SetString("command_name", kNamedCommandName);
122   keybinding->SetBoolean("global", false);
123   bindings->Set(anotherPlatformKey, std::move(keybinding));
124 
125   CommandService* command_service = CommandService::Get(browser()->profile());
126   command_service->RemoveKeybindingPrefs(extension->id(), kNamedCommandName);
127 
128   // Removal of keybinding preference should be platform-specific, so the key on
129   // another platform should always remained.
130   EXPECT_TRUE(bindings->HasKey(anotherPlatformKey));
131 }
132 
IN_PROC_BROWSER_TEST_F(CommandServiceTest,GetExtensionActionCommandQueryAll)133 IN_PROC_BROWSER_TEST_F(CommandServiceTest,
134                        GetExtensionActionCommandQueryAll) {
135   base::FilePath extension_dir =
136       test_data_dir_.AppendASCII("keybinding").AppendASCII("basics");
137   const Extension* extension = InstallExtension(extension_dir, 1);
138   ASSERT_TRUE(extension);
139 
140   CommandService* command_service = CommandService::Get(browser()->profile());
141 
142   {
143     Command command;
144     bool active = false;
145     EXPECT_TRUE(command_service->GetExtensionActionCommand(
146         extension->id(), ActionInfo::TYPE_BROWSER, CommandService::ALL,
147         &command, &active));
148 
149     EXPECT_EQ(kBasicBrowserActionKeybinding,
150               Command::AcceleratorToString(command.accelerator()));
151     EXPECT_TRUE(active);
152   }
153 
154   command_service->UpdateKeybindingPrefs(
155       extension->id(), manifest_values::kBrowserActionCommandEvent,
156       kBasicAlternateKeybinding);
157 
158   {
159     Command command;
160     bool active = false;
161     EXPECT_TRUE(command_service->GetExtensionActionCommand(
162         extension->id(), ActionInfo::TYPE_BROWSER, CommandService::ALL,
163         &command, &active));
164 
165     EXPECT_EQ(kBasicAlternateKeybinding,
166               Command::AcceleratorToString(command.accelerator()));
167     EXPECT_TRUE(active);
168   }
169 
170   command_service->RemoveKeybindingPrefs(
171       extension->id(), manifest_values::kBrowserActionCommandEvent);
172 
173   {
174     Command command;
175     bool active = true;
176     EXPECT_TRUE(command_service->GetExtensionActionCommand(
177         extension->id(), ActionInfo::TYPE_BROWSER, CommandService::ALL,
178         &command, &active));
179 
180     EXPECT_EQ(kBasicBrowserActionKeybinding,
181               Command::AcceleratorToString(command.accelerator()));
182     EXPECT_FALSE(active);
183   }
184 }
185 
IN_PROC_BROWSER_TEST_F(CommandServiceTest,GetExtensionActionCommandQueryActive)186 IN_PROC_BROWSER_TEST_F(CommandServiceTest,
187                        GetExtensionActionCommandQueryActive) {
188   base::FilePath extension_dir =
189       test_data_dir_.AppendASCII("keybinding").AppendASCII("basics");
190   const Extension* extension = InstallExtension(extension_dir, 1);
191   ASSERT_TRUE(extension);
192 
193   CommandService* command_service = CommandService::Get(browser()->profile());
194 
195   {
196     Command command;
197     bool active = false;
198     EXPECT_TRUE(command_service->GetExtensionActionCommand(
199         extension->id(), ActionInfo::TYPE_BROWSER, CommandService::ACTIVE,
200         &command, &active));
201 
202     EXPECT_EQ(kBasicBrowserActionKeybinding,
203               Command::AcceleratorToString(command.accelerator()));
204     EXPECT_TRUE(active);
205   }
206 
207   command_service->UpdateKeybindingPrefs(
208       extension->id(), manifest_values::kBrowserActionCommandEvent,
209       kBasicAlternateKeybinding);
210 
211   {
212     Command command;
213     bool active = false;
214     EXPECT_TRUE(command_service->GetExtensionActionCommand(
215         extension->id(), ActionInfo::TYPE_BROWSER, CommandService::ACTIVE,
216         &command, &active));
217 
218     EXPECT_EQ(kBasicAlternateKeybinding,
219               Command::AcceleratorToString(command.accelerator()));
220     EXPECT_TRUE(active);
221   }
222 
223   command_service->RemoveKeybindingPrefs(
224       extension->id(), manifest_values::kBrowserActionCommandEvent);
225 
226   {
227     Command command;
228     bool active = false;
229     EXPECT_FALSE(command_service->GetExtensionActionCommand(
230         extension->id(), ActionInfo::TYPE_BROWSER, CommandService::ACTIVE,
231         &command, &active));
232   }
233 }
234 
IN_PROC_BROWSER_TEST_F(CommandServiceTest,GetNamedCommandsQueryAll)235 IN_PROC_BROWSER_TEST_F(CommandServiceTest,
236                        GetNamedCommandsQueryAll) {
237   base::FilePath extension_dir =
238       test_data_dir_.AppendASCII("keybinding").AppendASCII("basics");
239   const Extension* extension = InstallExtension(extension_dir, 1);
240   ASSERT_TRUE(extension);
241 
242   CommandService* command_service = CommandService::Get(browser()->profile());
243 
244   {
245     CommandMap command_map;
246     EXPECT_TRUE(command_service->GetNamedCommands(
247         extension->id(), CommandService::ALL, CommandService::ANY_SCOPE,
248         &command_map));
249 
250     ASSERT_EQ(1u, command_map.count(kBasicNamedCommand));
251     Command command = command_map[kBasicNamedCommand];
252     EXPECT_EQ(kBasicNamedKeybinding,
253               Command::AcceleratorToString(command.accelerator()));
254   }
255 
256   command_service->UpdateKeybindingPrefs(
257       extension->id(), kBasicNamedCommand, kBasicAlternateKeybinding);
258 
259   {
260     CommandMap command_map;
261     EXPECT_TRUE(command_service->GetNamedCommands(
262         extension->id(), CommandService::ALL, CommandService::ANY_SCOPE,
263         &command_map));
264 
265     ASSERT_EQ(1u, command_map.count(kBasicNamedCommand));
266     Command command = command_map[kBasicNamedCommand];
267     EXPECT_EQ(kBasicAlternateKeybinding,
268               Command::AcceleratorToString(command.accelerator()));
269   }
270 
271   command_service->RemoveKeybindingPrefs(extension->id(), kBasicNamedCommand);
272 
273   {
274     CommandMap command_map;
275     EXPECT_TRUE(command_service->GetNamedCommands(
276         extension->id(), CommandService::ALL, CommandService::ANY_SCOPE,
277         &command_map));
278 
279     ASSERT_EQ(1u, command_map.count(kBasicNamedCommand));
280     Command command = command_map[kBasicNamedCommand];
281     EXPECT_EQ(kBasicNamedKeybinding,
282               Command::AcceleratorToString(command.accelerator()));
283   }
284 }
285 
IN_PROC_BROWSER_TEST_F(CommandServiceTest,GetNamedCommandsQueryActive)286 IN_PROC_BROWSER_TEST_F(CommandServiceTest, GetNamedCommandsQueryActive) {
287   base::FilePath extension_dir =
288       test_data_dir_.AppendASCII("keybinding").AppendASCII("basics");
289   const Extension* extension = InstallExtension(extension_dir, 1);
290   ASSERT_TRUE(extension);
291 
292   CommandService* command_service = CommandService::Get(browser()->profile());
293 
294   {
295     CommandMap command_map;
296     EXPECT_TRUE(command_service->GetNamedCommands(
297         extension->id(), CommandService::ACTIVE, CommandService::ANY_SCOPE,
298         &command_map));
299 
300     ASSERT_EQ(1u, command_map.count(kBasicNamedCommand));
301     Command command = command_map[kBasicNamedCommand];
302     EXPECT_EQ(kBasicNamedKeybinding,
303               Command::AcceleratorToString(command.accelerator()));
304   }
305 
306   command_service->UpdateKeybindingPrefs(
307       extension->id(), kBasicNamedCommand, kBasicAlternateKeybinding);
308 
309   {
310     CommandMap command_map;
311     EXPECT_TRUE(command_service->GetNamedCommands(
312         extension->id(), CommandService::ACTIVE, CommandService::ANY_SCOPE,
313         &command_map));
314 
315     ASSERT_EQ(1u, command_map.count(kBasicNamedCommand));
316     Command command = command_map[kBasicNamedCommand];
317     EXPECT_EQ(kBasicAlternateKeybinding,
318               Command::AcceleratorToString(command.accelerator()));
319   }
320 
321   command_service->RemoveKeybindingPrefs(extension->id(), kBasicNamedCommand);
322 
323   {
324     CommandMap command_map;
325     command_service->GetNamedCommands(
326         extension->id(), CommandService::ACTIVE, CommandService::ANY_SCOPE,
327         &command_map);
328     EXPECT_EQ(0u, command_map.count(kBasicNamedCommand));
329   }
330 }
331 
332 }  // namespace extensions
333