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.
5 #include "chrome/browser/extensions/api/commands/command_service.h"
9 #include "base/lazy_instance.h"
10 #include "base/prefs/scoped_user_pref_update.h"
11 #include "base/strings/string_split.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/app/chrome_command_ids.h"
15 #include "chrome/browser/extensions/api/commands/commands.h"
16 #include "chrome/browser/extensions/extension_commands_global_registry.h"
17 #include "chrome/browser/extensions/extension_keybinding_registry.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/accelerator_utils.h"
20 #include "chrome/common/extensions/api/commands/commands_handler.h"
21 #include "chrome/common/extensions/manifest_handlers/ui_overrides_handler.h"
22 #include "chrome/common/pref_names.h"
23 #include "components/pref_registry/pref_registry_syncable.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_service.h"
26 #include "extensions/browser/extension_function_registry.h"
27 #include "extensions/browser/extension_prefs.h"
28 #include "extensions/browser/extension_registry.h"
29 #include "extensions/browser/extension_system.h"
30 #include "extensions/browser/notification_types.h"
31 #include "extensions/common/feature_switch.h"
32 #include "extensions/common/manifest_constants.h"
33 #include "extensions/common/permissions/permissions_data.h"
35 namespace extensions {
38 const char kExtension[] = "extension";
39 const char kCommandName[] = "command_name";
40 const char kGlobal[] = "global";
42 // A preference that stores keybinding state associated with extension commands.
43 const char kCommands[] = "commands";
45 // Preference key name for saving the extension-suggested key.
46 const char kSuggestedKey[] = "suggested_key";
48 // Preference key name for saving whether the extension-suggested key was
50 const char kSuggestedKeyWasAssigned[] = "was_assigned";
52 // A preference that indicates that the initial keybindings for the given
53 // extension have been set.
54 const char kInitialBindingsHaveBeenAssigned[] = "initial_keybindings_set";
56 std::string GetPlatformKeybindingKeyForAccelerator(
57 const ui::Accelerator& accelerator, const std::string extension_id) {
58 std::string key = Command::CommandPlatform() + ":" +
59 Command::AcceleratorToString(accelerator);
61 // Media keys have a 1-to-many relationship with targets, unlike regular
62 // shortcut (1-to-1 relationship). That means two or more extensions can
63 // register for the same media key so the extension ID needs to be added to
64 // the key to make sure the key is unique.
65 if (Command::IsMediaKey(accelerator))
66 key += ":" + extension_id;
71 bool IsForCurrentPlatform(const std::string& key) {
72 return StartsWithASCII(key, Command::CommandPlatform() + ":", true);
75 void SetInitialBindingsHaveBeenAssigned(
76 ExtensionPrefs* prefs, const std::string& extension_id) {
77 prefs->UpdateExtensionPref(extension_id, kInitialBindingsHaveBeenAssigned,
78 new base::FundamentalValue(true));
81 bool InitialBindingsHaveBeenAssigned(
82 const ExtensionPrefs* prefs, const std::string& extension_id) {
83 bool assigned = false;
84 if (!prefs || !prefs->ReadPrefAsBoolean(extension_id,
85 kInitialBindingsHaveBeenAssigned,
92 // Merge |suggested_key_prefs| into the saved preferences for the extension. We
93 // merge rather than overwrite to preserve existing was_assigned preferences.
94 void MergeSuggestedKeyPrefs(
95 const std::string& extension_id,
96 ExtensionPrefs* extension_prefs,
97 scoped_ptr<base::DictionaryValue> suggested_key_prefs) {
98 const base::DictionaryValue* current_prefs;
99 if (extension_prefs->ReadPrefAsDictionary(extension_id,
102 scoped_ptr<base::DictionaryValue> new_prefs(current_prefs->DeepCopy());
103 new_prefs->MergeDictionary(suggested_key_prefs.get());
104 suggested_key_prefs.reset(new_prefs.release());
107 extension_prefs->UpdateExtensionPref(extension_id,
109 suggested_key_prefs.release());
115 void CommandService::RegisterProfilePrefs(
116 user_prefs::PrefRegistrySyncable* registry) {
117 registry->RegisterDictionaryPref(
118 prefs::kExtensionCommands,
119 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
122 CommandService::CommandService(content::BrowserContext* context)
123 : profile_(Profile::FromBrowserContext(context)),
124 extension_registry_observer_(this) {
125 ExtensionFunctionRegistry::GetInstance()->
126 RegisterFunction<GetAllCommandsFunction>();
128 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
131 CommandService::~CommandService() {
134 static base::LazyInstance<BrowserContextKeyedAPIFactory<CommandService> >
135 g_factory = LAZY_INSTANCE_INITIALIZER;
138 BrowserContextKeyedAPIFactory<CommandService>*
139 CommandService::GetFactoryInstance() {
140 return g_factory.Pointer();
144 CommandService* CommandService::Get(content::BrowserContext* context) {
145 return BrowserContextKeyedAPIFactory<CommandService>::Get(context);
149 bool CommandService::RemovesBookmarkShortcut(const Extension* extension) {
150 return UIOverrides::RemovesBookmarkShortcut(extension) &&
151 (extension->permissions_data()->HasAPIPermission(
152 APIPermission::kBookmarkManagerPrivate) ||
153 FeatureSwitch::enable_override_bookmarks_ui()->IsEnabled());
157 bool CommandService::RemovesBookmarkOpenPagesShortcut(
158 const Extension* extension) {
159 return UIOverrides::RemovesBookmarkOpenPagesShortcut(extension) &&
160 (extension->permissions_data()->HasAPIPermission(
161 APIPermission::kBookmarkManagerPrivate) ||
162 FeatureSwitch::enable_override_bookmarks_ui()->IsEnabled());
165 bool CommandService::GetBrowserActionCommand(const std::string& extension_id,
168 bool* active) const {
169 return GetExtensionActionCommand(
170 extension_id, type, command, active, BROWSER_ACTION);
173 bool CommandService::GetPageActionCommand(const std::string& extension_id,
176 bool* active) const {
177 return GetExtensionActionCommand(
178 extension_id, type, command, active, PAGE_ACTION);
181 bool CommandService::GetNamedCommands(const std::string& extension_id,
184 CommandMap* command_map) const {
185 const ExtensionSet& extensions =
186 ExtensionRegistry::Get(profile_)->enabled_extensions();
187 const Extension* extension = extensions.GetByID(extension_id);
190 command_map->clear();
191 const CommandMap* commands = CommandsInfo::GetNamedCommands(extension);
195 for (CommandMap::const_iterator iter = commands->begin();
196 iter != commands->end(); ++iter) {
197 // Look up to see if the user has overridden how the command should work.
198 Command saved_command =
199 FindCommandByName(extension_id, iter->second.command_name());
200 ui::Accelerator shortcut_assigned = saved_command.accelerator();
202 if (type == ACTIVE_ONLY && shortcut_assigned.key_code() == ui::VKEY_UNKNOWN)
205 Command command = iter->second;
206 if (scope != ANY_SCOPE && ((scope == GLOBAL) != saved_command.global()))
209 if (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN)
210 command.set_accelerator(shortcut_assigned);
211 command.set_global(saved_command.global());
213 (*command_map)[iter->second.command_name()] = command;
219 bool CommandService::AddKeybindingPref(
220 const ui::Accelerator& accelerator,
221 std::string extension_id,
222 std::string command_name,
223 bool allow_overrides,
225 if (accelerator.key_code() == ui::VKEY_UNKNOWN)
228 // Nothing needs to be done if the existing command is the same as the desired
230 Command existing_command = FindCommandByName(extension_id, command_name);
231 if (existing_command.accelerator() == accelerator &&
232 existing_command.global() == global)
235 // Media Keys are allowed to be used by named command only.
236 DCHECK(!Command::IsMediaKey(accelerator) ||
237 (command_name != manifest_values::kPageActionCommandEvent &&
238 command_name != manifest_values::kBrowserActionCommandEvent));
240 DictionaryPrefUpdate updater(profile_->GetPrefs(),
241 prefs::kExtensionCommands);
242 base::DictionaryValue* bindings = updater.Get();
244 std::string key = GetPlatformKeybindingKeyForAccelerator(accelerator,
247 if (bindings->HasKey(key)) {
248 if (!allow_overrides)
249 return false; // Already taken.
251 // If the shortcut has been assigned to another command, it should be
252 // removed before overriding, so that |ExtensionKeybindingRegistry| can get
253 // a chance to do clean-up.
254 const base::DictionaryValue* item = NULL;
255 bindings->GetDictionary(key, &item);
256 std::string old_extension_id;
257 std::string old_command_name;
258 item->GetString(kExtension, &old_extension_id);
259 item->GetString(kCommandName, &old_command_name);
260 RemoveKeybindingPrefs(old_extension_id, old_command_name);
263 // If the command that is taking a new shortcut already has a shortcut, remove
264 // it before assigning the new one.
265 if (existing_command.accelerator().key_code() != ui::VKEY_UNKNOWN)
266 RemoveKeybindingPrefs(extension_id, command_name);
268 // Set the keybinding pref.
269 base::DictionaryValue* keybinding = new base::DictionaryValue();
270 keybinding->SetString(kExtension, extension_id);
271 keybinding->SetString(kCommandName, command_name);
272 keybinding->SetBoolean(kGlobal, global);
274 bindings->Set(key, keybinding);
276 // Set the was_assigned pref for the suggested key.
277 scoped_ptr<base::DictionaryValue> command_keys(new base::DictionaryValue);
278 command_keys->SetBoolean(kSuggestedKeyWasAssigned, true);
279 scoped_ptr<base::DictionaryValue> suggested_key_prefs(
280 new base::DictionaryValue);
281 suggested_key_prefs->Set(command_name, command_keys.release());
282 MergeSuggestedKeyPrefs(extension_id,
283 ExtensionPrefs::Get(profile_),
284 suggested_key_prefs.Pass());
286 std::pair<const std::string, const std::string> details =
287 std::make_pair(extension_id, command_name);
288 content::NotificationService::current()->Notify(
289 extensions::NOTIFICATION_EXTENSION_COMMAND_ADDED,
290 content::Source<Profile>(profile_),
291 content::Details<std::pair<const std::string, const std::string> >(
297 void CommandService::OnExtensionWillBeInstalled(
298 content::BrowserContext* browser_context,
299 const Extension* extension,
302 const std::string& old_name) {
303 UpdateKeybindings(extension);
306 void CommandService::OnExtensionUninstalled(
307 content::BrowserContext* browser_context,
308 const Extension* extension,
309 extensions::UninstallReason reason) {
310 RemoveKeybindingPrefs(extension->id(), std::string());
313 void CommandService::UpdateKeybindingPrefs(const std::string& extension_id,
314 const std::string& command_name,
315 const std::string& keystroke) {
316 Command command = FindCommandByName(extension_id, command_name);
318 // The extension command might be assigned another shortcut. Remove that
319 // shortcut before proceeding.
320 RemoveKeybindingPrefs(extension_id, command_name);
322 ui::Accelerator accelerator =
323 Command::StringToAccelerator(keystroke, command_name);
324 AddKeybindingPref(accelerator, extension_id, command_name,
325 true, command.global());
328 bool CommandService::SetScope(const std::string& extension_id,
329 const std::string& command_name,
331 Command command = FindCommandByName(extension_id, command_name);
332 if (global == command.global())
335 // Pre-existing shortcuts must be removed before proceeding because the
336 // handlers for global and non-global extensions are not one and the same.
337 RemoveKeybindingPrefs(extension_id, command_name);
338 AddKeybindingPref(command.accelerator(), extension_id,
339 command_name, true, global);
343 Command CommandService::FindCommandByName(const std::string& extension_id,
344 const std::string& command) const {
345 const base::DictionaryValue* bindings =
346 profile_->GetPrefs()->GetDictionary(prefs::kExtensionCommands);
347 for (base::DictionaryValue::Iterator it(*bindings); !it.IsAtEnd();
349 const base::DictionaryValue* item = NULL;
350 it.value().GetAsDictionary(&item);
352 std::string extension;
353 item->GetString(kExtension, &extension);
354 if (extension != extension_id)
356 std::string command_name;
357 item->GetString(kCommandName, &command_name);
358 if (command != command_name)
360 // Format stored in Preferences is: "Platform:Shortcut[:ExtensionId]".
361 std::string shortcut = it.key();
362 if (!IsForCurrentPlatform(shortcut))
365 item->GetBoolean(kGlobal, &global);
367 std::vector<std::string> tokens;
368 base::SplitString(shortcut, ':', &tokens);
369 CHECK(tokens.size() >= 2);
370 shortcut = tokens[1];
372 return Command(command_name, base::string16(), shortcut, global);
378 bool CommandService::GetBoundExtensionCommand(
379 const std::string& extension_id,
380 const ui::Accelerator& accelerator,
382 ExtensionCommandType* command_type) const {
383 const Extension* extension =
384 ExtensionRegistry::Get(profile_)
385 ->GetExtensionById(extension_id, ExtensionRegistry::ENABLED);
388 Command prospective_command;
389 CommandMap command_map;
391 if (GetBrowserActionCommand(extension_id,
392 CommandService::ACTIVE_ONLY,
393 &prospective_command,
395 active && accelerator == prospective_command.accelerator()) {
397 *command = prospective_command;
399 *command_type = BROWSER_ACTION;
401 } else if (GetPageActionCommand(extension_id,
402 CommandService::ACTIVE_ONLY,
403 &prospective_command,
405 active && accelerator == prospective_command.accelerator()) {
407 *command = prospective_command;
409 *command_type = PAGE_ACTION;
411 } else if (GetNamedCommands(extension_id,
412 CommandService::ACTIVE_ONLY,
413 CommandService::REGULAR,
415 for (CommandMap::const_iterator it = command_map.begin();
416 it != command_map.end();
418 if (accelerator == it->second.accelerator()) {
420 *command = it->second;
422 *command_type = NAMED;
430 bool CommandService::OverridesBookmarkShortcut(
431 const Extension* extension) const {
432 return RemovesBookmarkShortcut(extension) &&
433 GetBoundExtensionCommand(
435 chrome::GetPrimaryChromeAcceleratorForCommandId(IDC_BOOKMARK_PAGE),
440 void CommandService::UpdateKeybindings(const Extension* extension) {
441 const ExtensionSet& extensions =
442 ExtensionRegistry::Get(profile_)->enabled_extensions();
443 // The extension is not added to the profile by this point on first install,
444 // so don't try to check for existing keybindings.
445 if (extensions.GetByID(extension->id()))
446 RemoveRelinquishedKeybindings(extension);
447 AssignKeybindings(extension);
448 UpdateExtensionSuggestedCommandPrefs(extension);
449 RemoveDefunctExtensionSuggestedCommandPrefs(extension);
452 void CommandService::RemoveRelinquishedKeybindings(const Extension* extension) {
453 // Remove keybindings if they have been removed by the extension and the user
454 // has not modified them.
455 CommandMap existing_command_map;
456 if (GetNamedCommands(extension->id(),
457 CommandService::ACTIVE_ONLY,
458 CommandService::REGULAR,
459 &existing_command_map)) {
460 const CommandMap* new_command_map =
461 CommandsInfo::GetNamedCommands(extension);
462 for (CommandMap::const_iterator it = existing_command_map.begin();
463 it != existing_command_map.end(); ++it) {
464 std::string command_name = it->first;
465 if (new_command_map->find(command_name) == new_command_map->end() &&
466 !IsCommandShortcutUserModified(extension, command_name)) {
467 RemoveKeybindingPrefs(extension->id(), command_name);
472 Command existing_browser_action_command;
473 const Command* new_browser_action_command =
474 CommandsInfo::GetBrowserActionCommand(extension);
475 if (GetBrowserActionCommand(extension->id(),
476 CommandService::ACTIVE_ONLY,
477 &existing_browser_action_command,
479 // The browser action command may be defaulted to an unassigned
480 // accelerator if a browser action is specified by the extension but a
481 // keybinding is not declared. See
482 // CommandsHandler::MaybeSetBrowserActionDefault.
483 (!new_browser_action_command ||
484 new_browser_action_command->accelerator().key_code() ==
486 !IsCommandShortcutUserModified(
488 existing_browser_action_command.command_name())) {
489 RemoveKeybindingPrefs(extension->id(),
490 existing_browser_action_command.command_name());
493 Command existing_page_action_command;
494 if (GetPageActionCommand(extension->id(),
495 CommandService::ACTIVE_ONLY,
496 &existing_page_action_command,
498 !CommandsInfo::GetPageActionCommand(extension) &&
499 !IsCommandShortcutUserModified(
501 existing_page_action_command.command_name())) {
502 RemoveKeybindingPrefs(extension->id(),
503 existing_page_action_command.command_name());
507 void CommandService::AssignKeybindings(const Extension* extension) {
508 const CommandMap* commands = CommandsInfo::GetNamedCommands(extension);
512 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
513 // TODO(wittman): remove use of this pref after M37 hits stable.
514 if (!InitialBindingsHaveBeenAssigned(extension_prefs, extension->id()))
515 SetInitialBindingsHaveBeenAssigned(extension_prefs, extension->id());
517 for (CommandMap::const_iterator iter = commands->begin();
518 iter != commands->end(); ++iter) {
519 const Command command = iter->second;
520 if (CanAutoAssign(command, extension)) {
521 AddKeybindingPref(command.accelerator(),
523 command.command_name(),
524 false, // Overwriting not allowed.
529 const Command* browser_action_command =
530 CommandsInfo::GetBrowserActionCommand(extension);
531 if (browser_action_command &&
532 CanAutoAssign(*browser_action_command, extension)) {
533 AddKeybindingPref(browser_action_command->accelerator(),
535 browser_action_command->command_name(),
536 false, // Overwriting not allowed.
537 false); // Not global.
540 const Command* page_action_command =
541 CommandsInfo::GetPageActionCommand(extension);
542 if (page_action_command && CanAutoAssign(*page_action_command, extension)) {
543 AddKeybindingPref(page_action_command->accelerator(),
545 page_action_command->command_name(),
546 false, // Overwriting not allowed.
547 false); // Not global.
551 bool CommandService::CanAutoAssign(const Command &command,
552 const Extension* extension) {
553 // Media Keys are non-exclusive, so allow auto-assigning them.
554 if (Command::IsMediaKey(command.accelerator()))
557 // Extensions are allowed to auto-assign updated keys if the user has not
558 // changed from the previous value.
559 if (IsCommandShortcutUserModified(extension, command.command_name()))
562 if (command.global()) {
563 using namespace extensions;
564 if (command.command_name() == manifest_values::kBrowserActionCommandEvent ||
565 command.command_name() == manifest_values::kPageActionCommandEvent)
566 return false; // Browser and page actions are not global in nature.
568 // Global shortcuts are restricted to (Ctrl|Command)+Shift+[0-9].
569 #if defined OS_MACOSX
570 if (!command.accelerator().IsCmdDown())
573 if (!command.accelerator().IsCtrlDown())
576 if (!command.accelerator().IsShiftDown())
578 return (command.accelerator().key_code() >= ui::VKEY_0 &&
579 command.accelerator().key_code() <= ui::VKEY_9);
581 // Not a global command, check if Chrome shortcut and whether
582 // we can override it.
583 if (command.accelerator() ==
584 chrome::GetPrimaryChromeAcceleratorForCommandId(IDC_BOOKMARK_PAGE) &&
585 CommandService::RemovesBookmarkShortcut(extension)) {
586 // If this check fails it either means we have an API to override a
587 // key that isn't a ChromeAccelerator (and the API can therefore be
588 // deprecated) or the IsChromeAccelerator isn't consistently
589 // returning true for all accelerators.
590 DCHECK(chrome::IsChromeAccelerator(command.accelerator(), profile_));
594 return !chrome::IsChromeAccelerator(command.accelerator(), profile_);
598 void CommandService::UpdateExtensionSuggestedCommandPrefs(
599 const Extension* extension) {
600 scoped_ptr<base::DictionaryValue> suggested_key_prefs(
601 new base::DictionaryValue);
603 const CommandMap* commands = CommandsInfo::GetNamedCommands(extension);
605 for (CommandMap::const_iterator iter = commands->begin();
606 iter != commands->end(); ++iter) {
607 const Command command = iter->second;
608 scoped_ptr<base::DictionaryValue> command_keys(new base::DictionaryValue);
609 command_keys->SetString(
611 Command::AcceleratorToString(command.accelerator()));
612 suggested_key_prefs->Set(command.command_name(), command_keys.release());
616 const Command* browser_action_command =
617 CommandsInfo::GetBrowserActionCommand(extension);
618 // The browser action command may be defaulted to an unassigned accelerator if
619 // a browser action is specified by the extension but a keybinding is not
620 // declared. See CommandsHandler::MaybeSetBrowserActionDefault.
621 if (browser_action_command &&
622 browser_action_command->accelerator().key_code() != ui::VKEY_UNKNOWN) {
623 scoped_ptr<base::DictionaryValue> command_keys(new base::DictionaryValue);
624 command_keys->SetString(
626 Command::AcceleratorToString(browser_action_command->accelerator()));
627 suggested_key_prefs->Set(browser_action_command->command_name(),
628 command_keys.release());
631 const Command* page_action_command =
632 CommandsInfo::GetPageActionCommand(extension);
633 if (page_action_command) {
634 scoped_ptr<base::DictionaryValue> command_keys(new base::DictionaryValue);
635 command_keys->SetString(
637 Command::AcceleratorToString(page_action_command->accelerator()));
638 suggested_key_prefs->Set(page_action_command->command_name(),
639 command_keys.release());
642 // Merge into current prefs, if present.
643 MergeSuggestedKeyPrefs(extension->id(),
644 ExtensionPrefs::Get(profile_),
645 suggested_key_prefs.Pass());
648 void CommandService::RemoveDefunctExtensionSuggestedCommandPrefs(
649 const Extension* extension) {
650 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
651 const base::DictionaryValue* current_prefs = NULL;
652 extension_prefs->ReadPrefAsDictionary(extension->id(),
657 scoped_ptr<base::DictionaryValue> suggested_key_prefs(
658 current_prefs->DeepCopy());
659 const CommandMap* named_commands =
660 CommandsInfo::GetNamedCommands(extension);
661 const Command* browser_action_command =
662 CommandsInfo::GetBrowserActionCommand(extension);
663 for (base::DictionaryValue::Iterator it(*current_prefs);
664 !it.IsAtEnd(); it.Advance()) {
665 if (it.key() == manifest_values::kBrowserActionCommandEvent) {
666 // The browser action command may be defaulted to an unassigned
667 // accelerator if a browser action is specified by the extension but a
668 // keybinding is not declared. See
669 // CommandsHandler::MaybeSetBrowserActionDefault.
670 if (!browser_action_command ||
671 browser_action_command->accelerator().key_code() ==
673 suggested_key_prefs->Remove(it.key(), NULL);
675 } else if (it.key() == manifest_values::kPageActionCommandEvent) {
676 if (!CommandsInfo::GetPageActionCommand(extension))
677 suggested_key_prefs->Remove(it.key(), NULL);
678 } else if (named_commands) {
679 if (named_commands->find(it.key()) == named_commands->end())
680 suggested_key_prefs->Remove(it.key(), NULL);
684 extension_prefs->UpdateExtensionPref(extension->id(),
686 suggested_key_prefs.release());
690 bool CommandService::IsCommandShortcutUserModified(
691 const Extension* extension,
692 const std::string& command_name) {
693 // Get the previous suggested key, if any.
694 ui::Accelerator suggested_key;
695 bool suggested_key_was_assigned = false;
696 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
697 const base::DictionaryValue* commands_prefs = NULL;
698 const base::DictionaryValue* suggested_key_prefs = NULL;
699 if (extension_prefs->ReadPrefAsDictionary(extension->id(),
702 commands_prefs->GetDictionary(command_name, &suggested_key_prefs)) {
703 std::string suggested_key_string;
704 if (suggested_key_prefs->GetString(kSuggestedKey, &suggested_key_string)) {
705 suggested_key = Command::StringToAccelerator(suggested_key_string,
709 suggested_key_prefs->GetBoolean(kSuggestedKeyWasAssigned,
710 &suggested_key_was_assigned);
713 // Get the active shortcut from the prefs, if any.
714 Command active_command = FindCommandByName(extension->id(), command_name);
716 return suggested_key_was_assigned ?
717 active_command.accelerator() != suggested_key :
718 active_command.accelerator().key_code() != ui::VKEY_UNKNOWN;
721 bool CommandService::IsKeybindingChanging(const Extension* extension,
722 const std::string& command_name) {
723 // Get the new assigned command, if any.
725 if (command_name == manifest_values::kBrowserActionCommandEvent) {
726 new_command = *CommandsInfo::GetBrowserActionCommand(extension);
727 } else if (command_name == manifest_values::kPageActionCommandEvent) {
728 new_command = *CommandsInfo::GetPageActionCommand(extension);
729 } else { // This is a named command.
730 const CommandMap* named_commands =
731 CommandsInfo::GetNamedCommands(extension);
732 if (named_commands) {
733 CommandMap::const_iterator loc = named_commands->find(command_name);
734 if (loc != named_commands->end())
735 new_command = loc->second;
739 return Command::StringToAccelerator(
740 GetSuggestedKeyPref(extension, command_name), command_name) !=
741 new_command.accelerator();
744 std::string CommandService::GetSuggestedKeyPref(
745 const Extension* extension,
746 const std::string& command_name) {
747 // Get the previous suggested key, if any.
748 ui::Accelerator suggested_key;
749 ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
750 const base::DictionaryValue* commands_prefs = NULL;
751 if (extension_prefs->ReadPrefAsDictionary(extension->id(),
754 const base::DictionaryValue* suggested_key_prefs = NULL;
755 std::string suggested_key;
756 if (commands_prefs->GetDictionary(command_name, &suggested_key_prefs) &&
757 suggested_key_prefs->GetString(kSuggestedKey, &suggested_key)) {
758 return suggested_key;
762 return std::string();
765 void CommandService::RemoveKeybindingPrefs(const std::string& extension_id,
766 const std::string& command_name) {
767 DictionaryPrefUpdate updater(profile_->GetPrefs(),
768 prefs::kExtensionCommands);
769 base::DictionaryValue* bindings = updater.Get();
771 typedef std::vector<std::string> KeysToRemove;
772 KeysToRemove keys_to_remove;
773 for (base::DictionaryValue::Iterator it(*bindings); !it.IsAtEnd();
775 // Removal of keybinding preference should be limited to current platform.
776 if (!IsForCurrentPlatform(it.key()))
779 const base::DictionaryValue* item = NULL;
780 it.value().GetAsDictionary(&item);
782 std::string extension;
783 item->GetString(kExtension, &extension);
785 if (extension == extension_id) {
786 // If |command_name| is specified, delete only that command. Otherwise,
787 // delete all commands.
788 if (!command_name.empty()) {
790 item->GetString(kCommandName, &command);
791 if (command_name != command)
795 keys_to_remove.push_back(it.key());
799 for (KeysToRemove::const_iterator it = keys_to_remove.begin();
800 it != keys_to_remove.end(); ++it) {
801 std::string key = *it;
802 bindings->Remove(key, NULL);
804 std::pair<const std::string, const std::string> details =
805 std::make_pair(extension_id, command_name);
806 content::NotificationService::current()->Notify(
807 extensions::NOTIFICATION_EXTENSION_COMMAND_REMOVED,
808 content::Source<Profile>(profile_),
809 content::Details<std::pair<const std::string, const std::string> >(
814 bool CommandService::GetExtensionActionCommand(
815 const std::string& extension_id,
816 QueryType query_type,
819 ExtensionCommandType action_type) const {
820 const ExtensionSet& extensions =
821 ExtensionRegistry::Get(profile_)->enabled_extensions();
822 const Extension* extension = extensions.GetByID(extension_id);
828 const Command* requested_command = NULL;
829 switch (action_type) {
831 requested_command = CommandsInfo::GetBrowserActionCommand(extension);
834 requested_command = CommandsInfo::GetPageActionCommand(extension);
840 if (!requested_command)
843 // Look up to see if the user has overridden how the command should work.
844 Command saved_command =
845 FindCommandByName(extension_id, requested_command->command_name());
846 ui::Accelerator shortcut_assigned = saved_command.accelerator();
849 *active = (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN);
851 if (query_type == ACTIVE_ONLY &&
852 shortcut_assigned.key_code() == ui::VKEY_UNKNOWN)
855 *command = *requested_command;
856 if (shortcut_assigned.key_code() != ui::VKEY_UNKNOWN)
857 command->set_accelerator(shortcut_assigned);
864 BrowserContextKeyedAPIFactory<CommandService>::DeclareFactoryDependencies() {
865 DependsOn(ExtensionCommandsGlobalRegistry::GetFactoryInstance());
868 } // namespace extensions