OLD | NEW |
| (Empty) |
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/api/commands/command_service.h" | |
6 | |
7 #include "base/lazy_instance.h" | |
8 #include "base/strings/string_util.h" | |
9 #include "base/strings/utf_string_conversions.h" | |
10 #include "chrome/browser/chrome_notification_types.h" | |
11 #include "chrome/browser/extensions/api/commands/commands.h" | |
12 #include "chrome/browser/extensions/extension_function_registry.h" | |
13 #include "chrome/browser/extensions/extension_keybinding_registry.h" | |
14 #include "chrome/browser/extensions/extension_service.h" | |
15 #include "chrome/browser/extensions/extension_system.h" | |
16 #include "chrome/browser/prefs/scoped_user_pref_update.h" | |
17 #include "chrome/browser/profiles/profile.h" | |
18 #include "chrome/common/pref_names.h" | |
19 #include "components/user_prefs/pref_registry_syncable.h" | |
20 #include "content/public/browser/notification_details.h" | |
21 #include "content/public/browser/notification_service.h" | |
22 | |
23 using extensions::Extension; | |
24 | |
25 namespace { | |
26 | |
27 const char kExtension[] = "extension"; | |
28 const char kCommandName[] = "command_name"; | |
29 | |
30 std::string GetPlatformKeybindingKeyForAccelerator( | |
31 const ui::Accelerator& accelerator) { | |
32 return extensions::Command::CommandPlatform() + ":" + | |
33 UTF16ToUTF8(accelerator.GetShortcutText()); | |
34 } | |
35 | |
36 } // namespace | |
37 | |
38 namespace extensions { | |
39 | |
40 // static | |
41 void CommandService::RegisterProfilePrefs( | |
42 user_prefs::PrefRegistrySyncable* registry) { | |
43 registry->RegisterDictionaryPref( | |
44 prefs::kExtensionCommands, | |
45 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); | |
46 } | |
47 | |
48 CommandService::CommandService(Profile* profile) | |
49 : profile_(profile) { | |
50 ExtensionFunctionRegistry::GetInstance()-> | |
51 RegisterFunction<GetAllCommandsFunction>(); | |
52 | |
53 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, | |
54 content::Source<Profile>(profile)); | |
55 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED, | |
56 content::Source<Profile>(profile)); | |
57 } | |
58 | |
59 CommandService::~CommandService() { | |
60 } | |
61 | |
62 static base::LazyInstance<ProfileKeyedAPIFactory<CommandService> > | |
63 g_factory = LAZY_INSTANCE_INITIALIZER; | |
64 | |
65 // static | |
66 ProfileKeyedAPIFactory<CommandService>* CommandService::GetFactoryInstance() { | |
67 return &g_factory.Get(); | |
68 } | |
69 | |
70 // static | |
71 CommandService* CommandService::Get(Profile* profile) { | |
72 return ProfileKeyedAPIFactory<CommandService>::GetForProfile(profile); | |
73 } | |
74 | |
75 bool CommandService::GetBrowserActionCommand( | |
76 const std::string& extension_id, | |
77 QueryType type, | |
78 extensions::Command* command, | |
79 bool* active) { | |
80 return GetExtensionActionCommand( | |
81 extension_id, type, command, active, BROWSER_ACTION); | |
82 } | |
83 | |
84 bool CommandService::GetPageActionCommand( | |
85 const std::string& extension_id, | |
86 QueryType type, | |
87 extensions::Command* command, | |
88 bool* active) { | |
89 return GetExtensionActionCommand( | |
90 extension_id, type, command, active, PAGE_ACTION); | |
91 } | |
92 | |
93 bool CommandService::GetScriptBadgeCommand( | |
94 const std::string& extension_id, | |
95 QueryType type, | |
96 extensions::Command* command, | |
97 bool* active) { | |
98 return GetExtensionActionCommand( | |
99 extension_id, type, command, active, SCRIPT_BADGE); | |
100 } | |
101 | |
102 bool CommandService::GetNamedCommands(const std::string& extension_id, | |
103 QueryType type, | |
104 extensions::CommandMap* command_map) { | |
105 const ExtensionSet* extensions = | |
106 ExtensionSystem::Get(profile_)->extension_service()->extensions(); | |
107 const Extension* extension = extensions->GetByID(extension_id); | |
108 CHECK(extension); | |
109 | |
110 command_map->clear(); | |
111 const extensions::CommandMap* commands = | |
112 CommandsInfo::GetNamedCommands(extension); | |
113 if (!commands) | |
114 return false; | |
115 | |
116 extensions::CommandMap::const_iterator iter = commands->begin(); | |
117 for (; iter != commands->end(); ++iter) { | |
118 ui::Accelerator shortcut_assigned = | |
119 FindShortcutForCommand(extension_id, iter->second.command_name()); | |
120 | |
121 if (type == ACTIVE_ONLY && shortcut_assigned.key_code() == ui::VKEY_UNKNOWN) | |
122 continue; | |
123 | |
124 extensions::Command command = iter->second; | |
125 if (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN) | |
126 command.set_accelerator(shortcut_assigned); | |
127 | |
128 (*command_map)[iter->second.command_name()] = command; | |
129 } | |
130 | |
131 return true; | |
132 } | |
133 | |
134 bool CommandService::AddKeybindingPref( | |
135 const ui::Accelerator& accelerator, | |
136 std::string extension_id, | |
137 std::string command_name, | |
138 bool allow_overrides) { | |
139 if (accelerator.key_code() == ui::VKEY_UNKNOWN) | |
140 return false; | |
141 | |
142 DictionaryPrefUpdate updater(profile_->GetPrefs(), | |
143 prefs::kExtensionCommands); | |
144 base::DictionaryValue* bindings = updater.Get(); | |
145 | |
146 std::string key = GetPlatformKeybindingKeyForAccelerator(accelerator); | |
147 | |
148 if (!allow_overrides && bindings->HasKey(key)) | |
149 return false; // Already taken. | |
150 | |
151 base::DictionaryValue* keybinding = new base::DictionaryValue(); | |
152 keybinding->SetString(kExtension, extension_id); | |
153 keybinding->SetString(kCommandName, command_name); | |
154 | |
155 bindings->Set(key, keybinding); | |
156 | |
157 std::pair<const std::string, const std::string> details = | |
158 std::make_pair(extension_id, command_name); | |
159 content::NotificationService::current()->Notify( | |
160 chrome::NOTIFICATION_EXTENSION_COMMAND_ADDED, | |
161 content::Source<Profile>(profile_), | |
162 content::Details< | |
163 std::pair<const std::string, const std::string> >(&details)); | |
164 | |
165 return true; | |
166 } | |
167 | |
168 void CommandService::Observe( | |
169 int type, | |
170 const content::NotificationSource& source, | |
171 const content::NotificationDetails& details) { | |
172 switch (type) { | |
173 case chrome::NOTIFICATION_EXTENSION_INSTALLED: | |
174 AssignInitialKeybindings( | |
175 content::Details<const InstalledExtensionInfo>(details)->extension); | |
176 break; | |
177 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: | |
178 RemoveKeybindingPrefs( | |
179 content::Details<const Extension>(details)->id(), | |
180 std::string()); | |
181 break; | |
182 default: | |
183 NOTREACHED(); | |
184 break; | |
185 } | |
186 } | |
187 | |
188 void CommandService::UpdateKeybindingPrefs(const std::string& extension_id, | |
189 const std::string& command_name, | |
190 const std::string& keystroke) { | |
191 // The extension command might be assigned another shortcut. Remove that | |
192 // shortcut before proceeding. | |
193 RemoveKeybindingPrefs(extension_id, command_name); | |
194 | |
195 ui::Accelerator accelerator = Command::StringToAccelerator(keystroke); | |
196 AddKeybindingPref(accelerator, extension_id, command_name, true); | |
197 } | |
198 | |
199 ui::Accelerator CommandService::FindShortcutForCommand( | |
200 const std::string& extension_id, const std::string& command) { | |
201 const base::DictionaryValue* bindings = | |
202 profile_->GetPrefs()->GetDictionary(prefs::kExtensionCommands); | |
203 for (base::DictionaryValue::Iterator it(*bindings); !it.IsAtEnd(); | |
204 it.Advance()) { | |
205 const base::DictionaryValue* item = NULL; | |
206 it.value().GetAsDictionary(&item); | |
207 | |
208 std::string extension; | |
209 item->GetString(kExtension, &extension); | |
210 if (extension != extension_id) | |
211 continue; | |
212 std::string command_name; | |
213 item->GetString(kCommandName, &command_name); | |
214 if (command != command_name) | |
215 continue; | |
216 | |
217 std::string shortcut = it.key(); | |
218 if (StartsWithASCII(shortcut, Command::CommandPlatform() + ":", true)) | |
219 shortcut = shortcut.substr(Command::CommandPlatform().length() + 1); | |
220 | |
221 return Command::StringToAccelerator(shortcut); | |
222 } | |
223 | |
224 return ui::Accelerator(); | |
225 } | |
226 | |
227 void CommandService::AssignInitialKeybindings(const Extension* extension) { | |
228 const extensions::CommandMap* commands = | |
229 CommandsInfo::GetNamedCommands(extension); | |
230 if (!commands) | |
231 return; | |
232 | |
233 extensions::CommandMap::const_iterator iter = commands->begin(); | |
234 for (; iter != commands->end(); ++iter) { | |
235 AddKeybindingPref(iter->second.accelerator(), | |
236 extension->id(), | |
237 iter->second.command_name(), | |
238 false); // Overwriting not allowed. | |
239 } | |
240 | |
241 const extensions::Command* browser_action_command = | |
242 CommandsInfo::GetBrowserActionCommand(extension); | |
243 if (browser_action_command) { | |
244 AddKeybindingPref(browser_action_command->accelerator(), | |
245 extension->id(), | |
246 browser_action_command->command_name(), | |
247 false); // Overwriting not allowed. | |
248 } | |
249 | |
250 const extensions::Command* page_action_command = | |
251 CommandsInfo::GetPageActionCommand(extension); | |
252 if (page_action_command) { | |
253 AddKeybindingPref(page_action_command->accelerator(), | |
254 extension->id(), | |
255 page_action_command->command_name(), | |
256 false); // Overwriting not allowed. | |
257 } | |
258 | |
259 const extensions::Command* script_badge_command = | |
260 CommandsInfo::GetScriptBadgeCommand(extension); | |
261 if (script_badge_command) { | |
262 AddKeybindingPref(script_badge_command->accelerator(), | |
263 extension->id(), | |
264 script_badge_command->command_name(), | |
265 false); // Overwriting not allowed. | |
266 } | |
267 } | |
268 | |
269 void CommandService::RemoveKeybindingPrefs(const std::string& extension_id, | |
270 const std::string& command_name) { | |
271 DictionaryPrefUpdate updater(profile_->GetPrefs(), | |
272 prefs::kExtensionCommands); | |
273 base::DictionaryValue* bindings = updater.Get(); | |
274 | |
275 typedef std::vector<std::string> KeysToRemove; | |
276 KeysToRemove keys_to_remove; | |
277 for (base::DictionaryValue::Iterator it(*bindings); !it.IsAtEnd(); | |
278 it.Advance()) { | |
279 const base::DictionaryValue* item = NULL; | |
280 it.value().GetAsDictionary(&item); | |
281 | |
282 std::string extension; | |
283 item->GetString(kExtension, &extension); | |
284 | |
285 if (extension == extension_id) { | |
286 // If |command_name| is specified, delete only that command. Otherwise, | |
287 // delete all commands. | |
288 if (!command_name.empty()) { | |
289 std::string command; | |
290 item->GetString(kCommandName, &command); | |
291 if (command_name != command) | |
292 continue; | |
293 } | |
294 | |
295 keys_to_remove.push_back(it.key()); | |
296 } | |
297 } | |
298 | |
299 for (KeysToRemove::const_iterator it = keys_to_remove.begin(); | |
300 it != keys_to_remove.end(); ++it) { | |
301 std::string key = *it; | |
302 bindings->Remove(key, NULL); | |
303 | |
304 std::pair<const std::string, const std::string> details = | |
305 std::make_pair(extension_id, command_name); | |
306 content::NotificationService::current()->Notify( | |
307 chrome::NOTIFICATION_EXTENSION_COMMAND_REMOVED, | |
308 content::Source<Profile>(profile_), | |
309 content::Details< | |
310 std::pair<const std::string, const std::string> >(&details)); | |
311 } | |
312 } | |
313 | |
314 bool CommandService::GetExtensionActionCommand( | |
315 const std::string& extension_id, | |
316 QueryType query_type, | |
317 extensions::Command* command, | |
318 bool* active, | |
319 ExtensionActionType action_type) { | |
320 ExtensionService* service = | |
321 ExtensionSystem::Get(profile_)->extension_service(); | |
322 if (!service) | |
323 return false; // Can happen in tests. | |
324 const ExtensionSet* extensions = service->extensions(); | |
325 const Extension* extension = extensions->GetByID(extension_id); | |
326 CHECK(extension); | |
327 | |
328 if (active) | |
329 *active = false; | |
330 | |
331 const extensions::Command* requested_command = NULL; | |
332 switch (action_type) { | |
333 case BROWSER_ACTION: | |
334 requested_command = CommandsInfo::GetBrowserActionCommand(extension); | |
335 break; | |
336 case PAGE_ACTION: | |
337 requested_command = CommandsInfo::GetPageActionCommand(extension); | |
338 break; | |
339 case SCRIPT_BADGE: | |
340 requested_command = CommandsInfo::GetScriptBadgeCommand(extension); | |
341 break; | |
342 } | |
343 if (!requested_command) | |
344 return false; | |
345 | |
346 ui::Accelerator shortcut_assigned = | |
347 FindShortcutForCommand(extension_id, requested_command->command_name()); | |
348 | |
349 if (active) | |
350 *active = (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN); | |
351 | |
352 if (query_type == ACTIVE_ONLY && | |
353 shortcut_assigned.key_code() == ui::VKEY_UNKNOWN) | |
354 return false; | |
355 | |
356 *command = *requested_command; | |
357 if (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN) | |
358 command->set_accelerator(shortcut_assigned); | |
359 | |
360 return true; | |
361 } | |
362 | |
363 } // namespace extensions | |
OLD | NEW |