command.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/command.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string_number_conversions.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string_split.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/string_util.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension_error_utils.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension_manifest_constants.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/generated_resources.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace errors = extension_manifest_errors;
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace keys = extension_manifest_keys;
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace values = extension_manifest_values;
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kMissing[] = "Missing";
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kCommandKeyNotSupported[] =
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "Command key is not supported. Note: Ctrl means Command on Mac";
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ui::Accelerator ParseImpl(const std::string& accelerator,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          const std::string& platform_key,
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          int index,
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          string16* error) {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (platform_key != values::kKeybindingPlatformWin &&
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformMac &&
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformChromeOs &&
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformLinux &&
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformDefault) {
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBindingUnknownPlatform,
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform_key);
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::Accelerator();
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> tokens;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::SplitString(accelerator, '+', &tokens);
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tokens.size() < 2 || tokens.size() > 3) {
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBinding,
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform_key,
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        accelerator);
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::Accelerator();
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now, parse it into an accelerator.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int modifiers = ui::EF_NONE;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::KeyboardCode key = ui::VKEY_UNKNOWN;
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tokens.size(); i++) {
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tokens[i] == "Ctrl") {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modifiers |= ui::EF_CONTROL_DOWN;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (tokens[i] == "Command") {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (platform_key == "mac") {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Either the developer specified Command+foo in the manifest for Mac or
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // they specified Ctrl and it got normalized to Command (to get Ctrl on
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Mac the developer has to specify MacCtrl). Therefore we treat this
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // as Command.
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modifiers |= ui::EF_COMMAND_DOWN;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_MACOSX)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (platform_key == "default") {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If we see "Command+foo" in the Default section it can mean two
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // things, depending on the platform:
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The developer specified "Ctrl+foo" for Default and it got normalized
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // on Mac to "Command+foo". This is fine. Treat it as Command.
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modifiers |= ui::EF_COMMAND_DOWN;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // No other platform supports Command.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = ui::VKEY_UNKNOWN;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (tokens[i] == "Alt") {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modifiers |= ui::EF_ALT_DOWN;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (tokens[i] == "Shift") {
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modifiers |= ui::EF_SHIFT_DOWN;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (tokens[i].size() == 1) {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (key != ui::VKEY_UNKNOWN) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Multiple key assignments.
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = ui::VKEY_UNKNOWN;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (tokens[i][0] >= 'A' && tokens[i][0] <= 'Z') {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = static_cast<ui::KeyboardCode>(ui::VKEY_A + (tokens[i][0] - 'A'));
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else if (tokens[i][0] >= '0' && tokens[i][0] <= '9') {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = static_cast<ui::KeyboardCode>(ui::VKEY_0 + (tokens[i][0] - '0'));
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = ui::VKEY_UNKNOWN;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errors::kInvalidKeyBinding,
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::IntToString(index),
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          platform_key,
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          accelerator);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ui::Accelerator();
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool command = (modifiers & ui::EF_COMMAND_DOWN) != 0;
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool ctrl = (modifiers & ui::EF_CONTROL_DOWN) != 0;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool alt = (modifiers & ui::EF_ALT_DOWN) != 0;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool shift = (modifiers & ui::EF_SHIFT_DOWN) != 0;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We support Ctrl+foo, Alt+foo, Ctrl+Shift+foo, Alt+Shift+foo, but not
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ctrl+Alt+foo and not Shift+foo either. For a more detailed reason why we
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // don't support Ctrl+Alt+foo see this article:
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx.
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // On Mac Command can also be used in combination with Shift or on its own,
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // as a modifier.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (key == ui::VKEY_UNKNOWN || (ctrl && alt) || (command && alt) ||
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (shift && !ctrl && !alt && !command)) {
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBinding,
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform_key,
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        accelerator);
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::Accelerator();
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ui::Accelerator(key, modifiers);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For Mac, we convert "Ctrl" to "Command" and "MacCtrl" to "Ctrl". Other
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// platforms leave the shortcut untouched.
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string NormalizeShortcutSuggestion(const std::string& suggestion,
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        const std::string& platform) {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool normalize = false;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (platform == "mac") {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    normalize = true;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else if (platform == "default") {
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_MACOSX)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    normalize = true;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!normalize)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return suggestion;
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string key;
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> tokens;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::SplitString(suggestion, '+', &tokens);
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tokens.size(); i++) {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (tokens[i] == "Ctrl")
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tokens[i] = "Command";
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else if (tokens[i] == "MacCtrl")
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tokens[i] = "Ctrl";
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return JoinString(tokens, '+');
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions {
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command::Command() {}
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command::Command(const std::string& command_name,
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 const string16& description,
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 const std::string& accelerator)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : command_name_(command_name),
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      description_(description) {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 error;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  accelerator_ = ParseImpl(accelerator, CommandPlatform(), 0, &error);
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command::~Command() {}
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string Command::CommandPlatform() {
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformWin;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_MACOSX)
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformMac;
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_CHROMEOS)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformChromeOs;
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_LINUX)
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformLinux;
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return "";
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ui::Accelerator Command::StringToAccelerator(const std::string& accelerator) {
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 error;
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Command command;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::Accelerator parsed =
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ParseImpl(accelerator, Command::CommandPlatform(), 0, &error);
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return parsed;
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool Command::Parse(DictionaryValue* command,
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const std::string& command_name,
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    int index,
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    string16* error) {
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!command_name.empty());
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We'll build up a map of platform-to-shortcut suggestions.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef std::map<const std::string, std::string> SuggestionMap;
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SuggestionMap suggestions;
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // First try to parse the |suggested_key| as a dictionary.
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DictionaryValue* suggested_key_dict;
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command->GetDictionary(keys::kSuggestedKey, &suggested_key_dict)) {
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DictionaryValue::key_iterator iter = suggested_key_dict->begin_keys();
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for ( ; iter != suggested_key_dict->end_keys(); ++iter) {
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // For each item in the dictionary, extract the platforms specified.
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string suggested_key_string;
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (suggested_key_dict->GetString(*iter, &suggested_key_string) &&
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !suggested_key_string.empty()) {
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Found a platform, add it to the suggestions list.
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        suggestions[*iter] = suggested_key_string;
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            errors::kInvalidKeyBinding,
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            base::IntToString(index),
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            keys::kSuggestedKey,
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            kMissing);
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // No dictionary was found, fall back to using just a string, so developers
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // don't have to specify a dictionary if they just want to use one default
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for all platforms.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string suggested_key_string;
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (command->GetString(keys::kSuggestedKey, &suggested_key_string) &&
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !suggested_key_string.empty()) {
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If only a single string is provided, it must be default for all.
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      suggestions["default"] = suggested_key_string;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errors::kInvalidKeyBinding,
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::IntToString(index),
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          keys::kSuggestedKey,
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          kMissing);
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       return false;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Normalize the suggestions.
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (SuggestionMap::iterator iter = suggestions.begin();
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != suggestions.end(); ++iter) {
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Before we normalize Ctrl to Command we must detect when the developer
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // specified Command in the Default section, which will work on Mac after
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // normalization but only fail on other platforms when they try it out on
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // other platforms, which is not what we want.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (iter->first == "default" &&
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        iter->second.find("Command+") != std::string::npos) {
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errors::kInvalidKeyBinding,
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::IntToString(index),
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          keys::kSuggestedKey,
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          kCommandKeyNotSupported);
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suggestions[iter->first] = NormalizeShortcutSuggestion(iter->second,
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           iter->first);
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string platform = CommandPlatform();
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string key = platform;
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (suggestions.find(key) == suggestions.end())
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    key = values::kKeybindingPlatformDefault;
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (suggestions.find(key) == suggestions.end()) {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBindingMissingPlatform,
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        keys::kSuggestedKey,
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform);
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;  // No platform specified and no fallback. Bail.
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For developer convenience, we parse all the suggestions (and complain about
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // errors for platforms other than the current one) but use only what we need.
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::map<const std::string, std::string>::const_iterator iter =
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      suggestions.begin();
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for ( ; iter != suggestions.end(); ++iter) {
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Note that we pass iter->first to pretend we are on a platform we're not
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // on.
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ui::Accelerator accelerator =
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ParseImpl(iter->second, iter->first, index, error);
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (accelerator.key_code() == ui::VKEY_UNKNOWN) {
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errors::kInvalidKeyBinding,
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::IntToString(index),
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          iter->first,
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          iter->second);
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (iter->first == key) {
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This platform is our platform, so grab this key.
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      accelerator_ = accelerator;
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      command_name_ = command_name;
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (command_name !=
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              extension_manifest_values::kPageActionCommandEvent &&
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          command_name !=
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              extension_manifest_values::kBrowserActionCommandEvent &&
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          command_name !=
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              extension_manifest_values::kScriptBadgeCommandEvent) {
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (!command->GetString(keys::kDescription, &description_) ||
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            description_.empty()) {
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              errors::kInvalidKeyBindingDescription,
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              base::IntToString(index));
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          return false;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)DictionaryValue* Command::ToValue(const Extension* extension,
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  bool active) const {
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DictionaryValue* extension_data = new DictionaryValue();
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 command_description;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_name() == values::kBrowserActionCommandEvent ||
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      command_name() == values::kPageActionCommandEvent ||
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      command_name() == values::kScriptBadgeCommandEvent) {
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command_description =
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_GENERIC_ACTIVATE);
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command_description = description();
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("description", command_description);
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetBoolean("active", active);
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("keybinding", accelerator().GetShortcutText());
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("command_name", command_name());
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("extension_id", extension->id());
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return extension_data;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace extensions
351