Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / commands / command_service.cc
index 16c366e..66ad30a 100644 (file)
@@ -4,28 +4,39 @@
 
 #include "chrome/browser/extensions/api/commands/command_service.h"
 
+#include <vector>
+
 #include "base/lazy_instance.h"
 #include "base/prefs/scoped_user_pref_update.h"
+#include "base/strings/string_split.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/extensions/api/commands/commands.h"
 #include "chrome/browser/extensions/extension_commands_global_registry.h"
-#include "chrome/browser/extensions/extension_function_registry.h"
 #include "chrome/browser/extensions/extension_keybinding_registry.h"
-#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/accelerator_utils.h"
 #include "chrome/common/extensions/api/commands/commands_handler.h"
-#include "chrome/common/extensions/feature_switch.h"
+#include "chrome/common/extensions/manifest_handlers/settings_overrides_handler.h"
+#include "chrome/common/extensions/manifest_handlers/ui_overrides_handler.h"
 #include "chrome/common/pref_names.h"
 #include "components/user_prefs/pref_registry_syncable.h"
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
+#include "extensions/browser/extension_function_registry.h"
+#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/feature_switch.h"
+#include "extensions/common/manifest_constants.h"
+#include "extensions/common/permissions/permissions_data.h"
 
 using extensions::Extension;
 using extensions::ExtensionPrefs;
+using extensions::SettingsOverrides;
+using extensions::UIOverrides;
 
 namespace {
 
@@ -38,9 +49,23 @@ const char kGlobal[] = "global";
 const char kInitialBindingsHaveBeenAssigned[] = "initial_keybindings_set";
 
 std::string GetPlatformKeybindingKeyForAccelerator(
-    const ui::Accelerator& accelerator) {
-  return extensions::Command::CommandPlatform() + ":" +
-         extensions::Command::AcceleratorToString(accelerator);
+    const ui::Accelerator& accelerator, const std::string extension_id) {
+  std::string key = extensions::Command::CommandPlatform() + ":" +
+                    extensions::Command::AcceleratorToString(accelerator);
+
+  // Media keys have a 1-to-many relationship with targets, unlike regular
+  // shortcut (1-to-1 relationship). That means two or more extensions can
+  // register for the same media key so the extension ID needs to be added to
+  // the key to make sure the key is unique.
+  if (extensions::Command::IsMediaKey(accelerator))
+    key += ":" + extension_id;
+
+  return key;
+}
+
+bool IsForCurrentPlatform(const std::string& key) {
+  return StartsWithASCII(
+      key, extensions::Command::CommandPlatform() + ":", true);
 }
 
 void SetInitialBindingsHaveBeenAssigned(
@@ -60,15 +85,49 @@ bool InitialBindingsHaveBeenAssigned(
   return assigned;
 }
 
-bool IsWhitelistedGlobalShortcut(const extensions::Command& command) {
-  if (!command.global())
+// Checks if |extension| is permitted to automatically assign the |accelerator|
+// key.
+bool CanAutoAssign(const ui::Accelerator& accelerator,
+                   const Extension* extension,
+                   Profile* profile,
+                   bool is_named_command,
+                   bool is_global) {
+  // Media Keys are non-exclusive, so allow auto-assigning them.
+  if (extensions::Command::IsMediaKey(accelerator))
     return true;
-  if (!command.accelerator().IsCtrlDown())
-    return false;
-  if (!command.accelerator().IsShiftDown())
-    return false;
-  return (command.accelerator().key_code() >= ui::VKEY_0 &&
-          command.accelerator().key_code() <= ui::VKEY_9);
+
+  if (is_global) {
+    if (!is_named_command)
+      return false;  // Browser and page actions are not global in nature.
+
+    // Global shortcuts are restricted to (Ctrl|Command)+Shift+[0-9].
+#if defined OS_MACOSX
+    if (!accelerator.IsCmdDown())
+      return false;
+#else
+    if (!accelerator.IsCtrlDown())
+      return false;
+#endif
+    if (!accelerator.IsShiftDown())
+      return false;
+    return (accelerator.key_code() >= ui::VKEY_0 &&
+            accelerator.key_code() <= ui::VKEY_9);
+  } else {
+    // Not a global command, check if Chrome shortcut and whether
+    // we can override it.
+    if (accelerator ==
+        chrome::GetPrimaryChromeAcceleratorForCommandId(IDC_BOOKMARK_PAGE) &&
+        extensions::CommandService::RemovesBookmarkShortcut(extension)) {
+      // If this check fails it either means we have an API to override a
+      // key that isn't a ChromeAccelerator (and the API can therefore be
+      // deprecated) or the IsChromeAccelerator isn't consistently
+      // returning true for all accelerators.
+      DCHECK(chrome::IsChromeAccelerator(accelerator, profile));
+      return true;
+    }
+
+    return !chrome::IsChromeAccelerator(accelerator, profile);
+  }
 }
 
 }  // namespace
@@ -83,70 +142,97 @@ void CommandService::RegisterProfilePrefs(
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
 }
 
-CommandService::CommandService(Profile* profile)
-    : profile_(profile) {
+CommandService::CommandService(content::BrowserContext* context)
+    : profile_(Profile::FromBrowserContext(context)) {
   ExtensionFunctionRegistry::GetInstance()->
       RegisterFunction<GetAllCommandsFunction>();
 
-  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
-      content::Source<Profile>(profile));
-  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
-      content::Source<Profile>(profile));
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_EXTENSION_INSTALLED,
+                 content::Source<Profile>(profile_));
+  registrar_.Add(this,
+                 chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
+                 content::Source<Profile>(profile_));
 }
 
 CommandService::~CommandService() {
 }
 
