| 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 |