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"
126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/grit/generated_resources.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "extensions/common/error_utils.h"
14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "extensions/common/extension.h"
15d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "extensions/common/manifest_constants.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace extensions {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
20d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace errors = manifest_errors;
21d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace keys = manifest_keys;
22d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)namespace values = manifest_values;
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kMissing[] = "Missing";
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static const char kCommandKeyNotSupported[] =
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "Command key is not supported. Note: Ctrl means Command on Mac";
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)bool IsNamedCommand(const std::string& command_name) {
3258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return command_name != values::kPageActionCommandEvent &&
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)         command_name != values::kBrowserActionCommandEvent;
3458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
3558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
3658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)bool DoesRequireModifier(const std::string& accelerator) {
3758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return accelerator != values::kKeyMediaNextTrack &&
3858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)         accelerator != values::kKeyMediaPlayPause &&
3958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)         accelerator != values::kKeyMediaPrevTrack &&
4058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)         accelerator != values::kKeyMediaStop;
4158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
4258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ui::Accelerator ParseImpl(const std::string& accelerator,
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          const std::string& platform_key,
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          int index,
4658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                          bool should_parse_media_keys,
47a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                          base::string16* error) {
4858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  error->clear();
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (platform_key != values::kKeybindingPlatformWin &&
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformMac &&
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformChromeOs &&
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformLinux &&
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      platform_key != values::kKeybindingPlatformDefault) {
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *error = ErrorUtils::FormatErrorMessageUTF16(
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBindingUnknownPlatform,
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform_key);
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::Accelerator();
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> tokens;
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::SplitString(accelerator, '+', &tokens);
6358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (tokens.size() == 0 ||
6458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      (tokens.size() == 1 && DoesRequireModifier(accelerator)) ||
6558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      tokens.size() > 3) {
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *error = ErrorUtils::FormatErrorMessageUTF16(
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBinding,
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform_key,
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        accelerator);
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::Accelerator();
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Now, parse it into an accelerator.
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int modifiers = ui::EF_NONE;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::KeyboardCode key = ui::VKEY_UNKNOWN;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tokens.size(); i++) {
78a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    if (tokens[i] == values::kKeyCtrl) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modifiers |= ui::EF_CONTROL_DOWN;
80a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    } else if (tokens[i] == values::kKeyCommand) {
81a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      if (platform_key == values::kKeybindingPlatformMac) {
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Either the developer specified Command+foo in the manifest for Mac or
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // they specified Ctrl and it got normalized to Command (to get Ctrl on
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Mac the developer has to specify MacCtrl). Therefore we treat this
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // as Command.
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modifiers |= ui::EF_COMMAND_DOWN;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_MACOSX)
88a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (platform_key == values::kKeybindingPlatformDefault) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // If we see "Command+foo" in the Default section it can mean two
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // things, depending on the platform:
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // The developer specified "Ctrl+foo" for Default and it got normalized
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // on Mac to "Command+foo". This is fine. Treat it as Command.
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        modifiers |= ui::EF_COMMAND_DOWN;
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // No other platform supports Command.
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = ui::VKEY_UNKNOWN;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
10003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    } else if (tokens[i] == values::kKeySearch) {
10103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      // Search is a special modifier only on ChromeOS and maps to 'Command'.
10203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if (platform_key == values::kKeybindingPlatformChromeOs) {
10303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        modifiers |= ui::EF_COMMAND_DOWN;
10403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      } else {
10503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        // No other platform supports Search.
10603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        key = ui::VKEY_UNKNOWN;
10703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        break;
10803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      }
109a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    } else if (tokens[i] == values::kKeyAlt) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modifiers |= ui::EF_ALT_DOWN;
111a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    } else if (tokens[i] == values::kKeyShift) {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      modifiers |= ui::EF_SHIFT_DOWN;
113a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    } else if (tokens[i].size() == 1 ||  // A-Z, 0-9.
114a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyComma ||
115a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyPeriod ||
116a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyUp ||
117a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyDown ||
118a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyLeft ||
119a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyRight ||
120a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyIns ||
121a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyDel ||
122a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyHome ||
123a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyEnd ||
124a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyPgUp ||
125a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)               tokens[i] == values::kKeyPgDwn ||
12658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)               tokens[i] == values::kKeyTab ||
12758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)               tokens[i] == values::kKeyMediaNextTrack ||
12858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)               tokens[i] == values::kKeyMediaPlayPause ||
12958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)               tokens[i] == values::kKeyMediaPrevTrack ||
13058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)               tokens[i] == values::kKeyMediaStop) {
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (key != ui::VKEY_UNKNOWN) {
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Multiple key assignments.
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = ui::VKEY_UNKNOWN;
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
136a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
137a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      if (tokens[i] == values::kKeyComma) {
138a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_OEM_COMMA;
139a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyPeriod) {
140a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_OEM_PERIOD;
141a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyUp) {
142a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_UP;
143a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyDown) {
144a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_DOWN;
145a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyLeft) {
146a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_LEFT;
147a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyRight) {
148a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_RIGHT;
149a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyIns) {
150a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_INSERT;
151a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyDel) {
152a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_DELETE;
153a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyHome) {
154a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_HOME;
155a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyEnd) {
156a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_END;
157a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyPgUp) {
158a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_PRIOR;
159a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyPgDwn) {
160a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_NEXT;
161a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i] == values::kKeyTab) {
162a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        key = ui::VKEY_TAB;
16358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      } else if (tokens[i] == values::kKeyMediaNextTrack &&
16458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                 should_parse_media_keys) {
16558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        key = ui::VKEY_MEDIA_NEXT_TRACK;
16658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      } else if (tokens[i] == values::kKeyMediaPlayPause &&
16758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                 should_parse_media_keys) {
16858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        key = ui::VKEY_MEDIA_PLAY_PAUSE;
16958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      } else if (tokens[i] == values::kKeyMediaPrevTrack &&
17058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                 should_parse_media_keys) {
17158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        key = ui::VKEY_MEDIA_PREV_TRACK;
17258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      } else if (tokens[i] == values::kKeyMediaStop &&
17358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                 should_parse_media_keys) {
17458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        key = ui::VKEY_MEDIA_STOP;
175a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i].size() == 1 &&
176a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)                 tokens[i][0] >= 'A' && tokens[i][0] <= 'Z') {
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = static_cast<ui::KeyboardCode>(ui::VKEY_A + (tokens[i][0] - 'A'));
178a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      } else if (tokens[i].size() == 1 &&
179a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)                 tokens[i][0] >= '0' && tokens[i][0] <= '9') {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = static_cast<ui::KeyboardCode>(ui::VKEY_0 + (tokens[i][0] - '0'));
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        key = ui::VKEY_UNKNOWN;
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        break;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      *error = ErrorUtils::FormatErrorMessageUTF16(
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errors::kInvalidKeyBinding,
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::IntToString(index),
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          platform_key,
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          accelerator);
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ui::Accelerator();
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
19458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool command = (modifiers & ui::EF_COMMAND_DOWN) != 0;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool ctrl = (modifiers & ui::EF_CONTROL_DOWN) != 0;
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool alt = (modifiers & ui::EF_ALT_DOWN) != 0;
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool shift = (modifiers & ui::EF_SHIFT_DOWN) != 0;
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We support Ctrl+foo, Alt+foo, Ctrl+Shift+foo, Alt+Shift+foo, but not
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Ctrl+Alt+foo and not Shift+foo either. For a more detailed reason why we
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // don't support Ctrl+Alt+foo see this article:
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx.
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // On Mac Command can also be used in combination with Shift or on its own,
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // as a modifier.
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (key == ui::VKEY_UNKNOWN || (ctrl && alt) || (command && alt) ||
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      (shift && !ctrl && !alt && !command)) {
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *error = ErrorUtils::FormatErrorMessageUTF16(
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBinding,
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform_key,
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        accelerator);
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ui::Accelerator();
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
21658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if ((key == ui::VKEY_MEDIA_NEXT_TRACK ||
21758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)       key == ui::VKEY_MEDIA_PREV_TRACK ||
21858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)       key == ui::VKEY_MEDIA_PLAY_PAUSE ||
21958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)       key == ui::VKEY_MEDIA_STOP) &&
22058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      (shift || ctrl || alt || command)) {
22158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    *error = ErrorUtils::FormatErrorMessageUTF16(
22258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        errors::kInvalidKeyBindingMediaKeyWithModifier,
22358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        base::IntToString(index),
22458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        platform_key,
22558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        accelerator);
22658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return ui::Accelerator();
22758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
22858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ui::Accelerator(key, modifiers);
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For Mac, we convert "Ctrl" to "Command" and "MacCtrl" to "Ctrl". Other
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// platforms leave the shortcut untouched.
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string NormalizeShortcutSuggestion(const std::string& suggestion,
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        const std::string& platform) {
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool normalize = false;
237a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (platform == values::kKeybindingPlatformMac) {
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    normalize = true;
239a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  } else if (platform == values::kKeybindingPlatformDefault) {
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_MACOSX)
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    normalize = true;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!normalize)
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return suggestion;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<std::string> tokens;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::SplitString(suggestion, '+', &tokens);
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < tokens.size(); i++) {
251a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    if (tokens[i] == values::kKeyCtrl)
252a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      tokens[i] = values::kKeyCommand;
253a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    else if (tokens[i] == values::kKeyMacCtrl)
254a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      tokens[i] = values::kKeyCtrl;
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return JoinString(tokens, '+');
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)Command::Command() : global_(false) {}
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command::Command(const std::string& command_name,
264a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 const base::string16& description,
2654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                 const std::string& accelerator,
2664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                 bool global)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : command_name_(command_name),
2684e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      description_(description),
2694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      global_(global) {
270a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 error;
27158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  accelerator_ = ParseImpl(accelerator, CommandPlatform(), 0,
27258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                           IsNamedCommand(command_name), &error);
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Command::~Command() {}
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string Command::CommandPlatform() {
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN)
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformWin;
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_MACOSX)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformMac;
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_CHROMEOS)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformChromeOs;
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif defined(OS_LINUX)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return values::kKeybindingPlatformLinux;
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return "";
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
29358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)ui::Accelerator Command::StringToAccelerator(const std::string& accelerator,
29458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                                             const std::string& command_name) {
295a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 error;
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ui::Accelerator parsed =
29758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      ParseImpl(accelerator, Command::CommandPlatform(), 0,
29858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                IsNamedCommand(command_name), &error);
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return parsed;
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
302a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)// static
303a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)std::string Command::AcceleratorToString(const ui::Accelerator& accelerator) {
304a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  std::string shortcut;
305a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
306a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  // Ctrl and Alt are mutually exclusive.
307a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (accelerator.IsCtrlDown())
308a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeyCtrl;
309a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  else if (accelerator.IsAltDown())
310a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeyAlt;
311a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (!shortcut.empty())
312a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeySeparator;
313a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
314a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (accelerator.IsCmdDown()) {
31503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#if defined(OS_CHROMEOS)
31603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    // Chrome OS treats the Search key like the Command key.
31703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    shortcut += values::kKeySearch;
31803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#else
319a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeyCommand;
32003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#endif
321a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeySeparator;
322a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  }
323a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
324a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (accelerator.IsShiftDown()) {
325a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeyShift;
326a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += values::kKeySeparator;
327a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  }
328a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
329a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  if (accelerator.key_code() >= ui::VKEY_0 &&
330a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      accelerator.key_code() <= ui::VKEY_9) {
331a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += '0' + (accelerator.key_code() - ui::VKEY_0);
332a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  } else if (accelerator.key_code() >= ui::VKEY_A &&
333a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)           accelerator.key_code() <= ui::VKEY_Z) {
334a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    shortcut += 'A' + (accelerator.key_code() - ui::VKEY_A);
335a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  } else {
336a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    switch (accelerator.key_code()) {
337a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_OEM_COMMA:
338a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyComma;
339a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
340a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_OEM_PERIOD:
341a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyPeriod;
342a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
343a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_UP:
344a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyUp;
345a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
346a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_DOWN:
347a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyDown;
348a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
349a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_LEFT:
350a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyLeft;
351a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
352a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_RIGHT:
353a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyRight;
354a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
355a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_INSERT:
356a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyIns;
357a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
358a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_DELETE:
359a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyDel;
360a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
361a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_HOME:
362a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyHome;
363a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
364a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_END:
365a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyEnd;
366a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
367a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_PRIOR:
368a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyPgUp;
369a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
370a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_NEXT:
371a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyPgDwn;
372a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
373a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      case ui::VKEY_TAB:
374a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        shortcut += values::kKeyTab;
375a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        break;
37658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      case ui::VKEY_MEDIA_NEXT_TRACK:
37758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        shortcut += values::kKeyMediaNextTrack;
37858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        break;
37958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      case ui::VKEY_MEDIA_PLAY_PAUSE:
38058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        shortcut += values::kKeyMediaPlayPause;
38158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        break;
38258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      case ui::VKEY_MEDIA_PREV_TRACK:
38358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        shortcut += values::kKeyMediaPrevTrack;
38458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        break;
38558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      case ui::VKEY_MEDIA_STOP:
38658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        shortcut += values::kKeyMediaStop;
38758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        break;
388a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      default:
389a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)        return "";
390a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    }
391a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  }
392a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  return shortcut;
393a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)}
394a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
395a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// static
396a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool Command::IsMediaKey(const ui::Accelerator& accelerator) {
397a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (accelerator.modifiers() != 0)
398a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
399a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
400a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return (accelerator.key_code() == ui::VKEY_MEDIA_NEXT_TRACK ||
401a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          accelerator.key_code() == ui::VKEY_MEDIA_PREV_TRACK ||
402a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          accelerator.key_code() == ui::VKEY_MEDIA_PLAY_PAUSE ||
403a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          accelerator.key_code() == ui::VKEY_MEDIA_STOP);
404a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
405a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
4067d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)bool Command::Parse(const base::DictionaryValue* command,
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const std::string& command_name,
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    int index,
409a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                    base::string16* error) {
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!command_name.empty());
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
412a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 description;
41358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (IsNamedCommand(command_name)) {
414c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (!command->GetString(keys::kDescription, &description) ||
415c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        description.empty()) {
416c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      *error = ErrorUtils::FormatErrorMessageUTF16(
417c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          errors::kInvalidKeyBindingDescription,
418c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          base::IntToString(index));
419c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return false;
420c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
421c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
422c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We'll build up a map of platform-to-shortcut suggestions.
4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  typedef std::map<const std::string, std::string> SuggestionMap;
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SuggestionMap suggestions;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // First try to parse the |suggested_key| as a dictionary.
4287d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  const base::DictionaryValue* suggested_key_dict;
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command->GetDictionary(keys::kSuggestedKey, &suggested_key_dict)) {
4307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)    for (base::DictionaryValue::Iterator iter(*suggested_key_dict);
4317d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)         !iter.IsAtEnd(); iter.Advance()) {
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // For each item in the dictionary, extract the platforms specified.
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string suggested_key_string;
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (iter.value().GetAsString(&suggested_key_string) &&
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          !suggested_key_string.empty()) {
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Found a platform, add it to the suggestions list.
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        suggestions[iter.key()] = suggested_key_string;
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        *error = ErrorUtils::FormatErrorMessageUTF16(
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            errors::kInvalidKeyBinding,
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            base::IntToString(index),
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            keys::kSuggestedKey,
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            kMissing);
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return false;
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // No dictionary was found, fall back to using just a string, so developers
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // don't have to specify a dictionary if they just want to use one default
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for all platforms.
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string suggested_key_string;
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (command->GetString(keys::kSuggestedKey, &suggested_key_string) &&
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        !suggested_key_string.empty()) {
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // If only a single string is provided, it must be default for all.
455a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      suggestions[values::kKeybindingPlatformDefault] = suggested_key_string;
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
457a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      suggestions[values::kKeybindingPlatformDefault] = "";
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4614e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Check if this is a global or a regular shortcut.
4624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  bool global = false;
463116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  command->GetBoolean(keys::kGlobal, &global);
4644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Normalize the suggestions.
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (SuggestionMap::iterator iter = suggestions.begin();
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       iter != suggestions.end(); ++iter) {
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Before we normalize Ctrl to Command we must detect when the developer
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // specified Command in the Default section, which will work on Mac after
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // normalization but only fail on other platforms when they try it out on
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // other platforms, which is not what we want.
472a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    if (iter->first == values::kKeybindingPlatformDefault &&
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        iter->second.find("Command+") != std::string::npos) {
4742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      *error = ErrorUtils::FormatErrorMessageUTF16(
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          errors::kInvalidKeyBinding,
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          base::IntToString(index),
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          keys::kSuggestedKey,
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          kCommandKeyNotSupported);
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return false;
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suggestions[iter->first] = NormalizeShortcutSuggestion(iter->second,
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           iter->first);
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string platform = CommandPlatform();
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string key = platform;
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (suggestions.find(key) == suggestions.end())
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    key = values::kKeybindingPlatformDefault;
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (suggestions.find(key) == suggestions.end()) {
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    *error = ErrorUtils::FormatErrorMessageUTF16(
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        errors::kInvalidKeyBindingMissingPlatform,
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString(index),
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        keys::kSuggestedKey,
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        platform);
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;  // No platform specified and no fallback. Bail.
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For developer convenience, we parse all the suggestions (and complain about
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // errors for platforms other than the current one) but use only what we need.
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::map<const std::string, std::string>::const_iterator iter =
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      suggestions.begin();
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for ( ; iter != suggestions.end(); ++iter) {
504c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ui::Accelerator accelerator;
505c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (!iter->second.empty()) {
506c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // Note that we pass iter->first to pretend we are on a platform we're not
507c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      // on.
50858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      accelerator = ParseImpl(iter->second, iter->first, index,
50958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)                              IsNamedCommand(command_name), error);
510c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      if (accelerator.key_code() == ui::VKEY_UNKNOWN) {
51158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        if (error->empty()) {
51258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)          *error = ErrorUtils::FormatErrorMessageUTF16(
51358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)              errors::kInvalidKeyBinding,
51458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)              base::IntToString(index),
51558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)              iter->first,
51658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)              iter->second);
51758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        }
518c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return false;
519c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      }
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (iter->first == key) {
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This platform is our platform, so grab this key.
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      accelerator_ = accelerator;
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      command_name_ = command_name;
526c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      description_ = description;
5274e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      global_ = global;
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5337d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)base::DictionaryValue* Command::ToValue(const Extension* extension,
5347d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)                                        bool active) const {
5357d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  base::DictionaryValue* extension_data = new base::DictionaryValue();
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
537a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::string16 command_description;
5388bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  bool extension_action = false;
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (command_name() == values::kBrowserActionCommandEvent ||
5405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      command_name() == values::kPageActionCommandEvent) {
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command_description =
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_GENERIC_ACTIVATE);
5438bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    extension_action = true;
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    command_description = description();
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("description", command_description);
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetBoolean("active", active);
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("keybinding", accelerator().GetShortcutText());
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("command_name", command_name());
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  extension_data->SetString("extension_id", extension->id());
5528bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  extension_data->SetBoolean("global", global());
5538bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  extension_data->SetBoolean("extension_action", extension_action);
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return extension_data;
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace extensions
558