OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/common/extensions/extension_commands.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/string_number_conversions.h" | |
9 #include "base/string_split.h" | |
10 #include "base/values.h" | |
11 #include "chrome/common/extensions/extension_error_utils.h" | |
12 #include "chrome/common/extensions/extension_manifest_constants.h" | |
13 #include "grit/generated_resources.h" | |
14 #include "ui/base/l10n/l10n_util.h" | |
15 | |
16 namespace errors = extension_manifest_errors; | |
17 namespace keys = extension_manifest_keys; | |
18 namespace values = extension_manifest_values; | |
19 | |
20 namespace extensions { | |
21 | |
22 Command::Command() {} | |
23 | |
24 Command::~Command() {} | |
25 | |
26 ui::Accelerator Command::ParseImpl( | |
27 const std::string& shortcut, | |
28 const std::string& platform_key, | |
29 int index, | |
30 string16* error) { | |
31 if (platform_key != values::kKeybindingPlatformWin && | |
32 platform_key != values::kKeybindingPlatformMac && | |
33 platform_key != values::kKeybindingPlatformChromeOs && | |
34 platform_key != values::kKeybindingPlatformLinux && | |
35 platform_key != values::kKeybindingPlatformDefault) { | |
36 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
37 errors::kInvalidKeyBindingUnknownPlatform, | |
38 base::IntToString(index), | |
39 platform_key); | |
40 return ui::Accelerator(); | |
41 } | |
42 | |
43 std::vector<std::string> tokens; | |
44 base::SplitString(shortcut, '+', &tokens); | |
45 if (tokens.size() < 2 || tokens.size() > 3) { | |
46 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
47 errors::kInvalidKeyBinding, | |
48 base::IntToString(index), | |
49 platform_key, | |
50 shortcut); | |
51 return ui::Accelerator(); | |
52 } | |
53 | |
54 // Now, parse it into an accelerator. | |
55 int modifiers = ui::EF_NONE; | |
56 ui::KeyboardCode key = ui::VKEY_UNKNOWN; | |
57 for (size_t i = 0; i < tokens.size(); i++) { | |
58 if (tokens[i] == "Ctrl") { | |
59 modifiers |= ui::EF_CONTROL_DOWN; | |
60 } else if (tokens[i] == "Alt") { | |
61 modifiers |= ui::EF_ALT_DOWN; | |
62 } else if (tokens[i] == "Shift") { | |
63 modifiers |= ui::EF_SHIFT_DOWN; | |
64 } else if (tokens[i] == "Command" && platform_key == "mac") { | |
65 // TODO(finnur): Implement for Mac. | |
66 } else if (tokens[i] == "Option" && platform_key == "mac") { | |
67 // TODO(finnur): Implement for Mac. | |
68 } else if (tokens[i].size() == 1 && | |
69 tokens[i][0] >= 'A' && tokens[i][0] <= 'Z') { | |
70 if (key != ui::VKEY_UNKNOWN) { | |
71 // Multiple key assignments. | |
72 key = ui::VKEY_UNKNOWN; | |
73 break; | |
74 } | |
75 | |
76 key = static_cast<ui::KeyboardCode>(ui::VKEY_A + (tokens[i][0] - 'A')); | |
77 } else { | |
78 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
79 errors::kInvalidKeyBinding, | |
80 base::IntToString(index), | |
81 platform_key, | |
82 shortcut); | |
83 return ui::Accelerator(); | |
84 } | |
85 } | |
86 bool ctrl = (modifiers & ui::EF_CONTROL_DOWN) != 0; | |
87 bool alt = (modifiers & ui::EF_ALT_DOWN) != 0; | |
88 // We support Ctrl+foo, Alt+foo, Ctrl+Shift+foo, Alt+Shift+foo, but not | |
89 // Ctrl+Alt+foo. For a more detailed reason why we don't support Ctrl+Alt+foo: | |
90 // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx. | |
91 if (key == ui::VKEY_UNKNOWN || (ctrl && alt)) { | |
92 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
93 errors::kInvalidKeyBinding, | |
94 base::IntToString(index), | |
95 platform_key, | |
96 shortcut); | |
97 return ui::Accelerator(); | |
98 } | |
99 | |
100 return ui::Accelerator(key, modifiers); | |
101 } | |
102 | |
103 // static | |
104 std::string Command::CommandPlatform() { | |
105 #if defined(OS_WIN) | |
106 return values::kKeybindingPlatformWin; | |
107 #elif defined(OS_MACOSX) | |
108 return values::kKeybindingPlatformMac; | |
109 #elif defined(OS_CHROMEOS) | |
110 return values::kKeybindingPlatformChromeOs; | |
111 #elif defined(OS_LINUX) | |
112 return values::kKeybindingPlatformLinux; | |
113 #else | |
114 return ""; | |
115 #endif | |
116 } | |
117 | |
118 bool Command::Parse(DictionaryValue* command, | |
119 const std::string& command_name, | |
120 int index, | |
121 string16* error) { | |
122 DCHECK(!command_name.empty()); | |
123 | |
124 // We'll build up a map of platform-to-shortcut suggestions. | |
125 std::map<const std::string, std::string> suggestions; | |
126 | |
127 // First try to parse the |suggested_key| as a dictionary. | |
128 DictionaryValue* suggested_key_dict; | |
129 if (command->GetDictionary(keys::kSuggestedKey, &suggested_key_dict)) { | |
130 DictionaryValue::key_iterator iter = suggested_key_dict->begin_keys(); | |
131 for ( ; iter != suggested_key_dict->end_keys(); ++iter) { | |
132 // For each item in the dictionary, extract the platforms specified. | |
133 std::string suggested_key_string; | |
134 if (suggested_key_dict->GetString(*iter, &suggested_key_string) && | |
135 !suggested_key_string.empty()) { | |
136 // Found a platform, add it to the suggestions list. | |
137 suggestions[*iter] = suggested_key_string; | |
138 } else { | |
139 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
140 errors::kInvalidKeyBinding, | |
141 base::IntToString(index), | |
142 keys::kSuggestedKey, | |
143 "Missing"); | |
144 return false; | |
145 } | |
146 } | |
147 } else { | |
148 // No dictionary was found, fall back to using just a string, so developers | |
149 // don't have to specify a dictionary if they just want to use one default | |
150 // for all platforms. | |
151 std::string suggested_key_string; | |
152 if (command->GetString(keys::kSuggestedKey, &suggested_key_string) && | |
153 !suggested_key_string.empty()) { | |
154 // If only a signle string is provided, it must be default for all. | |
155 suggestions["default"] = suggested_key_string; | |
156 } else { | |
157 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
158 errors::kInvalidKeyBinding, | |
159 base::IntToString(index), | |
160 keys::kSuggestedKey, | |
161 "Missing"); | |
162 return false; | |
163 } | |
164 } | |
165 | |
166 std::string platform = CommandPlatform(); | |
167 std::string key = platform; | |
168 if (suggestions.find(key) == suggestions.end()) | |
169 key = values::kKeybindingPlatformDefault; | |
170 if (suggestions.find(key) == suggestions.end()) { | |
171 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
172 errors::kInvalidKeyBindingMissingPlatform, | |
173 base::IntToString(index), | |
174 keys::kSuggestedKey, | |
175 platform); | |
176 return false; // No platform specified and no fallback. Bail. | |
177 } | |
178 | |
179 // For developer convenience, we parse all the suggestions (and complain about | |
180 // errors for platforms other than the current one) but use only what we need. | |
181 std::map<const std::string, std::string>::const_iterator iter = | |
182 suggestions.begin(); | |
183 for ( ; iter != suggestions.end(); ++iter) { | |
184 // Note that we pass iter->first to pretend we are on a platform we're not | |
185 // on. | |
186 ui::Accelerator accelerator = | |
187 ParseImpl(iter->second, iter->first, index, error); | |
188 if (accelerator.key_code() == ui::VKEY_UNKNOWN) { | |
189 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
190 errors::kInvalidKeyBinding, | |
191 base::IntToString(index), | |
192 iter->first, | |
193 iter->second); | |
194 return false; | |
195 } | |
196 | |
197 if (iter->first == key) { | |
198 // This platform is our platform, so grab this key. | |
199 accelerator_ = accelerator; | |
200 command_name_ = command_name; | |
201 | |
202 if (command_name != | |
203 extension_manifest_values::kPageActionKeybindingEvent && | |
204 command_name != | |
205 extension_manifest_values::kBrowserActionKeybindingEvent) { | |
206 if (!command->GetString(keys::kDescription, &description_) || | |
207 description_.empty()) { | |
208 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( | |
209 errors::kInvalidKeyBindingDescription, | |
210 base::IntToString(index)); | |
211 return false; | |
212 } | |
213 } | |
214 } | |
215 } | |
216 return true; | |
217 } | |
218 | |
219 DictionaryValue* Command::ToValue(const Extension* extension, | |
220 bool active) const { | |
221 DictionaryValue* extension_data = new DictionaryValue(); | |
222 | |
223 string16 command_description; | |
224 if (command_name() == values::kBrowserActionKeybindingEvent || | |
225 command_name() == values::kPageActionKeybindingEvent) { | |
226 command_description = | |
227 l10n_util::GetStringUTF16(IDS_EXTENSION_COMMANDS_GENERIC_ACTIVATE); | |
228 } else { | |
229 command_description = description(); | |
230 } | |
231 extension_data->SetString("description", command_description); | |
232 extension_data->SetBoolean("active", active); | |
233 extension_data->SetString("keybinding", accelerator().GetShortcutText()); | |
234 | |
235 return extension_data; | |
236 } | |
237 | |
238 } // namespace extensions | |
OLD | NEW |