OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/common/extensions/command.h" | 5 #include "chrome/common/extensions/command.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/strings/string_number_conversions.h" | 8 #include "base/strings/string_number_conversions.h" |
9 #include "base/strings/string_split.h" | 9 #include "base/strings/string_split.h" |
10 #include "base/strings/string_util.h" | 10 #include "base/strings/string_util.h" |
(...skipping 11 matching lines...) Expand all Loading... |
22 using extensions::ErrorUtils; | 22 using extensions::ErrorUtils; |
23 using extensions::Command; | 23 using extensions::Command; |
24 | 24 |
25 namespace { | 25 namespace { |
26 | 26 |
27 static const char kMissing[] = "Missing"; | 27 static const char kMissing[] = "Missing"; |
28 | 28 |
29 static const char kCommandKeyNotSupported[] = | 29 static const char kCommandKeyNotSupported[] = |
30 "Command key is not supported. Note: Ctrl means Command on Mac"; | 30 "Command key is not supported. Note: Ctrl means Command on Mac"; |
31 | 31 |
| 32 bool IsNamedCommand(const std::string& command_name) { |
| 33 return command_name != values::kPageActionCommandEvent && |
| 34 command_name != values::kBrowserActionCommandEvent && |
| 35 command_name != values::kScriptBadgeCommandEvent; |
| 36 } |
| 37 |
| 38 bool DoesRequireModifier(const std::string& accelerator) { |
| 39 return accelerator != values::kKeyMediaNextTrack && |
| 40 accelerator != values::kKeyMediaPlayPause && |
| 41 accelerator != values::kKeyMediaPrevTrack && |
| 42 accelerator != values::kKeyMediaStop; |
| 43 } |
| 44 |
32 ui::Accelerator ParseImpl(const std::string& accelerator, | 45 ui::Accelerator ParseImpl(const std::string& accelerator, |
33 const std::string& platform_key, | 46 const std::string& platform_key, |
34 int index, | 47 int index, |
| 48 bool should_parse_media_keys, |
35 string16* error) { | 49 string16* error) { |
| 50 error->clear(); |
36 if (platform_key != values::kKeybindingPlatformWin && | 51 if (platform_key != values::kKeybindingPlatformWin && |
37 platform_key != values::kKeybindingPlatformMac && | 52 platform_key != values::kKeybindingPlatformMac && |
38 platform_key != values::kKeybindingPlatformChromeOs && | 53 platform_key != values::kKeybindingPlatformChromeOs && |
39 platform_key != values::kKeybindingPlatformLinux && | 54 platform_key != values::kKeybindingPlatformLinux && |
40 platform_key != values::kKeybindingPlatformDefault) { | 55 platform_key != values::kKeybindingPlatformDefault) { |
41 *error = ErrorUtils::FormatErrorMessageUTF16( | 56 *error = ErrorUtils::FormatErrorMessageUTF16( |
42 errors::kInvalidKeyBindingUnknownPlatform, | 57 errors::kInvalidKeyBindingUnknownPlatform, |
43 base::IntToString(index), | 58 base::IntToString(index), |
44 platform_key); | 59 platform_key); |
45 return ui::Accelerator(); | 60 return ui::Accelerator(); |
46 } | 61 } |
47 | 62 |
48 std::vector<std::string> tokens; | 63 std::vector<std::string> tokens; |
49 base::SplitString(accelerator, '+', &tokens); | 64 base::SplitString(accelerator, '+', &tokens); |
50 if (tokens.size() < 2 || tokens.size() > 3) { | 65 if (tokens.size() == 0 || |
| 66 (tokens.size() == 1 && DoesRequireModifier(accelerator)) || |
| 67 tokens.size() > 3) { |
51 *error = ErrorUtils::FormatErrorMessageUTF16( | 68 *error = ErrorUtils::FormatErrorMessageUTF16( |
52 errors::kInvalidKeyBinding, | 69 errors::kInvalidKeyBinding, |
53 base::IntToString(index), | 70 base::IntToString(index), |
54 platform_key, | 71 platform_key, |
55 accelerator); | 72 accelerator); |
56 return ui::Accelerator(); | 73 return ui::Accelerator(); |
57 } | 74 } |
58 | 75 |
59 // Now, parse it into an accelerator. | 76 // Now, parse it into an accelerator. |
60 int modifiers = ui::EF_NONE; | 77 int modifiers = ui::EF_NONE; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 tokens[i] == values::kKeyUp || | 109 tokens[i] == values::kKeyUp || |
93 tokens[i] == values::kKeyDown || | 110 tokens[i] == values::kKeyDown || |
94 tokens[i] == values::kKeyLeft || | 111 tokens[i] == values::kKeyLeft || |
95 tokens[i] == values::kKeyRight || | 112 tokens[i] == values::kKeyRight || |
96 tokens[i] == values::kKeyIns || | 113 tokens[i] == values::kKeyIns || |
97 tokens[i] == values::kKeyDel || | 114 tokens[i] == values::kKeyDel || |
98 tokens[i] == values::kKeyHome || | 115 tokens[i] == values::kKeyHome || |
99 tokens[i] == values::kKeyEnd || | 116 tokens[i] == values::kKeyEnd || |
100 tokens[i] == values::kKeyPgUp || | 117 tokens[i] == values::kKeyPgUp || |
101 tokens[i] == values::kKeyPgDwn || | 118 tokens[i] == values::kKeyPgDwn || |
102 tokens[i] == values::kKeyTab) { | 119 tokens[i] == values::kKeyTab || |
| 120 tokens[i] == values::kKeyMediaNextTrack || |
| 121 tokens[i] == values::kKeyMediaPlayPause || |
| 122 tokens[i] == values::kKeyMediaPrevTrack || |
| 123 tokens[i] == values::kKeyMediaStop) { |
103 if (key != ui::VKEY_UNKNOWN) { | 124 if (key != ui::VKEY_UNKNOWN) { |
104 // Multiple key assignments. | 125 // Multiple key assignments. |
105 key = ui::VKEY_UNKNOWN; | 126 key = ui::VKEY_UNKNOWN; |
106 break; | 127 break; |
107 } | 128 } |
108 | 129 |
109 if (tokens[i] == values::kKeyComma) { | 130 if (tokens[i] == values::kKeyComma) { |
110 key = ui::VKEY_OEM_COMMA; | 131 key = ui::VKEY_OEM_COMMA; |
111 } else if (tokens[i] == values::kKeyPeriod) { | 132 } else if (tokens[i] == values::kKeyPeriod) { |
112 key = ui::VKEY_OEM_PERIOD; | 133 key = ui::VKEY_OEM_PERIOD; |
(...skipping 12 matching lines...) Expand all Loading... |
125 } else if (tokens[i] == values::kKeyHome) { | 146 } else if (tokens[i] == values::kKeyHome) { |
126 key = ui::VKEY_HOME; | 147 key = ui::VKEY_HOME; |
127 } else if (tokens[i] == values::kKeyEnd) { | 148 } else if (tokens[i] == values::kKeyEnd) { |
128 key = ui::VKEY_END; | 149 key = ui::VKEY_END; |
129 } else if (tokens[i] == values::kKeyPgUp) { | 150 } else if (tokens[i] == values::kKeyPgUp) { |
130 key = ui::VKEY_PRIOR; | 151 key = ui::VKEY_PRIOR; |
131 } else if (tokens[i] == values::kKeyPgDwn) { | 152 } else if (tokens[i] == values::kKeyPgDwn) { |
132 key = ui::VKEY_NEXT; | 153 key = ui::VKEY_NEXT; |
133 } else if (tokens[i] == values::kKeyTab) { | 154 } else if (tokens[i] == values::kKeyTab) { |
134 key = ui::VKEY_TAB; | 155 key = ui::VKEY_TAB; |
| 156 } else if (tokens[i] == values::kKeyMediaNextTrack && |
| 157 should_parse_media_keys) { |
| 158 key = ui::VKEY_MEDIA_NEXT_TRACK; |
| 159 } else if (tokens[i] == values::kKeyMediaPlayPause && |
| 160 should_parse_media_keys) { |
| 161 key = ui::VKEY_MEDIA_PLAY_PAUSE; |
| 162 } else if (tokens[i] == values::kKeyMediaPrevTrack && |
| 163 should_parse_media_keys) { |
| 164 key = ui::VKEY_MEDIA_PREV_TRACK; |
| 165 } else if (tokens[i] == values::kKeyMediaStop && |
| 166 should_parse_media_keys) { |
| 167 key = ui::VKEY_MEDIA_STOP; |
135 } else if (tokens[i].size() == 1 && | 168 } else if (tokens[i].size() == 1 && |
136 tokens[i][0] >= 'A' && tokens[i][0] <= 'Z') { | 169 tokens[i][0] >= 'A' && tokens[i][0] <= 'Z') { |
137 key = static_cast<ui::KeyboardCode>(ui::VKEY_A + (tokens[i][0] - 'A')); | 170 key = static_cast<ui::KeyboardCode>(ui::VKEY_A + (tokens[i][0] - 'A')); |
138 } else if (tokens[i].size() == 1 && | 171 } else if (tokens[i].size() == 1 && |
139 tokens[i][0] >= '0' && tokens[i][0] <= '9') { | 172 tokens[i][0] >= '0' && tokens[i][0] <= '9') { |
140 key = static_cast<ui::KeyboardCode>(ui::VKEY_0 + (tokens[i][0] - '0')); | 173 key = static_cast<ui::KeyboardCode>(ui::VKEY_0 + (tokens[i][0] - '0')); |
141 } else { | 174 } else { |
142 key = ui::VKEY_UNKNOWN; | 175 key = ui::VKEY_UNKNOWN; |
143 break; | 176 break; |
144 } | 177 } |
145 } else { | 178 } else { |
146 *error = ErrorUtils::FormatErrorMessageUTF16( | 179 *error = ErrorUtils::FormatErrorMessageUTF16( |
147 errors::kInvalidKeyBinding, | 180 errors::kInvalidKeyBinding, |
148 base::IntToString(index), | 181 base::IntToString(index), |
149 platform_key, | 182 platform_key, |
150 accelerator); | 183 accelerator); |
151 return ui::Accelerator(); | 184 return ui::Accelerator(); |
152 } | 185 } |
153 } | 186 } |
| 187 |
154 bool command = (modifiers & ui::EF_COMMAND_DOWN) != 0; | 188 bool command = (modifiers & ui::EF_COMMAND_DOWN) != 0; |
155 bool ctrl = (modifiers & ui::EF_CONTROL_DOWN) != 0; | 189 bool ctrl = (modifiers & ui::EF_CONTROL_DOWN) != 0; |
156 bool alt = (modifiers & ui::EF_ALT_DOWN) != 0; | 190 bool alt = (modifiers & ui::EF_ALT_DOWN) != 0; |
157 bool shift = (modifiers & ui::EF_SHIFT_DOWN) != 0; | 191 bool shift = (modifiers & ui::EF_SHIFT_DOWN) != 0; |
158 | 192 |
159 // We support Ctrl+foo, Alt+foo, Ctrl+Shift+foo, Alt+Shift+foo, but not | 193 // We support Ctrl+foo, Alt+foo, Ctrl+Shift+foo, Alt+Shift+foo, but not |
160 // Ctrl+Alt+foo and not Shift+foo either. For a more detailed reason why we | 194 // Ctrl+Alt+foo and not Shift+foo either. For a more detailed reason why we |
161 // don't support Ctrl+Alt+foo see this article: | 195 // don't support Ctrl+Alt+foo see this article: |
162 // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx. | 196 // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx. |
163 // On Mac Command can also be used in combination with Shift or on its own, | 197 // On Mac Command can also be used in combination with Shift or on its own, |
164 // as a modifier. | 198 // as a modifier. |
165 if (key == ui::VKEY_UNKNOWN || (ctrl && alt) || (command && alt) || | 199 if (key == ui::VKEY_UNKNOWN || (ctrl && alt) || (command && alt) || |
166 (shift && !ctrl && !alt && !command)) { | 200 (shift && !ctrl && !alt && !command)) { |
167 *error = ErrorUtils::FormatErrorMessageUTF16( | 201 *error = ErrorUtils::FormatErrorMessageUTF16( |
168 errors::kInvalidKeyBinding, | 202 errors::kInvalidKeyBinding, |
169 base::IntToString(index), | 203 base::IntToString(index), |
170 platform_key, | 204 platform_key, |
171 accelerator); | 205 accelerator); |
172 return ui::Accelerator(); | 206 return ui::Accelerator(); |
173 } | 207 } |
174 | 208 |
| 209 if ((key == ui::VKEY_MEDIA_NEXT_TRACK || |
| 210 key == ui::VKEY_MEDIA_PREV_TRACK || |
| 211 key == ui::VKEY_MEDIA_PLAY_PAUSE || |
| 212 key == ui::VKEY_MEDIA_STOP) && |
| 213 (shift || ctrl || alt || command)) { |
| 214 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 215 errors::kInvalidKeyBindingMediaKeyWithModifier, |
| 216 base::IntToString(index), |
| 217 platform_key, |
| 218 accelerator); |
| 219 return ui::Accelerator(); |
| 220 } |
| 221 |
175 return ui::Accelerator(key, modifiers); | 222 return ui::Accelerator(key, modifiers); |
176 } | 223 } |
177 | 224 |
178 // For Mac, we convert "Ctrl" to "Command" and "MacCtrl" to "Ctrl". Other | 225 // For Mac, we convert "Ctrl" to "Command" and "MacCtrl" to "Ctrl". Other |
179 // platforms leave the shortcut untouched. | 226 // platforms leave the shortcut untouched. |
180 std::string NormalizeShortcutSuggestion(const std::string& suggestion, | 227 std::string NormalizeShortcutSuggestion(const std::string& suggestion, |
181 const std::string& platform) { | 228 const std::string& platform) { |
182 bool normalize = false; | 229 bool normalize = false; |
183 if (platform == values::kKeybindingPlatformMac) { | 230 if (platform == values::kKeybindingPlatformMac) { |
184 normalize = true; | 231 normalize = true; |
(...skipping 22 matching lines...) Expand all Loading... |
207 namespace extensions { | 254 namespace extensions { |
208 | 255 |
209 Command::Command() {} | 256 Command::Command() {} |
210 | 257 |
211 Command::Command(const std::string& command_name, | 258 Command::Command(const std::string& command_name, |
212 const string16& description, | 259 const string16& description, |
213 const std::string& accelerator) | 260 const std::string& accelerator) |
214 : command_name_(command_name), | 261 : command_name_(command_name), |
215 description_(description) { | 262 description_(description) { |
216 string16 error; | 263 string16 error; |
217 accelerator_ = ParseImpl(accelerator, CommandPlatform(), 0, &error); | 264 accelerator_ = ParseImpl(accelerator, CommandPlatform(), 0, |
| 265 IsNamedCommand(command_name), &error); |
218 } | 266 } |
219 | 267 |
220 Command::~Command() {} | 268 Command::~Command() {} |
221 | 269 |
222 // static | 270 // static |
223 std::string Command::CommandPlatform() { | 271 std::string Command::CommandPlatform() { |
224 #if defined(OS_WIN) | 272 #if defined(OS_WIN) |
225 return values::kKeybindingPlatformWin; | 273 return values::kKeybindingPlatformWin; |
226 #elif defined(OS_MACOSX) | 274 #elif defined(OS_MACOSX) |
227 return values::kKeybindingPlatformMac; | 275 return values::kKeybindingPlatformMac; |
228 #elif defined(OS_CHROMEOS) | 276 #elif defined(OS_CHROMEOS) |
229 return values::kKeybindingPlatformChromeOs; | 277 return values::kKeybindingPlatformChromeOs; |
230 #elif defined(OS_LINUX) | 278 #elif defined(OS_LINUX) |
231 return values::kKeybindingPlatformLinux; | 279 return values::kKeybindingPlatformLinux; |
232 #else | 280 #else |
233 return ""; | 281 return ""; |
234 #endif | 282 #endif |
235 } | 283 } |
236 | 284 |
237 // static | 285 // static |
238 ui::Accelerator Command::StringToAccelerator(const std::string& accelerator) { | 286 ui::Accelerator Command::StringToAccelerator(const std::string& accelerator, |
| 287 const std::string& command_name) { |
239 string16 error; | 288 string16 error; |
240 Command command; | |
241 ui::Accelerator parsed = | 289 ui::Accelerator parsed = |
242 ParseImpl(accelerator, Command::CommandPlatform(), 0, &error); | 290 ParseImpl(accelerator, Command::CommandPlatform(), 0, |
| 291 IsNamedCommand(command_name), &error); |
243 return parsed; | 292 return parsed; |
244 } | 293 } |
245 | 294 |
246 // static | 295 // static |
247 std::string Command::AcceleratorToString(const ui::Accelerator& accelerator) { | 296 std::string Command::AcceleratorToString(const ui::Accelerator& accelerator) { |
248 std::string shortcut; | 297 std::string shortcut; |
249 | 298 |
250 // Ctrl and Alt are mutually exclusive. | 299 // Ctrl and Alt are mutually exclusive. |
251 if (accelerator.IsCtrlDown()) | 300 if (accelerator.IsCtrlDown()) |
252 shortcut += values::kKeyCtrl; | 301 shortcut += values::kKeyCtrl; |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
305 break; | 354 break; |
306 case ui::VKEY_PRIOR: | 355 case ui::VKEY_PRIOR: |
307 shortcut += values::kKeyPgUp; | 356 shortcut += values::kKeyPgUp; |
308 break; | 357 break; |
309 case ui::VKEY_NEXT: | 358 case ui::VKEY_NEXT: |
310 shortcut += values::kKeyPgDwn; | 359 shortcut += values::kKeyPgDwn; |
311 break; | 360 break; |
312 case ui::VKEY_TAB: | 361 case ui::VKEY_TAB: |
313 shortcut += values::kKeyTab; | 362 shortcut += values::kKeyTab; |
314 break; | 363 break; |
| 364 case ui::VKEY_MEDIA_NEXT_TRACK: |
| 365 shortcut += values::kKeyMediaNextTrack; |
| 366 break; |
| 367 case ui::VKEY_MEDIA_PLAY_PAUSE: |
| 368 shortcut += values::kKeyMediaPlayPause; |
| 369 break; |
| 370 case ui::VKEY_MEDIA_PREV_TRACK: |
| 371 shortcut += values::kKeyMediaPrevTrack; |
| 372 break; |
| 373 case ui::VKEY_MEDIA_STOP: |
| 374 shortcut += values::kKeyMediaStop; |
| 375 break; |
315 default: | 376 default: |
316 return ""; | 377 return ""; |
317 } | 378 } |
318 } | 379 } |
319 return shortcut; | 380 return shortcut; |
320 } | 381 } |
321 | 382 |
322 bool Command::Parse(const base::DictionaryValue* command, | 383 bool Command::Parse(const base::DictionaryValue* command, |
323 const std::string& command_name, | 384 const std::string& command_name, |
324 int index, | 385 int index, |
325 string16* error) { | 386 string16* error) { |
326 DCHECK(!command_name.empty()); | 387 DCHECK(!command_name.empty()); |
327 | 388 |
328 string16 description; | 389 string16 description; |
329 if (command_name != values::kPageActionCommandEvent && | 390 if (IsNamedCommand(command_name)) { |
330 command_name != values::kBrowserActionCommandEvent && | |
331 command_name != values::kScriptBadgeCommandEvent) { | |
332 if (!command->GetString(keys::kDescription, &description) || | 391 if (!command->GetString(keys::kDescription, &description) || |
333 description.empty()) { | 392 description.empty()) { |
334 *error = ErrorUtils::FormatErrorMessageUTF16( | 393 *error = ErrorUtils::FormatErrorMessageUTF16( |
335 errors::kInvalidKeyBindingDescription, | 394 errors::kInvalidKeyBindingDescription, |
336 base::IntToString(index)); | 395 base::IntToString(index)); |
337 return false; | 396 return false; |
338 } | 397 } |
339 } | 398 } |
340 | 399 |
341 // We'll build up a map of platform-to-shortcut suggestions. | 400 // We'll build up a map of platform-to-shortcut suggestions. |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
412 | 471 |
413 // For developer convenience, we parse all the suggestions (and complain about | 472 // For developer convenience, we parse all the suggestions (and complain about |
414 // errors for platforms other than the current one) but use only what we need. | 473 // errors for platforms other than the current one) but use only what we need. |
415 std::map<const std::string, std::string>::const_iterator iter = | 474 std::map<const std::string, std::string>::const_iterator iter = |
416 suggestions.begin(); | 475 suggestions.begin(); |
417 for ( ; iter != suggestions.end(); ++iter) { | 476 for ( ; iter != suggestions.end(); ++iter) { |
418 ui::Accelerator accelerator; | 477 ui::Accelerator accelerator; |
419 if (!iter->second.empty()) { | 478 if (!iter->second.empty()) { |
420 // Note that we pass iter->first to pretend we are on a platform we're not | 479 // Note that we pass iter->first to pretend we are on a platform we're not |
421 // on. | 480 // on. |
422 accelerator = ParseImpl(iter->second, iter->first, index, error); | 481 accelerator = ParseImpl(iter->second, iter->first, index, |
| 482 IsNamedCommand(command_name), error); |
423 if (accelerator.key_code() == ui::VKEY_UNKNOWN) { | 483 if (accelerator.key_code() == ui::VKEY_UNKNOWN) { |
424 *error = ErrorUtils::FormatErrorMessageUTF16( | 484 if (error->empty()) { |
425 errors::kInvalidKeyBinding, | 485 *error = ErrorUtils::FormatErrorMessageUTF16( |
426 base::IntToString(index), | 486 errors::kInvalidKeyBinding, |
427 iter->first, | 487 base::IntToString(index), |
428 iter->second); | 488 iter->first, |
| 489 iter->second); |
| 490 } |
429 return false; | 491 return false; |
430 } | 492 } |
431 } | 493 } |
432 | 494 |
433 if (iter->first == key) { | 495 if (iter->first == key) { |
434 // This platform is our platform, so grab this key. | 496 // This platform is our platform, so grab this key. |
435 accelerator_ = accelerator; | 497 accelerator_ = accelerator; |
436 command_name_ = command_name; | 498 command_name_ = command_name; |
437 description_ = description; | 499 description_ = description; |
438 } | 500 } |
(...skipping 17 matching lines...) Expand all Loading... |
456 extension_data->SetString("description", command_description); | 518 extension_data->SetString("description", command_description); |
457 extension_data->SetBoolean("active", active); | 519 extension_data->SetBoolean("active", active); |
458 extension_data->SetString("keybinding", accelerator().GetShortcutText()); | 520 extension_data->SetString("keybinding", accelerator().GetShortcutText()); |
459 extension_data->SetString("command_name", command_name()); | 521 extension_data->SetString("command_name", command_name()); |
460 extension_data->SetString("extension_id", extension->id()); | 522 extension_data->SetString("extension_id", extension->id()); |
461 | 523 |
462 return extension_data; | 524 return extension_data; |
463 } | 525 } |
464 | 526 |
465 } // namespace extensions | 527 } // namespace extensions |
OLD | NEW |