12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved. 22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file. 42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/extensions/api/commands/commands_handler.h" 62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h" 8868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h" 92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/values.h" 10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "chrome/common/extensions/command.h" 112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "extensions/common/error_utils.h" 12d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "extensions/common/manifest_constants.h" 131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "extensions/common/manifest_handlers/permissions_parser.h" 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace extensions { 162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)namespace keys = manifest_keys; 183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) 192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace { 20c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// The maximum number of commands (including page action/browser actions) with a 21c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// keybinding an extension can have. 22c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)const int kMaxCommandsWithKeybindingPerExtension = 4; 232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} // namespace 242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)CommandsInfo::CommandsInfo() { 262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)CommandsInfo::~CommandsInfo() { 292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static 322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const Command* CommandsInfo::GetBrowserActionCommand( 332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const Extension* extension) { 342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CommandsInfo* info = static_cast<CommandsInfo*>( 352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) extension->GetManifestData(keys::kCommands)); 362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return info ? info->browser_action_command.get() : NULL; 372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static 402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const Command* CommandsInfo::GetPageActionCommand(const Extension* extension) { 412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CommandsInfo* info = static_cast<CommandsInfo*>( 422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) extension->GetManifestData(keys::kCommands)); 432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return info ? info->page_action_command.get() : NULL; 442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static 472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const CommandMap* CommandsInfo::GetNamedCommands(const Extension* extension) { 482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CommandsInfo* info = static_cast<CommandsInfo*>( 492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) extension->GetManifestData(keys::kCommands)); 502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return info ? &info->named_commands : NULL; 512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)CommandsHandler::CommandsHandler() { 542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)CommandsHandler::~CommandsHandler() { 572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 59a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool CommandsHandler::Parse(Extension* extension, base::string16* error) { 602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!extension->manifest()->HasKey(keys::kCommands)) { 612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) scoped_ptr<CommandsInfo> commands_info(new CommandsInfo); 622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) MaybeSetBrowserActionDefault(extension, commands_info.get()); 632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) extension->SetManifestData(keys::kCommands, 642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) commands_info.release()); 652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) const base::DictionaryValue* dict = NULL; 692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!extension->manifest()->GetDictionary(keys::kCommands, &dict)) { 705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) *error = base::ASCIIToUTF16(manifest_errors::kInvalidCommandsKey); 712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) scoped_ptr<CommandsInfo> commands_info(new CommandsInfo); 752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) int command_index = 0; 77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) int keybindings_found = 0; 785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) for (base::DictionaryValue::Iterator iter(*dict); !iter.IsAtEnd(); 792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) iter.Advance()) { 802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) ++command_index; 812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) const base::DictionaryValue* command = NULL; 832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!iter.value().GetAsDictionary(&command)) { 842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) *error = ErrorUtils::FormatErrorMessageUTF16( 85d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) manifest_errors::kInvalidKeyBindingDictionary, 862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) base::IntToString(command_index)); 872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; 882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) scoped_ptr<extensions::Command> binding(new Command()); 912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (!binding->Parse(command, iter.key(), command_index, error)) 922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return false; // |error| already set. 932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) if (binding->accelerator().key_code() != ui::VKEY_UNKNOWN) { 95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // Only media keys are allowed to work without modifiers, and because 96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // media keys aren't registered exclusively they should not count towards 97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) // the max of four shortcuts per extension. 98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!Command::IsMediaKey(binding->accelerator())) 99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) ++keybindings_found; 100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (keybindings_found > kMaxCommandsWithKeybindingPerExtension && 1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci !PermissionsParser::HasAPIPermission( 1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci extension, APIPermission::kCommandsAccessibility)) { 104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) *error = ErrorUtils::FormatErrorMessageUTF16( 105d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) manifest_errors::kInvalidKeyBindingTooMany, 106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) base::IntToString(kMaxCommandsWithKeybindingPerExtension)); 107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) return false; 108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) } 110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) 1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) std::string command_name = binding->command_name(); 112d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) if (command_name == manifest_values::kBrowserActionCommandEvent) { 1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) commands_info->browser_action_command.reset(binding.release()); 1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } else if (command_name == 115d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) manifest_values::kPageActionCommandEvent) { 1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) commands_info->page_action_command.reset(binding.release()); 1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } else { 1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (command_name[0] != '_') // All commands w/underscore are reserved. 1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) commands_info->named_commands[command_name] = *binding.get(); 1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) MaybeSetBrowserActionDefault(extension, commands_info.get()); 1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) extension->SetManifestData(keys::kCommands, 1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) commands_info.release()); 1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return true; 1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool CommandsHandler::AlwaysParseForType(Manifest::Type type) const { 1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return type == Manifest::TYPE_EXTENSION || 1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) type == Manifest::TYPE_LEGACY_PACKAGED_APP || 1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) type == Manifest::TYPE_PLATFORM_APP; 1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void CommandsHandler::MaybeSetBrowserActionDefault(const Extension* extension, 1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) CommandsInfo* info) { 1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) if (extension->manifest()->HasKey(keys::kBrowserAction) && 1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) !info->browser_action_command.get()) { 140c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) info->browser_action_command.reset( 141d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles) new Command(manifest_values::kBrowserActionCommandEvent, 142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::string16(), 1434e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) std::string(), 1444e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles) false)); 1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) } 1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const std::vector<std::string> CommandsHandler::Keys() const { 1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) return SingleKey(keys::kCommands); 1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} 1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)} // namespace extensions 153