-static base::LazyInstance<ProfileKeyedAPIFactory<CommandService> >
-g_factory = LAZY_INSTANCE_INITIALIZER;
+static base::LazyInstance<BrowserContextKeyedAPIFactory<CommandService> >
+    g_factory = LAZY_INSTANCE_INITIALIZER;
 
 // static
-ProfileKeyedAPIFactory<CommandService>* CommandService::GetFactoryInstance() {
-  return &g_factory.Get();
+BrowserContextKeyedAPIFactory<CommandService>*
+CommandService::GetFactoryInstance() {
+  return g_factory.Pointer();
 }
 
 // static
-CommandService* CommandService::Get(Profile* profile) {
-  return ProfileKeyedAPIFactory<CommandService>::GetForProfile(profile);
+CommandService* CommandService::Get(content::BrowserContext* context) {
+  return BrowserContextKeyedAPIFactory<CommandService>::Get(context);
 }
 
-bool CommandService::GetBrowserActionCommand(
-    const std::string& extension_id,
-    QueryType type,
-    extensions::Command* command,
-    bool* active) {
+// static
+bool CommandService::RemovesBookmarkShortcut(
+    const extensions::Extension* extension) {
+  const UIOverrides* ui_overrides = UIOverrides::Get(extension);
+  const SettingsOverrides* settings_overrides =
+      SettingsOverrides::Get(extension);
+
+  return ((settings_overrides &&
+           SettingsOverrides::RemovesBookmarkShortcut(*settings_overrides)) ||
+          (ui_overrides &&
+           UIOverrides::RemovesBookmarkShortcut(*ui_overrides))) &&
+      (extensions::PermissionsData::HasAPIPermission(
+          extension,
+          extensions::APIPermission::kBookmarkManagerPrivate) ||
+       extensions::FeatureSwitch::enable_override_bookmarks_ui()->
+           IsEnabled());
+}
+
+// static
+bool CommandService::RemovesBookmarkOpenPagesShortcut(
+    const extensions::Extension* extension) {
+  const UIOverrides* ui_overrides = UIOverrides::Get(extension);
+  const SettingsOverrides* settings_overrides =
+      SettingsOverrides::Get(extension);
+
+  return ((settings_overrides &&
+           SettingsOverrides::RemovesBookmarkOpenPagesShortcut(
+               *settings_overrides)) ||
+          (ui_overrides &&
+           UIOverrides::RemovesBookmarkOpenPagesShortcut(*ui_overrides))) &&
+      (extensions::PermissionsData::HasAPIPermission(
+          extension,
+          extensions::APIPermission::kBookmarkManagerPrivate) ||
+       extensions::FeatureSwitch::enable_override_bookmarks_ui()->
+           IsEnabled());
+}
+
+bool CommandService::GetBrowserActionCommand(const std::string& extension_id,
+                                             QueryType type,
+                                             extensions::Command* command,
+                                             bool* active) const {
   return GetExtensionActionCommand(
       extension_id, type, command, active, BROWSER_ACTION);
 }
 
-bool CommandService::GetPageActionCommand(
-    const std::string& extension_id,
-    QueryType type,
-    extensions::Command* command,
-    bool* active) {
+bool CommandService::GetPageActionCommand(const std::string& extension_id,
+                                          QueryType type,
+                                          extensions::Command* command,
+                                          bool* active) const {
   return GetExtensionActionCommand(
       extension_id, type, command, active, PAGE_ACTION);
 }
 
-bool CommandService::GetScriptBadgeCommand(
+bool CommandService::GetNamedCommands(
     const std::string& extension_id,
     QueryType type,
-    extensions::Command* command,
-    bool* active) {
-  return GetExtensionActionCommand(
-      extension_id, type, command, active, SCRIPT_BADGE);
-}
-
-bool CommandService::GetNamedCommands(const std::string& extension_id,
-                                      QueryType type,
-                                      CommandScope scope,
-                                      extensions::CommandMap* command_map) {
-  ExtensionService* extension_service =
-      ExtensionSystem::Get(profile_)->extension_service();
-  if (!extension_service)
-    return false;  // Can occur during testing.
-  const ExtensionSet* extensions = extension_service->extensions();
-  const Extension* extension = extensions->GetByID(extension_id);
+    CommandScope scope,
+    extensions::CommandMap* command_map) const {
+  const ExtensionSet& extensions =
+      ExtensionRegistry::Get(profile_)->enabled_extensions();
+  const Extension* extension = extensions.GetByID(extension_id);
   CHECK(extension);
 
   command_map->clear();
@@ -188,14 +274,33 @@ bool CommandService::AddKeybindingPref(
   if (accelerator.key_code() == ui::VKEY_UNKNOWN)
     return false;
 
+  // Media Keys are allowed to be used by named command only.
+  DCHECK(!Command::IsMediaKey(accelerator) ||
+         (command_name != manifest_values::kPageActionCommandEvent &&
+          command_name != manifest_values::kBrowserActionCommandEvent));
+
   DictionaryPrefUpdate updater(profile_->GetPrefs(),
                                prefs::kExtensionCommands);
   base::DictionaryValue* bindings = updater.Get();
 
-  std::string key = GetPlatformKeybindingKeyForAccelerator(accelerator);
+  std::string key = GetPlatformKeybindingKeyForAccelerator(accelerator,
+                                                           extension_id);
 
-  if (!allow_overrides && bindings->HasKey(key))
-    return false;  // Already taken.
+  if (bindings->HasKey(key)) {
+    if (!allow_overrides)
+      return false;  // Already taken.
+
+    // If the shortcut has been assigned to another command, it should be
+    // removed before overriding, so that |ExtensionKeybindingRegistry| can get
+    // a chance to do clean-up.
+    const base::DictionaryValue* item = NULL;
+    bindings->GetDictionary(key, &item);
+    std::string old_extension_id;
+    std::string old_command_name;
+    item->GetString(kExtension, &old_extension_id);
+    item->GetString(kCommandName, &old_command_name);
+    RemoveKeybindingPrefs(old_extension_id, old_command_name);
+  }
 
   base::DictionaryValue* keybinding = new base::DictionaryValue();
   keybinding->SetString(kExtension, extension_id);
@@ -265,8 +370,8 @@ bool CommandService::SetScope(const std::string& extension_id,
   return true;
 }
 
-Command CommandService::FindCommandByName(
-    const std::string& extension_id, const std::string& command) {
+Command CommandService::FindCommandByName(const std::string& extension_id,
+                                          const std::string& command) const {
   const base::DictionaryValue* bindings =
       profile_->GetPrefs()->GetDictionary(prefs::kExtensionCommands);
   for (base::DictionaryValue::Iterator it(*bindings); !it.IsAtEnd();
@@ -282,83 +387,141 @@ Command CommandService::FindCommandByName(
     item->GetString(kCommandName, &command_name);
     if (command != command_name)
       continue;
+    // Format stored in Preferences is: "Platform:Shortcut[:ExtensionId]".
+    std::string shortcut = it.key();
+    if (!IsForCurrentPlatform(shortcut))
+      continue;
     bool global = false;
     if (FeatureSwitch::global_commands()->IsEnabled())
       item->GetBoolean(kGlobal, &global);
 
-    std::string shortcut = it.key();
-    if (StartsWithASCII(shortcut, Command::CommandPlatform() + ":", true))
-      shortcut = shortcut.substr(Command::CommandPlatform().length() + 1);
+    std::vector<std::string> tokens;
+    base::SplitString(shortcut, ':', &tokens);
+    CHECK(tokens.size() >= 2);
+    shortcut = tokens[1];
 
-    return Command(command_name, string16(), shortcut, global);
+    return Command(command_name, base::string16(), shortcut, global);
   }
 
   return Command();
 }
 
+bool CommandService::GetBoundExtensionCommand(
+    const std::string& extension_id,
+    const ui::Accelerator& accelerator,
+    extensions::Command* command,
+    ExtensionCommandType* command_type) const {
+  const ExtensionSet& extensions =
+      ExtensionRegistry::Get(profile_)->enabled_extensions();
+  const Extension* extension = extensions.GetByID(extension_id);
+  CHECK(extension);
+
+  extensions::Command prospective_command;
+  extensions::CommandMap command_map;
+  bool active = false;
+  if (GetBrowserActionCommand(extension_id,
+                              extensions::CommandService::ACTIVE_ONLY,
+                              &prospective_command,
+                              &active) && active &&
+          accelerator == prospective_command.accelerator()) {
+    if (command)
+      *command = prospective_command;
+    if (command_type)
+      *command_type = BROWSER_ACTION;
+    return true;
+  } else if (GetPageActionCommand(extension_id,
+                                  extensions::CommandService::ACTIVE_ONLY,
+                                  &prospective_command,
+                                  &active) && active &&
+                 accelerator == prospective_command.accelerator()) {
+    if (command)
+      *command = prospective_command;
+    if (command_type)
+      *command_type = PAGE_ACTION;
+    return true;
+  } else if (GetNamedCommands(extension_id,
+                              extensions::CommandService::ACTIVE_ONLY,
+                              extensions::CommandService::REGULAR,
+                              &command_map)) {
+    for (extensions::CommandMap::const_iterator it = command_map.begin();
+         it != command_map.end(); ++it) {
+      if (accelerator == it->second.accelerator()) {
+        if (command)
+          *command = it->second;
+        if (command_type)
+          *command_type = NAMED;
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+bool CommandService::OverridesBookmarkShortcut(
+    const extensions::Extension* extension) const {
+  return RemovesBookmarkShortcut(extension) &&
+      GetBoundExtensionCommand(
+          extension->id(),
+          chrome::GetPrimaryChromeAcceleratorForCommandId(IDC_BOOKMARK_PAGE),
+          NULL,
+          NULL);
+}
+
 void CommandService::AssignInitialKeybindings(const Extension* extension) {
   const extensions::CommandMap* commands =
       CommandsInfo::GetNamedCommands(extension);
   if (!commands)
     return;
 
-  ExtensionService* extension_service =
-      ExtensionSystem::Get(profile_)->extension_service();
-  ExtensionPrefs* extension_prefs = extension_service->extension_prefs();
+  ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile_);
   if (InitialBindingsHaveBeenAssigned(extension_prefs, extension->id()))
     return;
   SetInitialBindingsHaveBeenAssigned(extension_prefs, extension->id());
 
   extensions::CommandMap::const_iterator iter = commands->begin();
   for (; iter != commands->end(); ++iter) {
-    if (!chrome::IsChromeAccelerator(
-            iter->second.accelerator(), profile_) &&
-        IsWhitelistedGlobalShortcut(iter->second)) {
-      AddKeybindingPref(iter->second.accelerator(),
+    const extensions::Command command = iter->second;
+    if (CanAutoAssign(command.accelerator(),
+                      extension,
+                      profile_,
+                      true,  // Is a named command.
+                      command.global())) {
+      AddKeybindingPref(command.accelerator(),
                         extension->id(),
-                        iter->second.command_name(),
+                        command.command_name(),
                         false,  // Overwriting not allowed.
-                        iter->second.global());
+                        command.global());
     }
   }
 
   const extensions::Command* browser_action_command =
       CommandsInfo::GetBrowserActionCommand(extension);
-  if (browser_action_command) {
-    if (!chrome::IsChromeAccelerator(
-        browser_action_command->accelerator(), profile_)) {
-      AddKeybindingPref(browser_action_command->accelerator(),
-                        extension->id(),
-                        browser_action_command->command_name(),
-                        false,   // Overwriting not allowed.
-                        false);  // Browser actions can't be global.
-    }
+  if (browser_action_command &&
+      CanAutoAssign(browser_action_command->accelerator(),
+                    extension,
+                    profile_,
+                    false,     // Not a named command.
+                    false)) {  // Not global.
+    AddKeybindingPref(browser_action_command->accelerator(),
+                      extension->id(),
+                      browser_action_command->command_name(),
+                      false,   // Overwriting not allowed.
+                      false);  // Not global.
   }
 
   const extensions::Command* page_action_command =
       CommandsInfo::GetPageActionCommand(extension);
-  if (page_action_command) {
-    if (!chrome::IsChromeAccelerator(
-        page_action_command->accelerator(), profile_)) {
-      AddKeybindingPref(page_action_command->accelerator(),
-                        extension->id(),
-                        page_action_command->command_name(),
-                        false,   // Overwriting not allowed.
-                        false);  // Page actions can't be global.
-    }
-  }
-
-  const extensions::Command* script_badge_command =
-      CommandsInfo::GetScriptBadgeCommand(extension);
-  if (script_badge_command) {
-    if (!chrome::IsChromeAccelerator(
-        script_badge_command->accelerator(), profile_)) {
-      AddKeybindingPref(script_badge_command->accelerator(),
-                        extension->id(),
-                        script_badge_command->command_name(),
-                        false,   // Overwriting not allowed.
-                        false);  // Script badges can't be global.
-    }
+  if (page_action_command &&
+      CanAutoAssign(page_action_command->accelerator(),
+                    extension,
+                    profile_,
+                    false,     // Not a named command.
+                    false)) {  // Not global.
+    AddKeybindingPref(page_action_command->accelerator(),
+                      extension->id(),
+                      page_action_command->command_name(),
+                      false,   // Overwriting not allowed.
+                      false);  // Not global.
   }
 }
 
@@ -372,6 +535,10 @@ void CommandService::RemoveKeybindingPrefs(const std::string& extension_id,
   KeysToRemove keys_to_remove;
   for (base::DictionaryValue::Iterator it(*bindings); !it.IsAtEnd();
        it.Advance()) {
+    // Removal of keybinding preference should be limited to current platform.
+    if (!IsForCurrentPlatform(it.key()))
+      continue;
+
     const base::DictionaryValue* item = NULL;
     it.value().GetAsDictionary(&item);
 
@@ -412,13 +579,10 @@ bool CommandService::GetExtensionActionCommand(
     QueryType query_type,
     extensions::Command* command,
     bool* active,
-    ExtensionActionType action_type) {
-  ExtensionService* service =
-      ExtensionSystem::Get(profile_)->extension_service();
-  if (!service)
-    return false;  // Can happen in tests.
-  const ExtensionSet* extensions = service->extensions();
-  const Extension* extension = extensions->GetByID(extension_id);
+    ExtensionCommandType action_type) const {
+  const ExtensionSet& extensions =
+      ExtensionRegistry::Get(profile_)->enabled_extensions();
+  const Extension* extension = extensions.GetByID(extension_id);
   CHECK(extension);
 
   if (active)
@@ -432,9 +596,9 @@ bool CommandService::GetExtensionActionCommand(
     case PAGE_ACTION:
       requested_command = CommandsInfo::GetPageActionCommand(extension);
       break;
-    case SCRIPT_BADGE:
-      requested_command = CommandsInfo::GetScriptBadgeCommand(extension);
-      break;
+    case NAMED:
+      NOTREACHED();
+      return false;
   }
   if (!requested_command)
     return false;
@@ -459,7 +623,8 @@ bool CommandService::GetExtensionActionCommand(
 }
 
 template <>
-void ProfileKeyedAPIFactory<CommandService>::DeclareFactoryDependencies() {
+void
+BrowserContextKeyedAPIFactory<CommandService>::DeclareFactoryDependencies() {
   DependsOn(ExtensionCommandsGlobalRegistry::GetFactoryInstance());
 }