Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(233)

Side by Side Diff: chrome/common/extensions/extension.cc

Issue 9812008: Polish the keybinding implementation a bit. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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/extension.h" 5 #include "chrome/common/extensions/extension.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/base64.h" 9 #include "base/base64.h"
10 #include "base/basictypes.h" 10 #include "base/basictypes.h"
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after
237 237
238 Extension::TtsVoice::TtsVoice() {} 238 Extension::TtsVoice::TtsVoice() {}
239 Extension::TtsVoice::~TtsVoice() {} 239 Extension::TtsVoice::~TtsVoice() {}
240 240
241 Extension::OAuth2Info::OAuth2Info() {} 241 Extension::OAuth2Info::OAuth2Info() {}
242 Extension::OAuth2Info::~OAuth2Info() {} 242 Extension::OAuth2Info::~OAuth2Info() {}
243 243
244 Extension::ExtensionKeybinding::ExtensionKeybinding() {} 244 Extension::ExtensionKeybinding::ExtensionKeybinding() {}
245 Extension::ExtensionKeybinding::~ExtensionKeybinding() {} 245 Extension::ExtensionKeybinding::~ExtensionKeybinding() {}
246 246
247 bool Extension::ExtensionKeybinding::Parse(DictionaryValue* command, 247 ui::Accelerator Extension::ExtensionKeybinding::ParseImpl(
248 const std::string& command_name, 248 const std::string& shortcut,
249 int index, 249 const std::string& platform_key,
250 string16* error) { 250 int index,
251 DCHECK(!command_name.empty()); 251 string16* error) {
252 std::string key_binding; 252 std::string shortcut_normalized = StringToLowerASCII(shortcut);
Aaron Boodman 2012/03/26 21:44:57 Do we even need to do this part? I kinda prefer we
Finnur 2012/03/29 14:57:27 Need? No. I changed it everywhere to allow only
Aaron Boodman 2012/03/29 20:57:28 OK, let's try it out this way for awhile and see h
253 if (!command->GetString(keys::kKey, &key_binding) ||
254 key_binding.empty()) {
255 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
256 errors::kInvalidKeyBinding,
257 base::IntToString(index),
258 "Missing");
259 return false;
260 }
261
262 std::string original_keybinding = key_binding;
263 // Normalize '-' to '+'.
264 ReplaceSubstringsAfterOffset(&key_binding, 0, "-", "+");
265 // Remove all spaces.
266 ReplaceSubstringsAfterOffset(&key_binding, 0, " ", "");
267 // And finally, lower-case it.
268 key_binding = StringToLowerASCII(key_binding);
269 253
270 std::vector<std::string> tokens; 254 std::vector<std::string> tokens;
271 base::SplitString(key_binding, '+', &tokens); 255 base::SplitString(shortcut_normalized, '+', &tokens);
272 if (tokens.size() < 2 || tokens.size() > 3) { 256 if (tokens.size() < 2 || tokens.size() > 3) {
273 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( 257 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
274 errors::kInvalidKeyBinding, 258 errors::kInvalidKeyBinding,
275 base::IntToString(index), 259 base::IntToString(index),
276 original_keybinding); 260 platform_key,
277 return false; 261 shortcut);
262 return ui::Accelerator();
278 } 263 }
279 264
280 // Now, parse it into an accelerator. 265 // Now, parse it into an accelerator.
281 bool ctrl = false; 266 bool ctrl = false;
282 bool alt = false; 267 bool alt = false;
283 bool shift = false; 268 bool shift = false;
284 ui::KeyboardCode key = ui::VKEY_UNKNOWN; 269 ui::KeyboardCode key = ui::VKEY_UNKNOWN;
285 for (size_t i = 0; i < tokens.size(); i++) { 270 for (size_t i = 0; i < tokens.size(); i++) {
286 if (tokens[i] == "ctrl") { 271 if (tokens[i] == "ctrl") {
287 ctrl = true; 272 ctrl = true;
288 } else if (tokens[i] == "alt") { 273 } else if (tokens[i] == "alt") {
289 alt = true; 274 alt = true;
290 } else if (tokens[i] == "shift") { 275 } else if (tokens[i] == "shift") {
291 shift = true; 276 shift = true;
277 } else if (tokens[i] == "command" && platform_key == "mac") {
278 // TODO(finnur): Implement for Mac.
279 } else if (tokens[i] == "option" && platform_key == "mac") {
280 // TODO(finnur): Implement for Mac.
292 } else if (tokens[i].size() == 1 && 281 } else if (tokens[i].size() == 1 &&
293 tokens[i][0] >= 'a' && tokens[i][0] <= 'z') { 282 tokens[i][0] >= 'a' && tokens[i][0] <= 'z') {
294 if (key != ui::VKEY_UNKNOWN) { 283 if (key != ui::VKEY_UNKNOWN) {
295 // Multiple key assignments. 284 // Multiple key assignments.
296 key = ui::VKEY_UNKNOWN; 285 key = ui::VKEY_UNKNOWN;
297 break; 286 break;
298 } 287 }
299 288
300 key = static_cast<ui::KeyboardCode>(ui::VKEY_A + (tokens[i][0] - 'a')); 289 key = static_cast<ui::KeyboardCode>(ui::VKEY_A + (tokens[i][0] - 'a'));
301 } else { 290 } else {
302 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( 291 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
303 errors::kInvalidKeyBinding, 292 errors::kInvalidKeyBinding,
304 base::IntToString(index), 293 base::IntToString(index),
305 original_keybinding); 294 platform_key,
306 return false; 295 shortcut);
296 return ui::Accelerator();
307 } 297 }
308 } 298 }
309 299
310 // We support Ctrl+foo, Alt+foo, Ctrl+Shift+foo, Alt+Shift+foo, but not 300 // We support Ctrl+foo, Alt+foo, Ctrl+Shift+foo, Alt+Shift+foo, but not
311 // Ctrl+Alt+foo. For a more detailed reason why we don't support Ctrl+Alt+foo: 301 // Ctrl+Alt+foo. For a more detailed reason why we don't support Ctrl+Alt+foo:
Aaron Boodman 2012/03/26 21:44:57 What about ctrl+alt+shift+foo ?
Finnur 2012/03/29 14:57:27 That's just a variation of Ctrl+Alt+something, so
Aaron Boodman 2012/03/29 20:57:28 Bummer. OK>
312 // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx. 302 // http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx.
313 if (key == ui::VKEY_UNKNOWN || (ctrl && alt)) { 303 if (key == ui::VKEY_UNKNOWN || (ctrl && alt)) {
314 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( 304 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
315 errors::kInvalidKeyBinding, 305 errors::kInvalidKeyBinding,
316 base::IntToString(index), 306 base::IntToString(index),
317 original_keybinding); 307 platform_key,
318 return false; 308 shortcut);
309 return ui::Accelerator();
319 } 310 }
320 311
321 accelerator_ = ui::Accelerator(key, shift, ctrl, alt); 312 return ui::Accelerator(key, shift, ctrl, alt);
313 }
322 314
323 if (command_name != 315 bool Extension::ExtensionKeybinding::Parse(DictionaryValue* command,
324 extension_manifest_values::kPageActionKeybindingEvent && 316 const std::string& command_name,
325 command_name != 317 int index,
326 extension_manifest_values::kBrowserActionKeybindingEvent) { 318 string16* error) {
327 if (!command->GetString(keys::kDescription, &description_) || 319 DCHECK(!command_name.empty());
328 description_.empty()) { 320
321 // We'll build up a map of platform-to-shortcut suggestions.
322 std::map<const std::string, std::string> suggestions;
323
324 // First try to parse the |commands| section as a dictionary.
Aaron Boodman 2012/03/26 21:44:57 s/commands/suggested_key/ ?
325 DictionaryValue* suggested_key_dict;
326 if (command->GetDictionary(keys::kSuggestedKey, &suggested_key_dict)) {
327 DictionaryValue::key_iterator iter = suggested_key_dict->begin_keys();
328 for ( ; iter != suggested_key_dict->end_keys(); ++iter) {
329 // For each item in the dictionary, extract the platforms specified.
330 std::string suggested_key_string;
331 if (suggested_key_dict->GetString(*iter, &suggested_key_string) &&
332 !suggested_key_string.empty()) {
333 // Found a platform, add it to the suggestions list.
334 suggestions[StringToLowerASCII(*iter)] = suggested_key_string;
Aaron Boodman 2012/03/26 21:44:57 Again, I would just require the case to be correct
Aaron Boodman 2012/03/26 21:44:57 Do we handle the case where the platform is typo'd
Finnur 2012/03/29 14:57:27 We did not. But we do now. On 2012/03/26 21:44:57
335 } else {
336 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
337 errors::kInvalidKeyBinding,
338 base::IntToString(index),
339 keys::kSuggestedKey,
340 "Missing");
341 return false;
342 }
343 }
344 } else {
345 // No dictionary was found, fall back to using just a string, so developers
346 // don't have to specify a dictionary if they just want to use one default
347 // for all platforms.
348 std::string suggested_key_string;
349 if (command->GetString(keys::kSuggestedKey, &suggested_key_string) &&
350 !suggested_key_string.empty()) {
351 // If only a signle string is provided, it must be default for all.
352 suggestions["default"] = suggested_key_string;
353 } else {
329 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( 354 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
330 errors::kInvalidKeyBindingDescription, 355 errors::kInvalidKeyBinding,
331 base::IntToString(index)); 356 base::IntToString(index),
332 return false; 357 keys::kSuggestedKey,
358 "Missing");
359 return false;
333 } 360 }
334 } 361 }
335 362
336 command_name_ = command_name; 363 std::string platform =
364 #if defined(OS_WIN)
365 values::kKeybindingPlatformWin;
366 #elif defined(OS_MACOSX)
367 values::kKeybindingPlatformMac;
368 #elif defined(OS_CHROMEOS)
369 values::kKeybindingPlatformChromeOs;
370 #elif defined(OS_LINUX)
371 values::kKeybindingPlatformLinux;
372 #else
373 "";
374 #endif
375
376 std::string key = platform;
377 if (suggestions.find(key) == suggestions.end())
378 key = "default";
379 if (suggestions.find(key) == suggestions.end()) {
380 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
381 errors::kInvalidKeyBindingMissingPlatform,
382 base::IntToString(index),
383 keys::kSuggestedKey,
384 platform);
385 return false; // No platform specified and no fallback. Bail.
386 }
387
388 // For developer convenience, we parse all the suggestions (and complain about
389 // errors for platforms other than the current one) but use only what we need.
390 std::map<const std::string, std::string>::const_iterator iter =
391 suggestions.begin();
392 for ( ; iter != suggestions.end(); ++iter) {
393 // Note that we pass iter->first to pretend we are on a platform we're not
394 // on.
395 ui::Accelerator accelerator =
396 ParseImpl(iter->second, iter->first, index, error);
397 if (accelerator.key_code() == ui::VKEY_UNKNOWN) {
398 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
399 errors::kInvalidKeyBinding,
400 base::IntToString(index),
401 iter->first,
402 iter->second);
403 return false;
404 }
405
406 if (iter->first == key) {
407 // This platform is our platform, so grab this key.
408 accelerator_ = accelerator;
409 command_name_ = command_name;
410
411 if (command_name !=
412 extension_manifest_values::kPageActionKeybindingEvent &&
413 command_name !=
414 extension_manifest_values::kBrowserActionKeybindingEvent) {
415 if (!command->GetString(keys::kDescription, &description_) ||
416 description_.empty()) {
417 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
418 errors::kInvalidKeyBindingDescription,
419 base::IntToString(index));
420 return false;
421 }
422 }
423 }
424 }
337 return true; 425 return true;
338 } 426 }
339 427
340 // 428 //
341 // Extension 429 // Extension
342 // 430 //
343 431
344 // static 432 // static
345 scoped_refptr<Extension> Extension::Create(const FilePath& path, 433 scoped_refptr<Extension> Extension::Create(const FilePath& path,
346 Location location, 434 Location location,
(...skipping 1165 matching lines...) Expand 10 before | Expand all | Expand 10 after
1512 ++command_index; 1600 ++command_index;
1513 1601
1514 DictionaryValue* command = NULL; 1602 DictionaryValue* command = NULL;
1515 if (!commands->GetDictionary(*iter, &command)) { 1603 if (!commands->GetDictionary(*iter, &command)) {
1516 *error = ExtensionErrorUtils::FormatErrorMessageUTF16( 1604 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1517 errors::kInvalidKeyBindingDictionary, 1605 errors::kInvalidKeyBindingDictionary,
1518 base::IntToString(command_index)); 1606 base::IntToString(command_index));
1519 return false; 1607 return false;
1520 } 1608 }
1521 1609
1522 ExtensionKeybinding binding; 1610 scoped_ptr<Extension::ExtensionKeybinding> binding(
1523 if (!binding.Parse(command, *iter, command_index, error)) 1611 new ExtensionKeybinding());
1612 if (!binding->Parse(command, *iter, command_index, error))
1524 return false; // |error| already set. 1613 return false; // |error| already set.
1525 1614
1526 commands_.push_back(binding); 1615 std::string command_name = binding->command_name();
1616 if (command_name == values::kPageActionKeybindingEvent)
1617 page_action_command_.reset(binding.release());
1618 else if (command_name == values::kBrowserActionKeybindingEvent)
1619 browser_action_command_.reset(binding.release());
1620 else
Aaron Boodman 2012/03/26 21:44:57 You should skip keys beginning with "_" if they ar
1621 named_commands_[command_name] = *binding.release();
1527 } 1622 }
1528 } 1623 }
1529 return true; 1624 return true;
1530 } 1625 }
1531 1626
1532 bool Extension::LoadPlugins(string16* error) { 1627 bool Extension::LoadPlugins(string16* error) {
1533 if (!manifest_->HasKey(keys::kPlugins)) 1628 if (!manifest_->HasKey(keys::kPlugins))
1534 return true; 1629 return true;
1535 ListValue* list_value = NULL; 1630 ListValue* list_value = NULL;
1536 if (!manifest_->GetList(keys::kPlugins, &list_value)) { 1631 if (!manifest_->GetList(keys::kPlugins, &list_value)) {
(...skipping 1996 matching lines...) Expand 10 before | Expand all | Expand 10 after
3533 already_disabled(false), 3628 already_disabled(false),
3534 extension(extension) {} 3629 extension(extension) {}
3535 3630
3536 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( 3631 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo(
3537 const Extension* extension, 3632 const Extension* extension,
3538 const ExtensionPermissionSet* permissions, 3633 const ExtensionPermissionSet* permissions,
3539 Reason reason) 3634 Reason reason)
3540 : reason(reason), 3635 : reason(reason),
3541 extension(extension), 3636 extension(extension),
3542 permissions(permissions) {} 3637 permissions(permissions) {}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698