command.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h"
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/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_manifest_constants.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "extensions/common/error_utils.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)
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using extensions::ErrorUtils;
23a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)using extensions::Command;
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kMissing[] = "Missing";
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kCommandKeyNotSupported[] =
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "Command key is not supported. Note: Ctrl means Command on Mac";
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ui::Accelerator ParseImpl(const std::string& accelerator,
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          const std::string& platform_key,
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          int index,
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          string16* error) {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (platform_key != values::kKeybindingPlatformWin &&
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformMac &&
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformChromeOs &&
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformLinux &&
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformDefault) {
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *error = ErrorUtils::FormatErrorMessageUTF16(
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBindingUnknownPlatform,
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform_key);
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::Accelerator();
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> tokens;
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::SplitString(accelerator, '+', &tokens);
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (tokens.size() < 2 || tokens.size() > 3) {
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *error = ErrorUtils::FormatErrorMessageUTF16(
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBinding,
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform_key,
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        accelerator);
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::Accelerator();
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now, parse it into an accelerator.
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int modifiers = ui::EF_NONE;
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::KeyboardCode key = ui::VKEY_UNKNOWN;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tokens.size(); i++) {
63a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    if (tokens[i] == values::kKeyCtrl) {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modifiers |= ui::EF_CONTROL_DOWN;
65a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    } else if (tokens[i] == values::kKeyCommand) {
66a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      if (platform_key == values::kKeybindingPlatformMac) {
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Either the developer specified Command+foo in the manifest for Mac or
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // they specified Ctrl and it got normalized to Command (to get Ctrl on
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Mac the developer has to specify MacCtrl). Therefore we treat this
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // as Command.
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modifiers |= ui::EF_COMMAND_DOWN;
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_MACOSX)
73a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (platform_key == values::kKeybindingPlatformDefault) {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If we see "Command+foo" in the Default section it can mean two
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // things, depending on the platform:
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The developer specified "Ctrl+foo" for Default and it got normalized
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // on Mac to "Command+foo". This is fine. Treat it as Command.
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modifiers |= ui::EF_COMMAND_DOWN;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // No other platform supports Command.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = ui::VKEY_UNKNOWN;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
85a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    } else if (tokens[i] == values::kKeyAlt) {
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modifiers |= ui::EF_ALT_DOWN;
87a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    } else if (tokens[i] == values::kKeyShift) {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modifiers |= ui::EF_SHIFT_DOWN;
89a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    } else if (tokens[i].size() == 1 ||  // A-Z, 0-9.
90a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyComma ||
91a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyPeriod ||
92a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyUp ||
93a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyDown ||
94a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyLeft ||
95a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyRight ||
96a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyIns ||
97a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyDel ||
98a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyHome ||
99a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyEnd ||
100a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyPgUp ||
101a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyPgDwn ||
102a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyTab) {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (key != ui::VKEY_UNKNOWN) {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Multiple key assignments.
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = ui::VKEY_UNKNOWN;
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
108a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
109a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      if (tokens[i] == values::kKeyComma) {
110a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_OEM_COMMA;
111a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyPeriod) {
112a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_OEM_PERIOD;
113a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyUp) {
114a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_UP;
115a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyDown) {
116a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_DOWN;
117a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyLeft) {
118a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_LEFT;
119a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyRight) {
120a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_RIGHT;
121a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyIns) {
122a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_INSERT;
123a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyDel) {
124a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_DELETE;
125a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyHome) {
126a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_HOME;
127a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyEnd) {
128a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_END;
129a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyPgUp) {
130a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_PRIOR;
131a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyPgDwn) {
132a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_NEXT;
133a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyTab) {
134a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_TAB;
135a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i].size() == 1 &&
136a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)                 tokens[i][0] >= 'A' && tokens[i][0] <= 'Z') {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = static_cast<ui::KeyboardCode>(ui::VKEY_A + (tokens[i][0] - 'A'));
138a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i].size() == 1 &&
139a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)                 tokens[i][0] >= '0' && tokens[i][0] <= '9') {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = static_cast<ui::KeyboardCode>(ui::VKEY_0 + (tokens[i][0] - '0'));
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = ui::VKEY_UNKNOWN;
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      *error = ErrorUtils::FormatErrorMessageUTF16(
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errors::kInvalidKeyBinding,
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::IntToString(index),
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          platform_key,
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          accelerator);
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ui::Accelerator();
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool command = (modifiers & ui::EF_COMMAND_DOWN) != 0;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool ctrl = (modifiers & ui::EF_CONTROL_DOWN) != 0;
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool alt = (modifiers & ui::EF_ALT_DOWN) != 0;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool shift = (modifiers & ui::EF_SHIFT_DOWN) != 0;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We support Ctrl+foo, Alt+foo, Ctrl+Shift+foo, Alt+Shift+foo, but not
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ctrl+Alt+foo and not Shift+foo either. For a more detailed reason why we
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // don't support Ctrl+Alt+foo see this article:
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // On Mac Command can also be used in combination with Shift or on its own,
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // as a modifier.
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (key == ui::VKEY_UNKNOWN || (ctrl && alt) || (command && alt) ||
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (shift && !ctrl && !alt && !command)) {
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *error = ErrorUtils::FormatErrorMessageUTF16(
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBinding,
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform_key,
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        accelerator);
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::Accelerator();
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ui::Accelerator(key, modifiers);
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For Mac, we convert "Ctrl" to "Command" and "MacCtrl" to "Ctrl". Other
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// platforms leave the shortcut untouched.
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string NormalizeShortcutSuggestion(const std::string& suggestion,
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        const std::string& platform) {
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool normalize = false;
183a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (platform == values::kKeybindingPlatformMac) {
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    normalize = true;
185a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  } else if (platform == values::kKeybindingPlatformDefault) {
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_MACOSX)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    normalize = true;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!normalize)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return suggestion;
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string key;
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> tokens;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::SplitString(suggestion, '+', &tokens);
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tokens.size(); i++) {
198a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    if (tokens[i] == values::kKeyCtrl)
199a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      tokens[i] = values::kKeyCommand;
200a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    else if (tokens[i] == values::kKeyMacCtrl)
201a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      tokens[i] = values::kKeyCtrl;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return JoinString(tokens, '+');
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions {
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command::Command() {}
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command::Command(const std::string& command_name,
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 const string16& description,
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 const std::string& accelerator)
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : command_name_(command_name),
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      description_(description) {
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 error;
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  accelerator_ = ParseImpl(accelerator, CommandPlatform(), 0, &error);
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command::~Command() {}
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string Command::CommandPlatform() {
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformWin;
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_MACOSX)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformMac;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_CHROMEOS)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformChromeOs;
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_LINUX)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformLinux;
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return "";
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ui::Accelerator Command::StringToAccelerator(const std::string& accelerator) {
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 error;
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Command command;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::Accelerator parsed =
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ParseImpl(accelerator, Command::CommandPlatform(), 0, &error);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return parsed;
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
247a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)// static
248a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)std::string Command::AcceleratorToString(const ui::Accelerator& accelerator) {
249a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  std::string shortcut;
250a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
251a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  // Ctrl and Alt are mutually exclusive.
252a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (accelerator.IsCtrlDown())
253a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeyCtrl;
254a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  else if (accelerator.IsAltDown())
255a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeyAlt;
256a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (!shortcut.empty())
257a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeySeparator;
258a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
259a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (accelerator.IsCmdDown()) {
260a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeyCommand;
261a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeySeparator;
262a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  }
263a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
264a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (accelerator.IsShiftDown()) {
265a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeyShift;
266a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeySeparator;
267a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  }
268a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
269a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (accelerator.key_code() >= ui::VKEY_0 &&
270a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      accelerator.key_code() <= ui::VKEY_9) {
271a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += '0' + (accelerator.key_code() - ui::VKEY_0);
272a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  } else if (accelerator.key_code() >= ui::VKEY_A &&
273a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)           accelerator.key_code() <= ui::VKEY_Z) {
274a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += 'A' + (accelerator.key_code() - ui::VKEY_A);
275a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  } else {
276a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    switch (accelerator.key_code()) {
277a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_OEM_COMMA:
278a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyComma;
279a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
280a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_OEM_PERIOD:
281a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyPeriod;
282a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
283a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_UP:
284a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyUp;
285a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
286a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_DOWN:
287a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyDown;
288a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
289a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_LEFT:
290a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyLeft;
291a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
292a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_RIGHT:
293a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyRight;
294a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
295a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_INSERT:
296a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyIns;
297a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
298a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_DELETE:
299a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyDel;
300a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
301a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_HOME:
302a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyHome;
303a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
304a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_END:
305a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyEnd;
306a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
307a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_PRIOR:
308a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyPgUp;
309a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
310a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_NEXT:
311a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyPgDwn;
312a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
313a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_TAB:
314a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyTab;
315a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
316a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      default:
317a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        return "";
318a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    }
319a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  }
320a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  return shortcut;
321a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)}
322a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
3237d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)bool Command::Parse(const base::DictionaryValue* command,
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const std::string& command_name,
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    int index,
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    string16* error) {
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!command_name.empty());
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
329c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  string16 description;
330c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (command_name != values::kPageActionCommandEvent &&
331c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      command_name != values::kBrowserActionCommandEvent &&
332c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      command_name != values::kScriptBadgeCommandEvent) {
333c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (!command->GetString(keys::kDescription, &description) ||
334c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        description.empty()) {
335c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      *error = ErrorUtils::FormatErrorMessageUTF16(
336c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          errors::kInvalidKeyBindingDescription,
337c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          base::IntToString(index));
338c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return false;
339c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
340c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
341c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We'll build up a map of platform-to-shortcut suggestions.
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef std::map<const std::string, std::string> SuggestionMap;
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SuggestionMap suggestions;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // First try to parse the |suggested_key| as a dictionary.
3477d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  const base::DictionaryValue* suggested_key_dict;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command->GetDictionary(keys::kSuggestedKey, &suggested_key_dict)) {
3497d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    for (base::DictionaryValue::Iterator iter(*suggested_key_dict);
3507d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)         !iter.IsAtEnd(); iter.Advance()) {
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // For each item in the dictionary, extract the platforms specified.
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string suggested_key_string;
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (iter.value().GetAsString(&suggested_key_string) &&
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !suggested_key_string.empty()) {
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Found a platform, add it to the suggestions list.
3562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        suggestions[iter.key()] = suggested_key_string;
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
3582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        *error = ErrorUtils::FormatErrorMessageUTF16(
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            errors::kInvalidKeyBinding,
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            base::IntToString(index),
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            keys::kSuggestedKey,
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            kMissing);
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // No dictionary was found, fall back to using just a string, so developers
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // don't have to specify a dictionary if they just want to use one default
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for all platforms.
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string suggested_key_string;
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (command->GetString(keys::kSuggestedKey, &suggested_key_string) &&
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !suggested_key_string.empty()) {
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If only a single string is provided, it must be default for all.
374a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      suggestions[values::kKeybindingPlatformDefault] = suggested_key_string;
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
376a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      suggestions[values::kKeybindingPlatformDefault] = "";
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Normalize the suggestions.
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (SuggestionMap::iterator iter = suggestions.begin();
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != suggestions.end(); ++iter) {
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Before we normalize Ctrl to Command we must detect when the developer
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // specified Command in the Default section, which will work on Mac after
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // normalization but only fail on other platforms when they try it out on
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // other platforms, which is not what we want.
387a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    if (iter->first == values::kKeybindingPlatformDefault &&
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        iter->second.find("Command+") != std::string::npos) {
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      *error = ErrorUtils::FormatErrorMessageUTF16(
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errors::kInvalidKeyBinding,
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::IntToString(index),
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          keys::kSuggestedKey,
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          kCommandKeyNotSupported);
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suggestions[iter->first] = NormalizeShortcutSuggestion(iter->second,
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           iter->first);
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string platform = CommandPlatform();
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string key = platform;
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (suggestions.find(key) == suggestions.end())
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    key = values::kKeybindingPlatformDefault;
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (suggestions.find(key) == suggestions.end()) {
4062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *error = ErrorUtils::FormatErrorMessageUTF16(
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBindingMissingPlatform,
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        keys::kSuggestedKey,
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform);
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;  // No platform specified and no fallback. Bail.
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For developer convenience, we parse all the suggestions (and complain about
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // errors for platforms other than the current one) but use only what we need.
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::map<const std::string, std::string>::const_iterator iter =
4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      suggestions.begin();
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for ( ; iter != suggestions.end(); ++iter) {
419c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ui::Accelerator accelerator;
420c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (!iter->second.empty()) {
421c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // Note that we pass iter->first to pretend we are on a platform we're not
422c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // on.
423c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      accelerator = ParseImpl(iter->second, iter->first, index, error);
424c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (accelerator.key_code() == ui::VKEY_UNKNOWN) {
425c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        *error = ErrorUtils::FormatErrorMessageUTF16(
426c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            errors::kInvalidKeyBinding,
427c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            base::IntToString(index),
428c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            iter->first,
429c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)            iter->second);
430c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return false;
431c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (iter->first == key) {
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This platform is our platform, so grab this key.
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      accelerator_ = accelerator;
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      command_name_ = command_name;
438c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      description_ = description;
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4447d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)base::DictionaryValue* Command::ToValue(const Extension* extension,
4457d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                        bool active) const {
4467d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  base::DictionaryValue* extension_data = new base::DictionaryValue();
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  string16 command_description;
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_name() == values::kBrowserActionCommandEvent ||
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      command_name() == values::kPageActionCommandEvent ||
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      command_name() == values::kScriptBadgeCommandEvent) {
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command_description =
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_GENERIC_ACTIVATE);
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command_description = description();
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("description", command_description);
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetBoolean("active", active);
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("keybinding", accelerator().GetShortcutText());
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("command_name", command_name());
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("extension_id", extension->id());
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return extension_data;
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace extensions
467