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/extension.h" | 5 #include "chrome/common/extensions/extension.h" |
6 | 6 |
7 #include <ostream> | 7 #include <ostream> |
8 | 8 |
9 #include "base/base64.h" | 9 #include "base/base64.h" |
10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
274 *target = 0; | 274 *target = 0; |
275 *error = ErrorUtils::FormatErrorMessageUTF16( | 275 *error = ErrorUtils::FormatErrorMessageUTF16( |
276 errors::kInvalidLaunchValue, | 276 errors::kInvalidLaunchValue, |
277 key); | 277 key); |
278 return false; | 278 return false; |
279 } | 279 } |
280 } | 280 } |
281 return true; | 281 return true; |
282 } | 282 } |
283 | 283 |
| 284 std::string SizeToString(const gfx::Size& max_size) { |
| 285 return base::IntToString(max_size.width()) + "x" + |
| 286 base::IntToString(max_size.height()); |
| 287 } |
| 288 |
284 } // namespace | 289 } // namespace |
285 | 290 |
286 const FilePath::CharType Extension::kManifestFilename[] = | 291 const FilePath::CharType Extension::kManifestFilename[] = |
287 FILE_PATH_LITERAL("manifest.json"); | 292 FILE_PATH_LITERAL("manifest.json"); |
288 const FilePath::CharType Extension::kLocaleFolder[] = | 293 const FilePath::CharType Extension::kLocaleFolder[] = |
289 FILE_PATH_LITERAL("_locales"); | 294 FILE_PATH_LITERAL("_locales"); |
290 const FilePath::CharType Extension::kMessagesFilename[] = | 295 const FilePath::CharType Extension::kMessagesFilename[] = |
291 FILE_PATH_LITERAL("messages.json"); | 296 FILE_PATH_LITERAL("messages.json"); |
292 | 297 |
293 #if defined(OS_WIN) | 298 #if defined(OS_WIN) |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 Extension::ActionInfo::ActionInfo() {} | 340 Extension::ActionInfo::ActionInfo() {} |
336 Extension::ActionInfo::~ActionInfo() {} | 341 Extension::ActionInfo::~ActionInfo() {} |
337 | 342 |
338 Extension::FileHandlerInfo::FileHandlerInfo() {} | 343 Extension::FileHandlerInfo::FileHandlerInfo() {} |
339 Extension::FileHandlerInfo::~FileHandlerInfo() {} | 344 Extension::FileHandlerInfo::~FileHandlerInfo() {} |
340 | 345 |
341 // | 346 // |
342 // Extension | 347 // Extension |
343 // | 348 // |
344 | 349 |
| 350 bool Extension::InstallWarning::operator==(const InstallWarning& other) const { |
| 351 return format == other.format && message == other.message; |
| 352 } |
| 353 |
345 // static | 354 // static |
346 scoped_refptr<Extension> Extension::Create(const FilePath& path, | 355 scoped_refptr<Extension> Extension::Create(const FilePath& path, |
347 Location location, | 356 Location location, |
348 const DictionaryValue& value, | 357 const DictionaryValue& value, |
349 int flags, | 358 int flags, |
350 std::string* utf8_error) { | 359 std::string* utf8_error) { |
351 return Extension::Create(path, | 360 return Extension::Create(path, |
352 location, | 361 location, |
353 value, | 362 value, |
354 flags, | 363 flags, |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
404 int loc2_rank = GetLocationRank(loc2); | 413 int loc2_rank = GetLocationRank(loc2); |
405 | 414 |
406 // If two different locations have the same rank, then we can not | 415 // If two different locations have the same rank, then we can not |
407 // deterministicly choose a location. | 416 // deterministicly choose a location. |
408 CHECK(loc1_rank != loc2_rank); | 417 CHECK(loc1_rank != loc2_rank); |
409 | 418 |
410 // Highest rank has highest priority. | 419 // Highest rank has highest priority. |
411 return (loc1_rank > loc2_rank ? loc1 : loc2 ); | 420 return (loc1_rank > loc2_rank ? loc1 : loc2 ); |
412 } | 421 } |
413 | 422 |
414 void Extension::OverrideLaunchUrl(const GURL& override_url) { | |
415 GURL new_url(override_url); | |
416 if (!new_url.is_valid()) { | |
417 DLOG(WARNING) << "Invalid override url given for " << name(); | |
418 } else { | |
419 if (new_url.has_port()) { | |
420 DLOG(WARNING) << "Override URL passed for " << name() | |
421 << " should not contain a port. Removing it."; | |
422 | |
423 GURL::Replacements remove_port; | |
424 remove_port.ClearPort(); | |
425 new_url = new_url.ReplaceComponents(remove_port); | |
426 } | |
427 | |
428 launch_web_url_ = new_url.spec(); | |
429 | |
430 URLPattern pattern(kValidWebExtentSchemes); | |
431 URLPattern::ParseResult result = pattern.Parse(new_url.spec()); | |
432 DCHECK_EQ(result, URLPattern::PARSE_SUCCESS); | |
433 pattern.SetPath(pattern.path() + '*'); | |
434 extent_.AddPattern(pattern); | |
435 } | |
436 } | |
437 | |
438 FilePath Extension::MaybeNormalizePath(const FilePath& path) { | |
439 #if defined(OS_WIN) | |
440 // Normalize any drive letter to upper-case. We do this for consistency with | |
441 // net_utils::FilePathToFileURL(), which does the same thing, to make string | |
442 // comparisons simpler. | |
443 std::wstring path_str = path.value(); | |
444 if (path_str.size() >= 2 && path_str[0] >= L'a' && path_str[0] <= L'z' && | |
445 path_str[1] == ':') | |
446 path_str[0] += ('A' - 'a'); | |
447 | |
448 return FilePath(path_str); | |
449 #else | |
450 return path; | |
451 #endif | |
452 } | |
453 | |
454 Extension::Location Extension::location() const { | |
455 return manifest_->location(); | |
456 } | |
457 | |
458 const std::string& Extension::id() const { | |
459 return manifest_->extension_id(); | |
460 } | |
461 | |
462 const std::string Extension::VersionString() const { | |
463 return version()->GetString(); | |
464 } | |
465 | |
466 void Extension::AddInstallWarnings( | |
467 const InstallWarningVector& new_warnings) { | |
468 install_warnings_.insert(install_warnings_.end(), | |
469 new_warnings.begin(), new_warnings.end()); | |
470 } | |
471 | |
472 // static | |
473 bool Extension::IsExtension(const FilePath& file_name) { | |
474 return file_name.MatchesExtension(chrome::kExtensionFileExtension); | |
475 } | |
476 | |
477 // static | 423 // static |
478 bool Extension::IdIsValid(const std::string& id) { | 424 bool Extension::IdIsValid(const std::string& id) { |
479 // Verify that the id is legal. | 425 // Verify that the id is legal. |
480 if (id.size() != (kIdSize * 2)) | 426 if (id.size() != (kIdSize * 2)) |
481 return false; | 427 return false; |
482 | 428 |
483 // We only support lowercase IDs, because IDs can be used as URL components | 429 // We only support lowercase IDs, because IDs can be used as URL components |
484 // (where GURL will lowercase it). | 430 // (where GURL will lowercase it). |
485 std::string temp = StringToLowerASCII(id); | 431 std::string temp = StringToLowerASCII(id); |
486 for (size_t i = 0; i < temp.size(); i++) | 432 for (size_t i = 0; i < temp.size(); i++) |
487 if (temp[i] < 'a' || temp[i] > 'p') | 433 if (temp[i] < 'a' || temp[i] > 'p') |
488 return false; | 434 return false; |
489 | 435 |
490 return true; | 436 return true; |
491 } | 437 } |
492 | 438 |
493 // static | 439 // static |
494 std::string Extension::GenerateIdForPath(const FilePath& path) { | 440 std::string Extension::GenerateIdForPath(const FilePath& path) { |
495 FilePath new_path = Extension::MaybeNormalizePath(path); | 441 FilePath new_path = Extension::MaybeNormalizePath(path); |
496 std::string path_bytes = | 442 std::string path_bytes = |
497 std::string(reinterpret_cast<const char*>(new_path.value().data()), | 443 std::string(reinterpret_cast<const char*>(new_path.value().data()), |
498 new_path.value().size() * sizeof(FilePath::CharType)); | 444 new_path.value().size() * sizeof(FilePath::CharType)); |
499 std::string id; | 445 std::string id; |
500 return GenerateId(path_bytes, &id) ? id : ""; | 446 return GenerateId(path_bytes, &id) ? id : ""; |
501 } | 447 } |
502 | 448 |
| 449 // static |
| 450 bool Extension::IsExtension(const FilePath& file_name) { |
| 451 return file_name.MatchesExtension(chrome::kExtensionFileExtension); |
| 452 } |
| 453 |
503 void Extension::GetBasicInfo(bool enabled, | 454 void Extension::GetBasicInfo(bool enabled, |
504 DictionaryValue* info) const { | 455 DictionaryValue* info) const { |
505 info->SetString(info_keys::kIdKey, id()); | 456 info->SetString(info_keys::kIdKey, id()); |
506 info->SetString(info_keys::kNameKey, name()); | 457 info->SetString(info_keys::kNameKey, name()); |
507 info->SetBoolean(info_keys::kEnabledKey, enabled); | 458 info->SetBoolean(info_keys::kEnabledKey, enabled); |
508 info->SetBoolean(info_keys::kOfflineEnabledKey, offline_enabled()); | 459 info->SetBoolean(info_keys::kOfflineEnabledKey, offline_enabled()); |
509 info->SetString(info_keys::kVersionKey, VersionString()); | 460 info->SetString(info_keys::kVersionKey, VersionString()); |
510 info->SetString(info_keys::kDescriptionKey, description()); | 461 info->SetString(info_keys::kDescriptionKey, description()); |
511 info->SetString(info_keys::kOptionsUrlKey, | 462 info->SetString(info_keys::kOptionsUrlKey, |
512 options_url().possibly_invalid_spec()); | 463 options_url().possibly_invalid_spec()); |
(...skipping 21 matching lines...) Expand all Loading... |
534 // to that base directory, so strip the leading "/" if present. | 485 // to that base directory, so strip the leading "/" if present. |
535 if (relative_path.size() > 0 && relative_path[0] == '/') | 486 if (relative_path.size() > 0 && relative_path[0] == '/') |
536 path = relative_path.substr(1); | 487 path = relative_path.substr(1); |
537 | 488 |
538 GURL ret_val = GURL(extension_url.spec() + path); | 489 GURL ret_val = GURL(extension_url.spec() + path); |
539 DCHECK(StartsWithASCII(ret_val.spec(), extension_url.spec(), false)); | 490 DCHECK(StartsWithASCII(ret_val.spec(), extension_url.spec(), false)); |
540 | 491 |
541 return ret_val; | 492 return ret_val; |
542 } | 493 } |
543 | 494 |
544 bool Extension::is_platform_app() const { | |
545 return manifest_->is_platform_app(); | |
546 } | |
547 | |
548 bool Extension::is_hosted_app() const { | |
549 return manifest()->is_hosted_app(); | |
550 } | |
551 | |
552 bool Extension::is_legacy_packaged_app() const { | |
553 return manifest()->is_legacy_packaged_app(); | |
554 } | |
555 | |
556 bool Extension::is_theme() const { | |
557 return manifest()->is_theme(); | |
558 } | |
559 | |
560 GURL Extension::GetBackgroundURL() const { | |
561 if (background_scripts_.empty()) | |
562 return background_url_; | |
563 return GetResourceURL(extension_filenames::kGeneratedBackgroundPageFilename); | |
564 } | |
565 | |
566 bool Extension::ResourceMatches(const URLPatternSet& pattern_set, | 495 bool Extension::ResourceMatches(const URLPatternSet& pattern_set, |
567 const std::string& resource) const { | 496 const std::string& resource) const { |
568 return pattern_set.MatchesURL(extension_url_.Resolve(resource)); | 497 return pattern_set.MatchesURL(extension_url_.Resolve(resource)); |
569 } | 498 } |
570 | 499 |
571 bool Extension::IsResourceWebAccessible(const std::string& relative_path) | 500 bool Extension::IsResourceWebAccessible(const std::string& relative_path) |
572 const { | 501 const { |
573 // For old manifest versions which do not specify web_accessible_resources | 502 // For old manifest versions which do not specify web_accessible_resources |
574 // we always allow resource loads. | 503 // we always allow resource loads. |
575 if (manifest_version_ < 2 && !HasWebAccessibleResources()) | 504 if (manifest_version_ < 2 && !HasWebAccessibleResources()) |
576 return true; | 505 return true; |
577 | 506 |
578 return ResourceMatches(web_accessible_resources_, relative_path); | 507 return ResourceMatches(web_accessible_resources_, relative_path); |
579 } | 508 } |
580 | 509 |
581 bool Extension::HasWebAccessibleResources() const { | |
582 return web_accessible_resources_.size() > 0; | |
583 } | |
584 | |
585 bool Extension::IsSandboxedPage(const std::string& relative_path) const { | 510 bool Extension::IsSandboxedPage(const std::string& relative_path) const { |
586 return ResourceMatches(sandboxed_pages_, relative_path); | 511 return ResourceMatches(sandboxed_pages_, relative_path); |
587 } | 512 } |
588 | 513 |
589 std::string Extension::GetResourceContentSecurityPolicy( | 514 std::string Extension::GetResourceContentSecurityPolicy( |
590 const std::string& relative_path) const { | 515 const std::string& relative_path) const { |
591 return IsSandboxedPage(relative_path) ? | 516 return IsSandboxedPage(relative_path) ? |
592 sandboxed_pages_content_security_policy_ : content_security_policy(); | 517 sandboxed_pages_content_security_policy_ : content_security_policy(); |
593 } | 518 } |
594 | 519 |
| 520 bool Extension::HasWebAccessibleResources() const { |
| 521 return web_accessible_resources_.size() > 0; |
| 522 } |
| 523 |
| 524 ExtensionResource Extension::GetResource( |
| 525 const std::string& relative_path) const { |
| 526 std::string new_path = relative_path; |
| 527 // We have some legacy data where resources have leading slashes. |
| 528 // See: http://crbug.com/121164 |
| 529 if (!new_path.empty() && new_path.at(0) == '/') |
| 530 new_path.erase(0, 1); |
| 531 #if defined(OS_POSIX) |
| 532 FilePath relative_file_path(new_path); |
| 533 #elif defined(OS_WIN) |
| 534 FilePath relative_file_path(UTF8ToWide(new_path)); |
| 535 #endif |
| 536 ExtensionResource r(id(), path(), relative_file_path); |
| 537 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { |
| 538 r.set_follow_symlinks_anywhere(); |
| 539 } |
| 540 return r; |
| 541 } |
| 542 |
| 543 ExtensionResource Extension::GetResource( |
| 544 const FilePath& relative_file_path) const { |
| 545 ExtensionResource r(id(), path(), relative_file_path); |
| 546 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { |
| 547 r.set_follow_symlinks_anywhere(); |
| 548 } |
| 549 return r; |
| 550 } |
| 551 |
| 552 // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a |
| 553 // util class in base: |
| 554 // http://code.google.com/p/chromium/issues/detail?id=13572 |
| 555 // static |
| 556 bool Extension::ParsePEMKeyBytes(const std::string& input, |
| 557 std::string* output) { |
| 558 DCHECK(output); |
| 559 if (!output) |
| 560 return false; |
| 561 if (input.length() == 0) |
| 562 return false; |
| 563 |
| 564 std::string working = input; |
| 565 if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) { |
| 566 working = CollapseWhitespaceASCII(working, true); |
| 567 size_t header_pos = working.find(kKeyInfoEndMarker, |
| 568 sizeof(kKeyBeginHeaderMarker) - 1); |
| 569 if (header_pos == std::string::npos) |
| 570 return false; |
| 571 size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1; |
| 572 size_t end_pos = working.rfind(kKeyBeginFooterMarker); |
| 573 if (end_pos == std::string::npos) |
| 574 return false; |
| 575 if (start_pos >= end_pos) |
| 576 return false; |
| 577 |
| 578 working = working.substr(start_pos, end_pos - start_pos); |
| 579 if (working.length() == 0) |
| 580 return false; |
| 581 } |
| 582 |
| 583 return base::Base64Decode(working, output); |
| 584 } |
| 585 |
| 586 // static |
| 587 bool Extension::ProducePEM(const std::string& input, std::string* output) { |
| 588 DCHECK(output); |
| 589 return (input.length() == 0) ? false : base::Base64Encode(input, output); |
| 590 } |
| 591 |
| 592 // static |
595 bool Extension::GenerateId(const std::string& input, std::string* output) { | 593 bool Extension::GenerateId(const std::string& input, std::string* output) { |
596 DCHECK(output); | 594 DCHECK(output); |
597 uint8 hash[Extension::kIdSize]; | 595 uint8 hash[Extension::kIdSize]; |
598 crypto::SHA256HashString(input, hash, sizeof(hash)); | 596 crypto::SHA256HashString(input, hash, sizeof(hash)); |
599 *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash))); | 597 *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash))); |
600 ConvertHexadecimalToIDAlphabet(output); | 598 ConvertHexadecimalToIDAlphabet(output); |
601 | 599 |
602 return true; | 600 return true; |
603 } | 601 } |
604 | 602 |
605 // Helper method that loads a UserScript object from a dictionary in the | 603 // static |
606 // content_script list of the manifest. | 604 bool Extension::FormatPEMForFileOutput(const std::string& input, |
607 bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, | 605 std::string* output, |
608 int definition_index, | 606 bool is_public) { |
609 string16* error, | 607 DCHECK(output); |
610 UserScript* result) { | 608 if (input.length() == 0) |
611 // run_at | 609 return false; |
612 if (content_script->HasKey(keys::kRunAt)) { | 610 *output = ""; |
613 std::string run_location; | 611 output->append(kKeyBeginHeaderMarker); |
614 if (!content_script->GetString(keys::kRunAt, &run_location)) { | 612 output->append(" "); |
| 613 output->append(is_public ? kPublic : kPrivate); |
| 614 output->append(" "); |
| 615 output->append(kKeyInfoEndMarker); |
| 616 output->append("\n"); |
| 617 for (size_t i = 0; i < input.length(); ) { |
| 618 int slice = std::min<int>(input.length() - i, kPEMOutputColumns); |
| 619 output->append(input.substr(i, slice)); |
| 620 output->append("\n"); |
| 621 i += slice; |
| 622 } |
| 623 output->append(kKeyBeginFooterMarker); |
| 624 output->append(" "); |
| 625 output->append(is_public ? kPublic : kPrivate); |
| 626 output->append(" "); |
| 627 output->append(kKeyInfoEndMarker); |
| 628 output->append("\n"); |
| 629 |
| 630 return true; |
| 631 } |
| 632 |
| 633 // static |
| 634 void Extension::DecodeIcon(const Extension* extension, |
| 635 int preferred_icon_size, |
| 636 ExtensionIconSet::MatchType match_type, |
| 637 scoped_ptr<SkBitmap>* result) { |
| 638 std::string path = extension->icons().Get(preferred_icon_size, match_type); |
| 639 int size = extension->icons().GetIconSizeFromPath(path); |
| 640 ExtensionResource icon_resource = extension->GetResource(path); |
| 641 DecodeIconFromPath(icon_resource.GetFilePath(), size, result); |
| 642 } |
| 643 |
| 644 // static |
| 645 void Extension::DecodeIcon(const Extension* extension, |
| 646 int icon_size, |
| 647 scoped_ptr<SkBitmap>* result) { |
| 648 DecodeIcon(extension, icon_size, ExtensionIconSet::MATCH_EXACTLY, result); |
| 649 } |
| 650 |
| 651 // static |
| 652 void Extension::DecodeIconFromPath(const FilePath& icon_path, |
| 653 int icon_size, |
| 654 scoped_ptr<SkBitmap>* result) { |
| 655 if (icon_path.empty()) |
| 656 return; |
| 657 |
| 658 std::string file_contents; |
| 659 if (!file_util::ReadFileToString(icon_path, &file_contents)) { |
| 660 DLOG(ERROR) << "Could not read icon file: " << icon_path.LossyDisplayName(); |
| 661 return; |
| 662 } |
| 663 |
| 664 // Decode the image using WebKit's image decoder. |
| 665 const unsigned char* data = |
| 666 reinterpret_cast<const unsigned char*>(file_contents.data()); |
| 667 webkit_glue::ImageDecoder decoder; |
| 668 scoped_ptr<SkBitmap> decoded(new SkBitmap()); |
| 669 *decoded = decoder.Decode(data, file_contents.length()); |
| 670 if (decoded->empty()) { |
| 671 DLOG(ERROR) << "Could not decode icon file: " |
| 672 << icon_path.LossyDisplayName(); |
| 673 return; |
| 674 } |
| 675 |
| 676 if (decoded->width() != icon_size || decoded->height() != icon_size) { |
| 677 DLOG(ERROR) << "Icon file has unexpected size: " |
| 678 << base::IntToString(decoded->width()) << "x" |
| 679 << base::IntToString(decoded->height()); |
| 680 return; |
| 681 } |
| 682 |
| 683 result->swap(decoded); |
| 684 } |
| 685 |
| 686 // static |
| 687 const gfx::ImageSkia& Extension::GetDefaultIcon(bool is_app) { |
| 688 int id = is_app ? IDR_APP_DEFAULT_ICON : IDR_EXTENSION_DEFAULT_ICON; |
| 689 return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id); |
| 690 } |
| 691 |
| 692 // static |
| 693 GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { |
| 694 return GURL(std::string(extensions::kExtensionScheme) + |
| 695 content::kStandardSchemeSeparator + extension_id + "/"); |
| 696 } |
| 697 |
| 698 // static |
| 699 void Extension::SetScriptingWhitelist( |
| 700 const Extension::ScriptingWhitelist& whitelist) { |
| 701 ScriptingWhitelist* current_whitelist = |
| 702 ExtensionConfig::GetInstance()->whitelist(); |
| 703 current_whitelist->clear(); |
| 704 for (ScriptingWhitelist::const_iterator it = whitelist.begin(); |
| 705 it != whitelist.end(); ++it) { |
| 706 current_whitelist->push_back(*it); |
| 707 } |
| 708 } |
| 709 |
| 710 // static |
| 711 const Extension::ScriptingWhitelist* Extension::GetScriptingWhitelist() { |
| 712 return ExtensionConfig::GetInstance()->whitelist(); |
| 713 } |
| 714 |
| 715 bool Extension::ParsePermissions(const char* key, |
| 716 string16* error, |
| 717 APIPermissionSet* api_permissions, |
| 718 URLPatternSet* host_permissions) { |
| 719 if (manifest_->HasKey(key)) { |
| 720 ListValue* permissions = NULL; |
| 721 if (!manifest_->GetList(key, &permissions)) { |
615 *error = ErrorUtils::FormatErrorMessageUTF16( | 722 *error = ErrorUtils::FormatErrorMessageUTF16( |
616 errors::kInvalidRunAt, | 723 errors::kInvalidPermissions, ""); |
617 base::IntToString(definition_index)); | 724 return false; |
618 return false; | 725 } |
619 } | 726 |
620 | 727 // NOTE: We need to get the APIPermission before we check if features |
621 if (run_location == values::kRunAtDocumentStart) { | 728 // associated with them are available because the feature system does not |
622 result->set_run_location(UserScript::DOCUMENT_START); | 729 // know about aliases. |
623 } else if (run_location == values::kRunAtDocumentEnd) { | 730 |
624 result->set_run_location(UserScript::DOCUMENT_END); | 731 std::vector<std::string> host_data; |
625 } else if (run_location == values::kRunAtDocumentIdle) { | 732 if (!APIPermissionSet::ParseFromJSON(permissions, api_permissions, |
626 result->set_run_location(UserScript::DOCUMENT_IDLE); | 733 error, &host_data)) |
627 } else { | 734 return false; |
628 *error = ErrorUtils::FormatErrorMessageUTF16( | 735 |
629 errors::kInvalidRunAt, | 736 // Verify feature availability of permissions. |
630 base::IntToString(definition_index)); | 737 std::vector<APIPermission::ID> to_remove; |
631 return false; | 738 SimpleFeatureProvider* permission_features = |
632 } | 739 SimpleFeatureProvider::GetPermissionFeatures(); |
633 } | 740 for (APIPermissionSet::const_iterator it = api_permissions->begin(); |
634 | 741 it != api_permissions->end(); ++it) { |
635 // all frames | 742 extensions::Feature* feature = |
636 if (content_script->HasKey(keys::kAllFrames)) { | 743 permission_features->GetFeature(it->name()); |
637 bool all_frames = false; | 744 |
638 if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) { | 745 // The feature should exist since we just got an APIPermission |
639 *error = ErrorUtils::FormatErrorMessageUTF16( | 746 // for it. The two systems should be updated together whenever a |
640 errors::kInvalidAllFrames, base::IntToString(definition_index)); | 747 // permission is added. |
641 return false; | 748 CHECK(feature); |
642 } | 749 |
643 result->set_match_all_frames(all_frames); | 750 Feature::Availability availability = |
644 } | 751 feature->IsAvailableToManifest( |
645 | 752 id(), |
646 // matches (required) | 753 GetType(), |
647 const ListValue* matches = NULL; | 754 Feature::ConvertLocation(location()), |
648 if (!content_script->GetList(keys::kMatches, &matches)) { | 755 manifest_version()); |
649 *error = ErrorUtils::FormatErrorMessageUTF16( | 756 if (!availability.is_available()) { |
650 errors::kInvalidMatches, | 757 // Don't fail, but warn the developer that the manifest contains |
651 base::IntToString(definition_index)); | 758 // unrecognized permissions. This may happen legitimately if the |
652 return false; | 759 // extensions requests platform- or channel-specific permissions. |
653 } | 760 install_warnings_.push_back(InstallWarning(InstallWarning::FORMAT_TEXT, |
654 | 761 availability.message())); |
655 if (matches->GetSize() == 0) { | 762 to_remove.push_back(it->id()); |
656 *error = ErrorUtils::FormatErrorMessageUTF16( | 763 continue; |
657 errors::kInvalidMatchCount, | 764 } |
658 base::IntToString(definition_index)); | 765 |
659 return false; | 766 if (it->id() == APIPermission::kExperimental) { |
660 } | 767 if (!CanSpecifyExperimentalPermission()) { |
661 for (size_t j = 0; j < matches->GetSize(); ++j) { | 768 *error = ASCIIToUTF16(errors::kExperimentalFlagRequired); |
662 std::string match_str; | 769 return false; |
663 if (!matches->GetString(j, &match_str)) { | |
664 *error = ErrorUtils::FormatErrorMessageUTF16( | |
665 errors::kInvalidMatch, | |
666 base::IntToString(definition_index), | |
667 base::IntToString(j), | |
668 errors::kExpectString); | |
669 return false; | |
670 } | |
671 | |
672 URLPattern pattern(UserScript::kValidUserScriptSchemes); | |
673 if (CanExecuteScriptEverywhere()) | |
674 pattern.SetValidSchemes(URLPattern::SCHEME_ALL); | |
675 | |
676 URLPattern::ParseResult parse_result = pattern.Parse(match_str); | |
677 if (parse_result != URLPattern::PARSE_SUCCESS) { | |
678 *error = ErrorUtils::FormatErrorMessageUTF16( | |
679 errors::kInvalidMatch, | |
680 base::IntToString(definition_index), | |
681 base::IntToString(j), | |
682 URLPattern::GetParseResultString(parse_result)); | |
683 return false; | |
684 } | |
685 | |
686 if (pattern.MatchesScheme(chrome::kFileScheme) && | |
687 !CanExecuteScriptEverywhere()) { | |
688 wants_file_access_ = true; | |
689 if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { | |
690 pattern.SetValidSchemes( | |
691 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); | |
692 } | |
693 } | |
694 | |
695 result->add_url_pattern(pattern); | |
696 } | |
697 | |
698 // exclude_matches | |
699 if (content_script->HasKey(keys::kExcludeMatches)) { // optional | |
700 const ListValue* exclude_matches = NULL; | |
701 if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) { | |
702 *error = ErrorUtils::FormatErrorMessageUTF16( | |
703 errors::kInvalidExcludeMatches, | |
704 base::IntToString(definition_index)); | |
705 return false; | |
706 } | |
707 | |
708 for (size_t j = 0; j < exclude_matches->GetSize(); ++j) { | |
709 std::string match_str; | |
710 if (!exclude_matches->GetString(j, &match_str)) { | |
711 *error = ErrorUtils::FormatErrorMessageUTF16( | |
712 errors::kInvalidExcludeMatch, | |
713 base::IntToString(definition_index), | |
714 base::IntToString(j), | |
715 errors::kExpectString); | |
716 return false; | |
717 } | |
718 | |
719 URLPattern pattern(UserScript::kValidUserScriptSchemes); | |
720 if (CanExecuteScriptEverywhere()) | |
721 pattern.SetValidSchemes(URLPattern::SCHEME_ALL); | |
722 URLPattern::ParseResult parse_result = pattern.Parse(match_str); | |
723 if (parse_result != URLPattern::PARSE_SUCCESS) { | |
724 *error = ErrorUtils::FormatErrorMessageUTF16( | |
725 errors::kInvalidExcludeMatch, | |
726 base::IntToString(definition_index), base::IntToString(j), | |
727 URLPattern::GetParseResultString(parse_result)); | |
728 return false; | |
729 } | |
730 | |
731 result->add_exclude_url_pattern(pattern); | |
732 } | |
733 } | |
734 | |
735 // include/exclude globs (mostly for Greasemonkey compatibility) | |
736 if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs, | |
737 error, &UserScript::add_glob, result)) { | |
738 return false; | |
739 } | |
740 | |
741 if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs, | |
742 error, &UserScript::add_exclude_glob, result)) { | |
743 return false; | |
744 } | |
745 | |
746 // js and css keys | |
747 const ListValue* js = NULL; | |
748 if (content_script->HasKey(keys::kJs) && | |
749 !content_script->GetList(keys::kJs, &js)) { | |
750 *error = ErrorUtils::FormatErrorMessageUTF16( | |
751 errors::kInvalidJsList, | |
752 base::IntToString(definition_index)); | |
753 return false; | |
754 } | |
755 | |
756 const ListValue* css = NULL; | |
757 if (content_script->HasKey(keys::kCss) && | |
758 !content_script->GetList(keys::kCss, &css)) { | |
759 *error = ErrorUtils:: | |
760 FormatErrorMessageUTF16(errors::kInvalidCssList, | |
761 base::IntToString(definition_index)); | |
762 return false; | |
763 } | |
764 | |
765 // The manifest needs to have at least one js or css user script definition. | |
766 if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) { | |
767 *error = ErrorUtils::FormatErrorMessageUTF16( | |
768 errors::kMissingFile, | |
769 base::IntToString(definition_index)); | |
770 return false; | |
771 } | |
772 | |
773 if (js) { | |
774 for (size_t script_index = 0; script_index < js->GetSize(); | |
775 ++script_index) { | |
776 const Value* value; | |
777 std::string relative; | |
778 if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) { | |
779 *error = ErrorUtils::FormatErrorMessageUTF16( | |
780 errors::kInvalidJs, | |
781 base::IntToString(definition_index), | |
782 base::IntToString(script_index)); | |
783 return false; | |
784 } | |
785 GURL url = GetResourceURL(relative); | |
786 ExtensionResource resource = GetResource(relative); | |
787 result->js_scripts().push_back(UserScript::File( | |
788 resource.extension_root(), resource.relative_path(), url)); | |
789 } | |
790 } | |
791 | |
792 if (css) { | |
793 for (size_t script_index = 0; script_index < css->GetSize(); | |
794 ++script_index) { | |
795 const Value* value; | |
796 std::string relative; | |
797 if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) { | |
798 *error = ErrorUtils::FormatErrorMessageUTF16( | |
799 errors::kInvalidCss, | |
800 base::IntToString(definition_index), | |
801 base::IntToString(script_index)); | |
802 return false; | |
803 } | |
804 GURL url = GetResourceURL(relative); | |
805 ExtensionResource resource = GetResource(relative); | |
806 result->css_scripts().push_back(UserScript::File( | |
807 resource.extension_root(), resource.relative_path(), url)); | |
808 } | |
809 } | |
810 | |
811 return true; | |
812 } | |
813 | |
814 bool Extension::LoadGlobsHelper( | |
815 const DictionaryValue* content_script, | |
816 int content_script_index, | |
817 const char* globs_property_name, | |
818 string16* error, | |
819 void(UserScript::*add_method)(const std::string& glob), | |
820 UserScript* instance) { | |
821 if (!content_script->HasKey(globs_property_name)) | |
822 return true; // they are optional | |
823 | |
824 const ListValue* list = NULL; | |
825 if (!content_script->GetList(globs_property_name, &list)) { | |
826 *error = ErrorUtils::FormatErrorMessageUTF16( | |
827 errors::kInvalidGlobList, | |
828 base::IntToString(content_script_index), | |
829 globs_property_name); | |
830 return false; | |
831 } | |
832 | |
833 for (size_t i = 0; i < list->GetSize(); ++i) { | |
834 std::string glob; | |
835 if (!list->GetString(i, &glob)) { | |
836 *error = ErrorUtils::FormatErrorMessageUTF16( | |
837 errors::kInvalidGlob, | |
838 base::IntToString(content_script_index), | |
839 globs_property_name, | |
840 base::IntToString(i)); | |
841 return false; | |
842 } | |
843 | |
844 (instance->*add_method)(glob); | |
845 } | |
846 | |
847 return true; | |
848 } | |
849 | |
850 scoped_ptr<Extension::ActionInfo> Extension::LoadExtensionActionInfoHelper( | |
851 const DictionaryValue* extension_action, | |
852 ActionInfo::Type action_type, | |
853 string16* error) { | |
854 scoped_ptr<ActionInfo> result(new ActionInfo()); | |
855 | |
856 if (manifest_version_ == 1) { | |
857 // kPageActionIcons is obsolete, and used by very few extensions. Continue | |
858 // loading it, but only take the first icon as the default_icon path. | |
859 const ListValue* icons = NULL; | |
860 if (extension_action->HasKey(keys::kPageActionIcons) && | |
861 extension_action->GetList(keys::kPageActionIcons, &icons)) { | |
862 for (ListValue::const_iterator iter = icons->begin(); | |
863 iter != icons->end(); ++iter) { | |
864 std::string path; | |
865 if (!(*iter)->GetAsString(&path) || !NormalizeAndValidatePath(&path)) { | |
866 *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); | |
867 return scoped_ptr<ActionInfo>(); | |
868 } | 770 } |
869 | 771 } |
870 result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, path); | 772 } |
871 break; | 773 |
872 } | 774 // Remove permissions that are not available to this extension. |
873 } | 775 for (std::vector<APIPermission::ID>::const_iterator it = to_remove.begin(); |
874 | 776 it != to_remove.end(); ++it) { |
875 std::string id; | 777 api_permissions->erase(*it); |
876 if (extension_action->HasKey(keys::kPageActionId)) { | 778 } |
877 if (!extension_action->GetString(keys::kPageActionId, &id)) { | 779 |
878 *error = ASCIIToUTF16(errors::kInvalidPageActionId); | 780 // Parse host pattern permissions. |
879 return scoped_ptr<ActionInfo>(); | 781 const int kAllowedSchemes = CanExecuteScriptEverywhere() ? |
880 } | 782 URLPattern::SCHEME_ALL : kValidHostPermissionSchemes; |
881 result->id = id; | 783 |
882 } | 784 for (std::vector<std::string>::const_iterator it = host_data.begin(); |
883 } | 785 it != host_data.end(); ++it) { |
884 | 786 const std::string& permission_str = *it; |
885 // Read the page action |default_icon| (optional). | 787 |
886 // The |default_icon| value can be either dictionary {icon size -> icon path} | 788 // Check if it's a host pattern permission. |
887 // or non empty string value. | 789 URLPattern pattern = URLPattern(kAllowedSchemes); |
888 if (extension_action->HasKey(keys::kPageActionDefaultIcon)) { | 790 URLPattern::ParseResult parse_result = pattern.Parse(permission_str); |
889 const DictionaryValue* icons_value = NULL; | 791 if (parse_result == URLPattern::PARSE_SUCCESS) { |
890 std::string default_icon; | 792 if (!CanSpecifyHostPermission(pattern, *api_permissions)) { |
891 if (extension_action->GetDictionary(keys::kPageActionDefaultIcon, | 793 *error = ErrorUtils::FormatErrorMessageUTF16( |
892 &icons_value)) { | 794 errors::kInvalidPermissionScheme, permission_str); |
893 if (!LoadIconsFromDictionary(icons_value, | 795 return false; |
894 extension_misc::kExtensionActionIconSizes, | 796 } |
895 extension_misc::kNumExtensionActionIconSizes, | 797 |
896 &result->default_icon, | 798 // The path component is not used for host permissions, so we force it |
897 error)) { | 799 // to match all paths. |
898 return scoped_ptr<ActionInfo>(); | 800 pattern.SetPath("/*"); |
899 } | 801 |
900 } else if (extension_action->GetString(keys::kPageActionDefaultIcon, | 802 if (pattern.MatchesScheme(chrome::kFileScheme) && |
901 &default_icon) && | 803 !CanExecuteScriptEverywhere()) { |
902 NormalizeAndValidatePath(&default_icon)) { | 804 wants_file_access_ = true; |
903 result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, | 805 if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { |
904 default_icon); | 806 pattern.SetValidSchemes( |
905 } else { | 807 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); |
906 *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); | 808 } |
907 return scoped_ptr<ActionInfo>(); | 809 } |
908 } | 810 |
909 } | 811 host_permissions->AddPattern(pattern); |
910 | 812 continue; |
911 // Read the page action title from |default_title| if present, |name| if not | 813 } |
912 // (both optional). | 814 |
913 if (extension_action->HasKey(keys::kPageActionDefaultTitle)) { | 815 // It's probably an unknown API permission. Do not throw an error so |
914 if (!extension_action->GetString(keys::kPageActionDefaultTitle, | 816 // extensions can retain backwards compatability (http://crbug.com/42742). |
915 &result->default_title)) { | 817 install_warnings_.push_back(InstallWarning( |
916 *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle); | 818 InstallWarning::FORMAT_TEXT, |
917 return scoped_ptr<ActionInfo>(); | 819 base::StringPrintf( |
918 } | 820 "Permission '%s' is unknown or URL pattern is malformed.", |
919 } else if (manifest_version_ == 1 && extension_action->HasKey(keys::kName)) { | 821 permission_str.c_str()))); |
920 if (!extension_action->GetString(keys::kName, &result->default_title)) { | 822 } |
921 *error = ASCIIToUTF16(errors::kInvalidPageActionName); | 823 } |
922 return scoped_ptr<ActionInfo>(); | 824 return true; |
923 } | 825 } |
924 } | 826 |
925 | 827 bool Extension::HasAPIPermission(APIPermission::ID permission) const { |
926 // Read the action's |popup| (optional). | 828 base::AutoLock auto_lock(runtime_data_lock_); |
927 const char* popup_key = NULL; | 829 return runtime_data_.GetActivePermissions()->HasAPIPermission(permission); |
928 if (extension_action->HasKey(keys::kPageActionDefaultPopup)) | 830 } |
929 popup_key = keys::kPageActionDefaultPopup; | 831 |
930 | 832 bool Extension::HasAPIPermission(const std::string& function_name) const { |
931 if (manifest_version_ == 1 && | 833 base::AutoLock auto_lock(runtime_data_lock_); |
932 extension_action->HasKey(keys::kPageActionPopup)) { | 834 return runtime_data_.GetActivePermissions()-> |
933 if (popup_key) { | 835 HasAccessToFunction(function_name); |
934 *error = ErrorUtils::FormatErrorMessageUTF16( | 836 } |
935 errors::kInvalidPageActionOldAndNewKeys, | 837 |
936 keys::kPageActionDefaultPopup, | 838 bool Extension::HasAPIPermissionForTab(int tab_id, |
937 keys::kPageActionPopup); | 839 APIPermission::ID permission) const { |
938 return scoped_ptr<ActionInfo>(); | 840 base::AutoLock auto_lock(runtime_data_lock_); |
939 } | 841 if (runtime_data_.GetActivePermissions()->HasAPIPermission(permission)) |
940 popup_key = keys::kPageActionPopup; | 842 return true; |
941 } | 843 scoped_refptr<const PermissionSet> tab_specific_permissions = |
942 | 844 runtime_data_.GetTabSpecificPermissions(tab_id); |
943 if (popup_key) { | 845 return tab_specific_permissions.get() && |
944 const DictionaryValue* popup = NULL; | 846 tab_specific_permissions->HasAPIPermission(permission); |
945 std::string url_str; | 847 } |
946 | 848 |
947 if (extension_action->GetString(popup_key, &url_str)) { | 849 bool Extension::CheckAPIPermissionWithParam(APIPermission::ID permission, |
948 // On success, |url_str| is set. Nothing else to do. | 850 const APIPermission::CheckParam* param) const { |
949 } else if (manifest_version_ == 1 && | 851 base::AutoLock auto_lock(runtime_data_lock_); |
950 extension_action->GetDictionary(popup_key, &popup)) { | 852 return runtime_data_.GetActivePermissions()-> |
951 if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) { | 853 CheckAPIPermissionWithParam(permission, param); |
952 *error = ErrorUtils::FormatErrorMessageUTF16( | 854 } |
953 errors::kInvalidPageActionPopupPath, "<missing>"); | 855 |
954 return scoped_ptr<ActionInfo>(); | 856 const URLPatternSet& Extension::GetEffectiveHostPermissions() const { |
955 } | 857 base::AutoLock auto_lock(runtime_data_lock_); |
956 } else { | 858 return runtime_data_.GetActivePermissions()->effective_hosts(); |
957 *error = ASCIIToUTF16(errors::kInvalidPageActionPopup); | 859 } |
958 return scoped_ptr<ActionInfo>(); | 860 |
959 } | 861 bool Extension::CanSilentlyIncreasePermissions() const { |
960 | 862 return location() != INTERNAL; |
961 if (!url_str.empty()) { | 863 } |
962 // An empty string is treated as having no popup. | 864 |
963 result->default_popup_url = GetResourceURL(url_str); | 865 bool Extension::HasHostPermission(const GURL& url) const { |
964 if (!result->default_popup_url.is_valid()) { | 866 if (url.SchemeIs(chrome::kChromeUIScheme) && |
965 *error = ErrorUtils::FormatErrorMessageUTF16( | 867 url.host() != chrome::kChromeUIFaviconHost && |
966 errors::kInvalidPageActionPopupPath, url_str); | 868 url.host() != chrome::kChromeUIThumbnailHost && |
967 return scoped_ptr<ActionInfo>(); | 869 location() != Extension::COMPONENT) { |
968 } | 870 return false; |
969 } else { | 871 } |
970 DCHECK(result->default_popup_url.is_empty()) | 872 |
971 << "Shouldn't be possible for the popup to be set."; | 873 base::AutoLock auto_lock(runtime_data_lock_); |
972 } | 874 return runtime_data_.GetActivePermissions()-> |
973 } | 875 HasExplicitAccessToOrigin(url); |
974 | 876 } |
975 return result.Pass(); | 877 |
| 878 bool Extension::HasEffectiveAccessToAllHosts() const { |
| 879 base::AutoLock auto_lock(runtime_data_lock_); |
| 880 return runtime_data_.GetActivePermissions()->HasEffectiveAccessToAllHosts(); |
| 881 } |
| 882 |
| 883 bool Extension::HasFullPermissions() const { |
| 884 base::AutoLock auto_lock(runtime_data_lock_); |
| 885 return runtime_data_.GetActivePermissions()->HasEffectiveFullAccess(); |
| 886 } |
| 887 |
| 888 PermissionMessages Extension::GetPermissionMessages() const { |
| 889 base::AutoLock auto_lock(runtime_data_lock_); |
| 890 if (IsTrustedId(id())) { |
| 891 return PermissionMessages(); |
| 892 } else { |
| 893 return runtime_data_.GetActivePermissions()->GetPermissionMessages( |
| 894 GetType()); |
| 895 } |
| 896 } |
| 897 |
| 898 std::vector<string16> Extension::GetPermissionMessageStrings() const { |
| 899 base::AutoLock auto_lock(runtime_data_lock_); |
| 900 if (IsTrustedId(id())) |
| 901 return std::vector<string16>(); |
| 902 else |
| 903 return runtime_data_.GetActivePermissions()->GetWarningMessages(GetType()); |
| 904 } |
| 905 |
| 906 bool Extension::ShouldSkipPermissionWarnings() const { |
| 907 return IsTrustedId(id()); |
| 908 } |
| 909 |
| 910 void Extension::SetActivePermissions( |
| 911 const PermissionSet* permissions) const { |
| 912 base::AutoLock auto_lock(runtime_data_lock_); |
| 913 runtime_data_.SetActivePermissions(permissions); |
| 914 } |
| 915 |
| 916 scoped_refptr<const PermissionSet> |
| 917 Extension::GetActivePermissions() const { |
| 918 base::AutoLock auto_lock(runtime_data_lock_); |
| 919 return runtime_data_.GetActivePermissions(); |
| 920 } |
| 921 |
| 922 bool Extension::ShowConfigureContextMenus() const { |
| 923 // Don't show context menu for component extensions. We might want to show |
| 924 // options for component extension button but now there is no component |
| 925 // extension with options. All other menu items like uninstall have |
| 926 // no sense for component extensions. |
| 927 return location() != Extension::COMPONENT; |
| 928 } |
| 929 |
| 930 GURL Extension::GetHomepageURL() const { |
| 931 if (homepage_url_.is_valid()) |
| 932 return homepage_url_; |
| 933 |
| 934 return UpdatesFromGallery() ? |
| 935 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id()) : GURL(); |
| 936 } |
| 937 |
| 938 std::set<FilePath> Extension::GetBrowserImages() const { |
| 939 std::set<FilePath> image_paths; |
| 940 // TODO(viettrungluu): These |FilePath::FromWStringHack(UTF8ToWide())| |
| 941 // indicate that we're doing something wrong. |
| 942 |
| 943 // Extension icons. |
| 944 for (ExtensionIconSet::IconMap::const_iterator iter = icons().map().begin(); |
| 945 iter != icons().map().end(); ++iter) { |
| 946 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); |
| 947 } |
| 948 |
| 949 // Theme images. |
| 950 DictionaryValue* theme_images = GetThemeImages(); |
| 951 if (theme_images) { |
| 952 for (DictionaryValue::key_iterator it = theme_images->begin_keys(); |
| 953 it != theme_images->end_keys(); ++it) { |
| 954 std::string val; |
| 955 if (theme_images->GetStringWithoutPathExpansion(*it, &val)) |
| 956 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(val))); |
| 957 } |
| 958 } |
| 959 |
| 960 if (page_action_info() && !page_action_info()->default_icon.empty()) { |
| 961 for (ExtensionIconSet::IconMap::const_iterator iter = |
| 962 page_action_info()->default_icon.map().begin(); |
| 963 iter != page_action_info()->default_icon.map().end(); |
| 964 ++iter) { |
| 965 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); |
| 966 } |
| 967 } |
| 968 |
| 969 if (browser_action_info() && !browser_action_info()->default_icon.empty()) { |
| 970 for (ExtensionIconSet::IconMap::const_iterator iter = |
| 971 browser_action_info()->default_icon.map().begin(); |
| 972 iter != browser_action_info()->default_icon.map().end(); |
| 973 ++iter) { |
| 974 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); |
| 975 } |
| 976 } |
| 977 |
| 978 return image_paths; |
| 979 } |
| 980 |
| 981 ExtensionResource Extension::GetIconResource( |
| 982 int size, ExtensionIconSet::MatchType match_type) const { |
| 983 std::string path = icons().Get(size, match_type); |
| 984 return path.empty() ? ExtensionResource() : GetResource(path); |
| 985 } |
| 986 |
| 987 GURL Extension::GetIconURL(int size, |
| 988 ExtensionIconSet::MatchType match_type) const { |
| 989 std::string path = icons().Get(size, match_type); |
| 990 return path.empty() ? GURL() : GetResourceURL(path); |
| 991 } |
| 992 |
| 993 GURL Extension::GetFullLaunchURL() const { |
| 994 return launch_local_path().empty() ? GURL(launch_web_url()) : |
| 995 url().Resolve(launch_local_path()); |
| 996 } |
| 997 |
| 998 void Extension::SetCachedImage(const ExtensionResource& source, |
| 999 const SkBitmap& image, |
| 1000 const gfx::Size& original_size) const { |
| 1001 DCHECK(source.extension_root() == path()); // The resource must come from |
| 1002 // this extension. |
| 1003 const FilePath& path = source.relative_path(); |
| 1004 gfx::Size actual_size(image.width(), image.height()); |
| 1005 std::string location; |
| 1006 if (actual_size != original_size) |
| 1007 location = SizeToString(actual_size); |
| 1008 image_cache_[ImageCacheKey(path, location)] = image; |
| 1009 } |
| 1010 |
| 1011 bool Extension::HasCachedImage(const ExtensionResource& source, |
| 1012 const gfx::Size& max_size) const { |
| 1013 DCHECK(source.extension_root() == path()); // The resource must come from |
| 1014 // this extension. |
| 1015 return GetCachedImageImpl(source, max_size) != NULL; |
| 1016 } |
| 1017 |
| 1018 SkBitmap Extension::GetCachedImage(const ExtensionResource& source, |
| 1019 const gfx::Size& max_size) const { |
| 1020 DCHECK(source.extension_root() == path()); // The resource must come from |
| 1021 // this extension. |
| 1022 SkBitmap* image = GetCachedImageImpl(source, max_size); |
| 1023 return image ? *image : SkBitmap(); |
| 1024 } |
| 1025 |
| 1026 bool Extension::CanExecuteScriptOnPage(const GURL& document_url, |
| 1027 const GURL& top_frame_url, |
| 1028 int tab_id, |
| 1029 const UserScript* script, |
| 1030 std::string* error) const { |
| 1031 base::AutoLock auto_lock(runtime_data_lock_); |
| 1032 // The gallery is special-cased as a restricted URL for scripting to prevent |
| 1033 // access to special JS bindings we expose to the gallery (and avoid things |
| 1034 // like extensions removing the "report abuse" link). |
| 1035 // TODO(erikkay): This seems like the wrong test. Shouldn't we we testing |
| 1036 // against the store app extent? |
| 1037 GURL store_url(extension_urls::GetWebstoreLaunchURL()); |
| 1038 if ((document_url.host() == store_url.host()) && |
| 1039 !CanExecuteScriptEverywhere() && |
| 1040 !CommandLine::ForCurrentProcess()->HasSwitch( |
| 1041 switches::kAllowScriptingGallery)) { |
| 1042 if (error) |
| 1043 *error = errors::kCannotScriptGallery; |
| 1044 return false; |
| 1045 } |
| 1046 |
| 1047 if (document_url.SchemeIs(chrome::kChromeUIScheme) && |
| 1048 !CanExecuteScriptEverywhere()) { |
| 1049 return false; |
| 1050 } |
| 1051 |
| 1052 if (top_frame_url.SchemeIs(extensions::kExtensionScheme) && |
| 1053 top_frame_url.GetOrigin() != |
| 1054 GetBaseURLFromExtensionId(id()).GetOrigin() && |
| 1055 !CanExecuteScriptEverywhere()) { |
| 1056 return false; |
| 1057 } |
| 1058 |
| 1059 // If a tab ID is specified, try the tab-specific permissions. |
| 1060 if (tab_id >= 0) { |
| 1061 scoped_refptr<const PermissionSet> tab_permissions = |
| 1062 runtime_data_.GetTabSpecificPermissions(tab_id); |
| 1063 if (tab_permissions.get() && |
| 1064 tab_permissions->explicit_hosts().MatchesSecurityOrigin(document_url)) { |
| 1065 return true; |
| 1066 } |
| 1067 } |
| 1068 |
| 1069 // If a script is specified, use its matches. |
| 1070 if (script) |
| 1071 return script->MatchesURL(document_url); |
| 1072 |
| 1073 // Otherwise, see if this extension has permission to execute script |
| 1074 // programmatically on pages. |
| 1075 if (runtime_data_.GetActivePermissions()->HasExplicitAccessToOrigin( |
| 1076 document_url)) { |
| 1077 return true; |
| 1078 } |
| 1079 |
| 1080 if (error) { |
| 1081 *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, |
| 1082 document_url.spec()); |
| 1083 } |
| 1084 |
| 1085 return false; |
| 1086 } |
| 1087 |
| 1088 bool Extension::CanExecuteScriptEverywhere() const { |
| 1089 if (location() == Extension::COMPONENT) |
| 1090 return true; |
| 1091 |
| 1092 ScriptingWhitelist* whitelist = ExtensionConfig::GetInstance()->whitelist(); |
| 1093 |
| 1094 for (ScriptingWhitelist::const_iterator it = whitelist->begin(); |
| 1095 it != whitelist->end(); ++it) { |
| 1096 if (id() == *it) { |
| 1097 return true; |
| 1098 } |
| 1099 } |
| 1100 |
| 1101 return false; |
| 1102 } |
| 1103 |
| 1104 bool Extension::CanCaptureVisiblePage(const GURL& page_url, |
| 1105 int tab_id, |
| 1106 std::string* error) const { |
| 1107 if (tab_id >= 0) { |
| 1108 scoped_refptr<const PermissionSet> tab_permissions = |
| 1109 GetTabSpecificPermissions(tab_id); |
| 1110 if (tab_permissions.get() && |
| 1111 tab_permissions->explicit_hosts().MatchesSecurityOrigin(page_url)) { |
| 1112 return true; |
| 1113 } |
| 1114 } |
| 1115 |
| 1116 if (HasHostPermission(page_url) || page_url.GetOrigin() == url()) |
| 1117 return true; |
| 1118 |
| 1119 if (error) { |
| 1120 *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, |
| 1121 page_url.spec()); |
| 1122 } |
| 1123 return false; |
| 1124 } |
| 1125 |
| 1126 bool Extension::UpdatesFromGallery() const { |
| 1127 return extension_urls::IsWebstoreUpdateUrl(update_url()); |
| 1128 } |
| 1129 |
| 1130 bool Extension::OverlapsWithOrigin(const GURL& origin) const { |
| 1131 if (url() == origin) |
| 1132 return true; |
| 1133 |
| 1134 if (web_extent().is_empty()) |
| 1135 return false; |
| 1136 |
| 1137 // Note: patterns and extents ignore port numbers. |
| 1138 URLPattern origin_only_pattern(kValidWebExtentSchemes); |
| 1139 if (!origin_only_pattern.SetScheme(origin.scheme())) |
| 1140 return false; |
| 1141 origin_only_pattern.SetHost(origin.host()); |
| 1142 origin_only_pattern.SetPath("/*"); |
| 1143 |
| 1144 URLPatternSet origin_only_pattern_list; |
| 1145 origin_only_pattern_list.AddPattern(origin_only_pattern); |
| 1146 |
| 1147 return web_extent().OverlapsWith(origin_only_pattern_list); |
| 1148 } |
| 1149 |
| 1150 Extension::SyncType Extension::GetSyncType() const { |
| 1151 if (!IsSyncable()) { |
| 1152 // We have a non-standard location. |
| 1153 return SYNC_TYPE_NONE; |
| 1154 } |
| 1155 |
| 1156 // Disallow extensions with non-gallery auto-update URLs for now. |
| 1157 // |
| 1158 // TODO(akalin): Relax this restriction once we've put in UI to |
| 1159 // approve synced extensions. |
| 1160 if (!update_url().is_empty() && !UpdatesFromGallery()) |
| 1161 return SYNC_TYPE_NONE; |
| 1162 |
| 1163 // Disallow extensions with native code plugins. |
| 1164 // |
| 1165 // TODO(akalin): Relax this restriction once we've put in UI to |
| 1166 // approve synced extensions. |
| 1167 if (!plugins().empty()) { |
| 1168 return SYNC_TYPE_NONE; |
| 1169 } |
| 1170 |
| 1171 switch (GetType()) { |
| 1172 case Extension::TYPE_EXTENSION: |
| 1173 return SYNC_TYPE_EXTENSION; |
| 1174 |
| 1175 case Extension::TYPE_USER_SCRIPT: |
| 1176 // We only want to sync user scripts with gallery update URLs. |
| 1177 if (UpdatesFromGallery()) |
| 1178 return SYNC_TYPE_EXTENSION; |
| 1179 else |
| 1180 return SYNC_TYPE_NONE; |
| 1181 |
| 1182 case Extension::TYPE_HOSTED_APP: |
| 1183 case Extension::TYPE_LEGACY_PACKAGED_APP: |
| 1184 case Extension::TYPE_PLATFORM_APP: |
| 1185 return SYNC_TYPE_APP; |
| 1186 |
| 1187 default: |
| 1188 return SYNC_TYPE_NONE; |
| 1189 } |
| 1190 } |
| 1191 |
| 1192 bool Extension::IsSyncable() const { |
| 1193 // TODO(akalin): Figure out if we need to allow some other types. |
| 1194 |
| 1195 // Default apps are not synced because otherwise they will pollute profiles |
| 1196 // that don't already have them. Specially, if a user doesn't have default |
| 1197 // apps, creates a new profile (which get default apps) and then enables sync |
| 1198 // for it, then their profile everywhere gets the default apps. |
| 1199 bool is_syncable = (location() == Extension::INTERNAL && |
| 1200 !was_installed_by_default()); |
| 1201 // Sync the chrome web store to maintain its position on the new tab page. |
| 1202 is_syncable |= (id() == extension_misc::kWebStoreAppId); |
| 1203 return is_syncable; |
| 1204 } |
| 1205 |
| 1206 bool Extension::RequiresSortOrdinal() const { |
| 1207 return is_app() && (display_in_launcher_ || display_in_new_tab_page_); |
| 1208 } |
| 1209 |
| 1210 bool Extension::ShouldDisplayInAppLauncher() const { |
| 1211 // Only apps should be displayed in the launcher. |
| 1212 return is_app() && display_in_launcher_; |
| 1213 } |
| 1214 |
| 1215 bool Extension::ShouldDisplayInNewTabPage() const { |
| 1216 // Only apps should be displayed on the NTP. |
| 1217 return is_app() && display_in_new_tab_page_; |
| 1218 } |
| 1219 |
| 1220 bool Extension::ShouldDisplayInExtensionSettings() const { |
| 1221 // Don't show for themes since the settings UI isn't really useful for them. |
| 1222 if (is_theme()) |
| 1223 return false; |
| 1224 |
| 1225 // Don't show component extensions because they are only extensions as an |
| 1226 // implementation detail of Chrome. |
| 1227 if (location() == Extension::COMPONENT && |
| 1228 !CommandLine::ForCurrentProcess()->HasSwitch( |
| 1229 switches::kShowComponentExtensionOptions)) { |
| 1230 return false; |
| 1231 } |
| 1232 |
| 1233 // Always show unpacked extensions and apps. |
| 1234 if (location() == Extension::LOAD) |
| 1235 return true; |
| 1236 |
| 1237 // Unless they are unpacked, never show hosted apps. Note: We intentionally |
| 1238 // show packaged apps and platform apps because there are some pieces of |
| 1239 // functionality that are only available in chrome://extensions/ but which |
| 1240 // are needed for packaged and platform apps. For example, inspecting |
| 1241 // background pages. See http://crbug.com/116134. |
| 1242 if (is_hosted_app()) |
| 1243 return false; |
| 1244 |
| 1245 return true; |
| 1246 } |
| 1247 |
| 1248 bool Extension::HasContentScriptAtURL(const GURL& url) const { |
| 1249 for (UserScriptList::const_iterator it = content_scripts_.begin(); |
| 1250 it != content_scripts_.end(); ++it) { |
| 1251 if (it->MatchesURL(url)) |
| 1252 return true; |
| 1253 } |
| 1254 return false; |
| 1255 } |
| 1256 |
| 1257 scoped_refptr<const PermissionSet> Extension::GetTabSpecificPermissions( |
| 1258 int tab_id) const { |
| 1259 base::AutoLock auto_lock(runtime_data_lock_); |
| 1260 return runtime_data_.GetTabSpecificPermissions(tab_id); |
| 1261 } |
| 1262 |
| 1263 void Extension::UpdateTabSpecificPermissions( |
| 1264 int tab_id, |
| 1265 scoped_refptr<const PermissionSet> permissions) const { |
| 1266 base::AutoLock auto_lock(runtime_data_lock_); |
| 1267 runtime_data_.UpdateTabSpecificPermissions(tab_id, permissions); |
| 1268 } |
| 1269 |
| 1270 void Extension::ClearTabSpecificPermissions(int tab_id) const { |
| 1271 base::AutoLock auto_lock(runtime_data_lock_); |
| 1272 runtime_data_.ClearTabSpecificPermissions(tab_id); |
| 1273 } |
| 1274 |
| 1275 Extension::Location Extension::location() const { |
| 1276 return manifest_->location(); |
| 1277 } |
| 1278 |
| 1279 const std::string& Extension::id() const { |
| 1280 return manifest_->extension_id(); |
| 1281 } |
| 1282 |
| 1283 const std::string Extension::VersionString() const { |
| 1284 return version()->GetString(); |
| 1285 } |
| 1286 |
| 1287 void Extension::AddInstallWarnings( |
| 1288 const InstallWarningVector& new_warnings) { |
| 1289 install_warnings_.insert(install_warnings_.end(), |
| 1290 new_warnings.begin(), new_warnings.end()); |
| 1291 } |
| 1292 |
| 1293 bool Extension::is_platform_app() const { |
| 1294 return manifest_->is_platform_app(); |
| 1295 } |
| 1296 |
| 1297 bool Extension::is_hosted_app() const { |
| 1298 return manifest()->is_hosted_app(); |
| 1299 } |
| 1300 |
| 1301 bool Extension::is_legacy_packaged_app() const { |
| 1302 return manifest()->is_legacy_packaged_app(); |
| 1303 } |
| 1304 |
| 1305 bool Extension::is_theme() const { |
| 1306 return manifest()->is_theme(); |
| 1307 } |
| 1308 |
| 1309 GURL Extension::GetBackgroundURL() const { |
| 1310 if (background_scripts_.empty()) |
| 1311 return background_url_; |
| 1312 return GetResourceURL(extension_filenames::kGeneratedBackgroundPageFilename); |
| 1313 } |
| 1314 |
| 1315 Extension::RuntimeData::RuntimeData() {} |
| 1316 Extension::RuntimeData::RuntimeData(const PermissionSet* active) |
| 1317 : active_permissions_(active) {} |
| 1318 Extension::RuntimeData::~RuntimeData() {} |
| 1319 |
| 1320 void Extension::RuntimeData::SetActivePermissions( |
| 1321 const PermissionSet* active) { |
| 1322 active_permissions_ = active; |
| 1323 } |
| 1324 |
| 1325 scoped_refptr<const PermissionSet> |
| 1326 Extension::RuntimeData::GetActivePermissions() const { |
| 1327 return active_permissions_; |
| 1328 } |
| 1329 |
| 1330 scoped_refptr<const PermissionSet> |
| 1331 Extension::RuntimeData::GetTabSpecificPermissions(int tab_id) const { |
| 1332 CHECK_GE(tab_id, 0); |
| 1333 TabPermissionsMap::const_iterator it = tab_specific_permissions_.find(tab_id); |
| 1334 return (it != tab_specific_permissions_.end()) ? it->second : NULL; |
| 1335 } |
| 1336 |
| 1337 void Extension::RuntimeData::UpdateTabSpecificPermissions( |
| 1338 int tab_id, |
| 1339 scoped_refptr<const PermissionSet> permissions) { |
| 1340 CHECK_GE(tab_id, 0); |
| 1341 if (tab_specific_permissions_.count(tab_id)) { |
| 1342 tab_specific_permissions_[tab_id] = PermissionSet::CreateUnion( |
| 1343 tab_specific_permissions_[tab_id], |
| 1344 permissions.get()); |
| 1345 } else { |
| 1346 tab_specific_permissions_[tab_id] = permissions; |
| 1347 } |
| 1348 } |
| 1349 |
| 1350 void Extension::RuntimeData::ClearTabSpecificPermissions(int tab_id) { |
| 1351 CHECK_GE(tab_id, 0); |
| 1352 tab_specific_permissions_.erase(tab_id); |
976 } | 1353 } |
977 | 1354 |
978 // static | 1355 // static |
979 bool Extension::InitExtensionID(extensions::Manifest* manifest, | 1356 bool Extension::InitExtensionID(extensions::Manifest* manifest, |
980 const FilePath& path, | 1357 const FilePath& path, |
981 const std::string& explicit_id, | 1358 const std::string& explicit_id, |
982 int creation_flags, | 1359 int creation_flags, |
983 string16* error) { | 1360 string16* error) { |
984 if (!explicit_id.empty()) { | 1361 if (!explicit_id.empty()) { |
985 manifest->set_extension_id(explicit_id); | 1362 manifest->set_extension_id(explicit_id); |
(...skipping 24 matching lines...) Expand all Loading... |
1010 std::string extension_id = GenerateIdForPath(path); | 1387 std::string extension_id = GenerateIdForPath(path); |
1011 if (extension_id.empty()) { | 1388 if (extension_id.empty()) { |
1012 NOTREACHED() << "Could not create ID from path."; | 1389 NOTREACHED() << "Could not create ID from path."; |
1013 return false; | 1390 return false; |
1014 } | 1391 } |
1015 manifest->set_extension_id(extension_id); | 1392 manifest->set_extension_id(extension_id); |
1016 return true; | 1393 return true; |
1017 } | 1394 } |
1018 } | 1395 } |
1019 | 1396 |
| 1397 // static |
| 1398 FilePath Extension::MaybeNormalizePath(const FilePath& path) { |
| 1399 #if defined(OS_WIN) |
| 1400 // Normalize any drive letter to upper-case. We do this for consistency with |
| 1401 // net_utils::FilePathToFileURL(), which does the same thing, to make string |
| 1402 // comparisons simpler. |
| 1403 std::wstring path_str = path.value(); |
| 1404 if (path_str.size() >= 2 && path_str[0] >= L'a' && path_str[0] <= L'z' && |
| 1405 path_str[1] == ':') |
| 1406 path_str[0] += ('A' - 'a'); |
| 1407 |
| 1408 return FilePath(path_str); |
| 1409 #else |
| 1410 return path; |
| 1411 #endif |
| 1412 } |
| 1413 |
| 1414 // static |
| 1415 bool Extension::IsTrustedId(const std::string& id) { |
| 1416 // See http://b/4946060 for more details. |
| 1417 return id == std::string("nckgahadagoaajjgafhacjanaoiihapd"); |
| 1418 } |
| 1419 |
| 1420 Extension::Extension(const FilePath& path, |
| 1421 scoped_ptr<extensions::Manifest> manifest) |
| 1422 : manifest_version_(0), |
| 1423 incognito_split_mode_(false), |
| 1424 offline_enabled_(false), |
| 1425 converted_from_user_script_(false), |
| 1426 background_page_is_persistent_(true), |
| 1427 allow_background_js_access_(true), |
| 1428 manifest_(manifest.release()), |
| 1429 is_storage_isolated_(false), |
| 1430 launch_container_(extension_misc::LAUNCH_TAB), |
| 1431 launch_width_(0), |
| 1432 launch_height_(0), |
| 1433 display_in_launcher_(true), |
| 1434 display_in_new_tab_page_(true), |
| 1435 wants_file_access_(false), |
| 1436 creation_flags_(0) { |
| 1437 DCHECK(path.empty() || path.IsAbsolute()); |
| 1438 path_ = MaybeNormalizePath(path); |
| 1439 } |
| 1440 |
| 1441 Extension::~Extension() { |
| 1442 } |
| 1443 |
| 1444 bool Extension::InitFromValue(int flags, string16* error) { |
| 1445 DCHECK(error); |
| 1446 |
| 1447 base::AutoLock auto_lock(runtime_data_lock_); |
| 1448 |
| 1449 // Initialize permissions with an empty, default permission set. |
| 1450 runtime_data_.SetActivePermissions(new PermissionSet()); |
| 1451 optional_permission_set_ = new PermissionSet(); |
| 1452 required_permission_set_ = new PermissionSet(); |
| 1453 |
| 1454 creation_flags_ = flags; |
| 1455 |
| 1456 // Important to load manifest version first because many other features |
| 1457 // depend on its value. |
| 1458 if (!LoadManifestVersion(error)) |
| 1459 return false; |
| 1460 |
| 1461 // Validate minimum Chrome version. We don't need to store this, since the |
| 1462 // extension is not valid if it is incorrect |
| 1463 if (!CheckMinimumChromeVersion(error)) |
| 1464 return false; |
| 1465 |
| 1466 if (!LoadRequiredFeatures(error)) |
| 1467 return false; |
| 1468 |
| 1469 // We don't need to validate because InitExtensionID already did that. |
| 1470 manifest_->GetString(keys::kPublicKey, &public_key_); |
| 1471 |
| 1472 extension_url_ = Extension::GetBaseURLFromExtensionId(id()); |
| 1473 |
| 1474 // Load App settings. LoadExtent at least has to be done before |
| 1475 // ParsePermissions(), because the valid permissions depend on what type of |
| 1476 // package this is. |
| 1477 if (is_app() && !LoadAppFeatures(error)) |
| 1478 return false; |
| 1479 |
| 1480 APIPermissionSet api_permissions; |
| 1481 URLPatternSet host_permissions; |
| 1482 if (!ParsePermissions(keys::kPermissions, |
| 1483 error, |
| 1484 &api_permissions, |
| 1485 &host_permissions)) { |
| 1486 return false; |
| 1487 } |
| 1488 |
| 1489 // TODO(jeremya/kalman) do this via the features system by exposing the |
| 1490 // app.window API to platform apps, with no dependency on any permissions. |
| 1491 // See http://crbug.com/120069. |
| 1492 if (is_platform_app()) { |
| 1493 api_permissions.insert(APIPermission::kAppCurrentWindowInternal); |
| 1494 api_permissions.insert(APIPermission::kAppRuntime); |
| 1495 api_permissions.insert(APIPermission::kAppWindow); |
| 1496 } |
| 1497 |
| 1498 if (from_webstore()) { |
| 1499 details_url_ = |
| 1500 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id()); |
| 1501 } |
| 1502 |
| 1503 APIPermissionSet optional_api_permissions; |
| 1504 URLPatternSet optional_host_permissions; |
| 1505 if (!ParsePermissions(keys::kOptionalPermissions, |
| 1506 error, |
| 1507 &optional_api_permissions, |
| 1508 &optional_host_permissions)) { |
| 1509 return false; |
| 1510 } |
| 1511 |
| 1512 if (!LoadAppIsolation(api_permissions, error)) |
| 1513 return false; |
| 1514 |
| 1515 if (!LoadSharedFeatures(api_permissions, error)) |
| 1516 return false; |
| 1517 |
| 1518 if (!LoadExtensionFeatures(&api_permissions, error)) |
| 1519 return false; |
| 1520 |
| 1521 if (!LoadThemeFeatures(error)) |
| 1522 return false; |
| 1523 |
| 1524 if (HasMultipleUISurfaces()) { |
| 1525 *error = ASCIIToUTF16(errors::kOneUISurfaceOnly); |
| 1526 return false; |
| 1527 } |
| 1528 |
| 1529 runtime_data_.SetActivePermissions(new PermissionSet( |
| 1530 this, api_permissions, host_permissions)); |
| 1531 required_permission_set_ = new PermissionSet( |
| 1532 this, api_permissions, host_permissions); |
| 1533 optional_permission_set_ = new PermissionSet( |
| 1534 optional_api_permissions, optional_host_permissions, URLPatternSet()); |
| 1535 |
| 1536 return true; |
| 1537 } |
| 1538 |
| 1539 bool Extension::LoadAppIsolation(const APIPermissionSet& api_permissions, |
| 1540 string16* error) { |
| 1541 // Platform apps always get isolated storage. |
| 1542 if (is_platform_app()) { |
| 1543 is_storage_isolated_ = true; |
| 1544 return true; |
| 1545 } |
| 1546 |
| 1547 // Other apps only get it if it is requested _and_ experimental APIs are |
| 1548 // enabled. |
| 1549 if (!api_permissions.count(APIPermission::kExperimental) || !is_app()) |
| 1550 return true; |
| 1551 |
| 1552 Value* tmp_isolation = NULL; |
| 1553 if (!manifest_->Get(keys::kIsolation, &tmp_isolation)) |
| 1554 return true; |
| 1555 |
| 1556 if (tmp_isolation->GetType() != Value::TYPE_LIST) { |
| 1557 *error = ASCIIToUTF16(errors::kInvalidIsolation); |
| 1558 return false; |
| 1559 } |
| 1560 |
| 1561 ListValue* isolation_list = static_cast<ListValue*>(tmp_isolation); |
| 1562 for (size_t i = 0; i < isolation_list->GetSize(); ++i) { |
| 1563 std::string isolation_string; |
| 1564 if (!isolation_list->GetString(i, &isolation_string)) { |
| 1565 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 1566 errors::kInvalidIsolationValue, |
| 1567 base::UintToString(i)); |
| 1568 return false; |
| 1569 } |
| 1570 |
| 1571 // Check for isolated storage. |
| 1572 if (isolation_string == values::kIsolatedStorage) { |
| 1573 is_storage_isolated_ = true; |
| 1574 } else { |
| 1575 DLOG(WARNING) << "Did not recognize isolation type: " << isolation_string; |
| 1576 } |
| 1577 } |
| 1578 return true; |
| 1579 } |
| 1580 |
1020 bool Extension::LoadRequiredFeatures(string16* error) { | 1581 bool Extension::LoadRequiredFeatures(string16* error) { |
1021 if (!LoadName(error) || | 1582 if (!LoadName(error) || |
1022 !LoadVersion(error)) | 1583 !LoadVersion(error)) |
1023 return false; | 1584 return false; |
1024 return true; | 1585 return true; |
1025 } | 1586 } |
1026 | 1587 |
1027 bool Extension::LoadName(string16* error) { | 1588 bool Extension::LoadName(string16* error) { |
1028 string16 localized_name; | 1589 string16 localized_name; |
1029 if (!manifest_->GetString(keys::kName, &localized_name)) { | 1590 if (!manifest_->GetString(keys::kName, &localized_name)) { |
1030 *error = ASCIIToUTF16(errors::kInvalidName); | 1591 *error = ASCIIToUTF16(errors::kInvalidName); |
1031 return false; | 1592 return false; |
1032 } | 1593 } |
1033 non_localized_name_ = UTF16ToUTF8(localized_name); | 1594 non_localized_name_ = UTF16ToUTF8(localized_name); |
1034 base::i18n::AdjustStringForLocaleDirection(&localized_name); | 1595 base::i18n::AdjustStringForLocaleDirection(&localized_name); |
1035 name_ = UTF16ToUTF8(localized_name); | 1596 name_ = UTF16ToUTF8(localized_name); |
1036 return true; | 1597 return true; |
1037 } | 1598 } |
1038 | 1599 |
1039 bool Extension::LoadDescription(string16* error) { | 1600 bool Extension::LoadVersion(string16* error) { |
1040 if (manifest_->HasKey(keys::kDescription) && | 1601 std::string version_str; |
1041 !manifest_->GetString(keys::kDescription, &description_)) { | 1602 if (!manifest_->GetString(keys::kVersion, &version_str)) { |
1042 *error = ASCIIToUTF16(errors::kInvalidDescription); | 1603 *error = ASCIIToUTF16(errors::kInvalidVersion); |
| 1604 return false; |
| 1605 } |
| 1606 version_.reset(new Version(version_str)); |
| 1607 if (!version_->IsValid() || version_->components().size() > 4) { |
| 1608 *error = ASCIIToUTF16(errors::kInvalidVersion); |
1043 return false; | 1609 return false; |
1044 } | 1610 } |
1045 return true; | 1611 return true; |
1046 } | 1612 } |
1047 | 1613 |
1048 bool Extension::LoadAppFeatures(string16* error) { | 1614 bool Extension::LoadAppFeatures(string16* error) { |
1049 if (!LoadExtent(keys::kWebURLs, &extent_, | 1615 if (!LoadExtent(keys::kWebURLs, &extent_, |
1050 errors::kInvalidWebURLs, errors::kInvalidWebURL, error) || | 1616 errors::kInvalidWebURLs, errors::kInvalidWebURL, error) || |
1051 !LoadLaunchURL(error) || | 1617 !LoadLaunchURL(error) || |
1052 !LoadLaunchContainer(error)) { | 1618 !LoadLaunchContainer(error)) { |
(...skipping 10 matching lines...) Expand all Loading... |
1063 *error = ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage); | 1629 *error = ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage); |
1064 return false; | 1630 return false; |
1065 } | 1631 } |
1066 } else { | 1632 } else { |
1067 // Inherit default from display_in_launcher property. | 1633 // Inherit default from display_in_launcher property. |
1068 display_in_new_tab_page_ = display_in_launcher_; | 1634 display_in_new_tab_page_ = display_in_launcher_; |
1069 } | 1635 } |
1070 return true; | 1636 return true; |
1071 } | 1637 } |
1072 | 1638 |
1073 bool Extension::LoadOAuth2Info(string16* error) { | |
1074 if (!manifest_->HasKey(keys::kOAuth2)) | |
1075 return true; | |
1076 | |
1077 if (!manifest_->GetString(keys::kOAuth2ClientId, &oauth2_info_.client_id) || | |
1078 oauth2_info_.client_id.empty()) { | |
1079 *error = ASCIIToUTF16(errors::kInvalidOAuth2ClientId); | |
1080 return false; | |
1081 } | |
1082 | |
1083 ListValue* list = NULL; | |
1084 if (!manifest_->GetList(keys::kOAuth2Scopes, &list)) { | |
1085 *error = ASCIIToUTF16(errors::kInvalidOAuth2Scopes); | |
1086 return false; | |
1087 } | |
1088 | |
1089 for (size_t i = 0; i < list->GetSize(); ++i) { | |
1090 std::string scope; | |
1091 if (!list->GetString(i, &scope)) { | |
1092 *error = ASCIIToUTF16(errors::kInvalidOAuth2Scopes); | |
1093 return false; | |
1094 } | |
1095 oauth2_info_.scopes.push_back(scope); | |
1096 } | |
1097 | |
1098 return true; | |
1099 } | |
1100 | |
1101 bool Extension::LoadExtent(const char* key, | 1639 bool Extension::LoadExtent(const char* key, |
1102 URLPatternSet* extent, | 1640 URLPatternSet* extent, |
1103 const char* list_error, | 1641 const char* list_error, |
1104 const char* value_error, | 1642 const char* value_error, |
1105 string16* error) { | 1643 string16* error) { |
1106 Value* temp_pattern_value = NULL; | 1644 Value* temp_pattern_value = NULL; |
1107 if (!manifest_->Get(key, &temp_pattern_value)) | 1645 if (!manifest_->Get(key, &temp_pattern_value)) |
1108 return true; | 1646 return true; |
1109 | 1647 |
1110 if (temp_pattern_value->GetType() != Value::TYPE_LIST) { | 1648 if (temp_pattern_value->GetType() != Value::TYPE_LIST) { |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1165 return false; | 1703 return false; |
1166 } | 1704 } |
1167 pattern.SetPath(pattern.path() + '*'); | 1705 pattern.SetPath(pattern.path() + '*'); |
1168 | 1706 |
1169 extent->AddPattern(pattern); | 1707 extent->AddPattern(pattern); |
1170 } | 1708 } |
1171 | 1709 |
1172 return true; | 1710 return true; |
1173 } | 1711 } |
1174 | 1712 |
| 1713 bool Extension::LoadLaunchContainer(string16* error) { |
| 1714 Value* tmp_launcher_container = NULL; |
| 1715 if (!manifest_->Get(keys::kLaunchContainer, &tmp_launcher_container)) |
| 1716 return true; |
| 1717 |
| 1718 std::string launch_container_string; |
| 1719 if (!tmp_launcher_container->GetAsString(&launch_container_string)) { |
| 1720 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); |
| 1721 return false; |
| 1722 } |
| 1723 |
| 1724 if (launch_container_string == values::kLaunchContainerPanel) { |
| 1725 launch_container_ = extension_misc::LAUNCH_PANEL; |
| 1726 } else if (launch_container_string == values::kLaunchContainerTab) { |
| 1727 launch_container_ = extension_misc::LAUNCH_TAB; |
| 1728 } else { |
| 1729 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); |
| 1730 return false; |
| 1731 } |
| 1732 |
| 1733 bool can_specify_initial_size = |
| 1734 launch_container_ == extension_misc::LAUNCH_PANEL || |
| 1735 launch_container_ == extension_misc::LAUNCH_WINDOW; |
| 1736 |
| 1737 // Validate the container width if present. |
| 1738 if (!ReadLaunchDimension(manifest_.get(), |
| 1739 keys::kLaunchWidth, |
| 1740 &launch_width_, |
| 1741 can_specify_initial_size, |
| 1742 error)) { |
| 1743 return false; |
| 1744 } |
| 1745 |
| 1746 // Validate container height if present. |
| 1747 if (!ReadLaunchDimension(manifest_.get(), |
| 1748 keys::kLaunchHeight, |
| 1749 &launch_height_, |
| 1750 can_specify_initial_size, |
| 1751 error)) { |
| 1752 return false; |
| 1753 } |
| 1754 |
| 1755 return true; |
| 1756 } |
| 1757 |
1175 bool Extension::LoadLaunchURL(string16* error) { | 1758 bool Extension::LoadLaunchURL(string16* error) { |
1176 Value* temp = NULL; | 1759 Value* temp = NULL; |
1177 | 1760 |
1178 // launch URL can be either local (to chrome-extension:// root) or an absolute | 1761 // launch URL can be either local (to chrome-extension:// root) or an absolute |
1179 // web URL. | 1762 // web URL. |
1180 if (manifest_->Get(keys::kLaunchLocalPath, &temp)) { | 1763 if (manifest_->Get(keys::kLaunchLocalPath, &temp)) { |
1181 if (manifest_->Get(keys::kLaunchWebURL, NULL)) { | 1764 if (manifest_->Get(keys::kLaunchWebURL, NULL)) { |
1182 *error = ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive); | 1765 *error = ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive); |
1183 return false; | 1766 return false; |
1184 } | 1767 } |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1277 } | 1860 } |
1278 } else if (id() == extension_misc::kChromeAppId) { | 1861 } else if (id() == extension_misc::kChromeAppId) { |
1279 // Override launch url to new tab. | 1862 // Override launch url to new tab. |
1280 launch_web_url_ = chrome::kChromeUINewTabURL; | 1863 launch_web_url_ = chrome::kChromeUINewTabURL; |
1281 extent_.ClearPatterns(); | 1864 extent_.ClearPatterns(); |
1282 } | 1865 } |
1283 | 1866 |
1284 return true; | 1867 return true; |
1285 } | 1868 } |
1286 | 1869 |
1287 bool Extension::LoadLaunchContainer(string16* error) { | |
1288 Value* tmp_launcher_container = NULL; | |
1289 if (!manifest_->Get(keys::kLaunchContainer, &tmp_launcher_container)) | |
1290 return true; | |
1291 | |
1292 std::string launch_container_string; | |
1293 if (!tmp_launcher_container->GetAsString(&launch_container_string)) { | |
1294 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); | |
1295 return false; | |
1296 } | |
1297 | |
1298 if (launch_container_string == values::kLaunchContainerPanel) { | |
1299 launch_container_ = extension_misc::LAUNCH_PANEL; | |
1300 } else if (launch_container_string == values::kLaunchContainerTab) { | |
1301 launch_container_ = extension_misc::LAUNCH_TAB; | |
1302 } else { | |
1303 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); | |
1304 return false; | |
1305 } | |
1306 | |
1307 bool can_specify_initial_size = | |
1308 launch_container_ == extension_misc::LAUNCH_PANEL || | |
1309 launch_container_ == extension_misc::LAUNCH_WINDOW; | |
1310 | |
1311 // Validate the container width if present. | |
1312 if (!ReadLaunchDimension(manifest_.get(), | |
1313 keys::kLaunchWidth, | |
1314 &launch_width_, | |
1315 can_specify_initial_size, | |
1316 error)) { | |
1317 return false; | |
1318 } | |
1319 | |
1320 // Validate container height if present. | |
1321 if (!ReadLaunchDimension(manifest_.get(), | |
1322 keys::kLaunchHeight, | |
1323 &launch_height_, | |
1324 can_specify_initial_size, | |
1325 error)) { | |
1326 return false; | |
1327 } | |
1328 | |
1329 return true; | |
1330 } | |
1331 | |
1332 bool Extension::LoadSharedFeatures( | 1870 bool Extension::LoadSharedFeatures( |
1333 const APIPermissionSet& api_permissions, | 1871 const APIPermissionSet& api_permissions, |
1334 string16* error) { | 1872 string16* error) { |
1335 if (!LoadDescription(error) || | 1873 if (!LoadDescription(error) || |
1336 !LoadHomepageURL(error) || | 1874 !LoadHomepageURL(error) || |
1337 !LoadUpdateURL(error) || | 1875 !LoadUpdateURL(error) || |
1338 !LoadIcons(error) || | 1876 !LoadIcons(error) || |
1339 !LoadCommands(error) || | 1877 !LoadCommands(error) || |
1340 !LoadPlugins(error) || | 1878 !LoadPlugins(error) || |
1341 !LoadNaClModules(error) || | 1879 !LoadNaClModules(error) || |
1342 !LoadWebAccessibleResources(error) || | 1880 !LoadWebAccessibleResources(error) || |
1343 !LoadSandboxedPages(error) || | 1881 !LoadSandboxedPages(error) || |
1344 !LoadRequirements(error) || | 1882 !LoadRequirements(error) || |
1345 !LoadDefaultLocale(error) || | 1883 !LoadDefaultLocale(error) || |
1346 !LoadOfflineEnabled(error) || | 1884 !LoadOfflineEnabled(error) || |
1347 !LoadOptionsPage(error) || | 1885 !LoadOptionsPage(error) || |
1348 // LoadBackgroundScripts() must be called before LoadBackgroundPage(). | 1886 // LoadBackgroundScripts() must be called before LoadBackgroundPage(). |
1349 !LoadBackgroundScripts(error) || | 1887 !LoadBackgroundScripts(error) || |
1350 !LoadBackgroundPage(api_permissions, error) || | 1888 !LoadBackgroundPage(api_permissions, error) || |
1351 !LoadBackgroundPersistent(api_permissions, error) || | 1889 !LoadBackgroundPersistent(api_permissions, error) || |
1352 !LoadBackgroundAllowJSAccess(api_permissions, error) || | 1890 !LoadBackgroundAllowJSAccess(api_permissions, error) || |
1353 !LoadWebIntentServices(error) || | 1891 !LoadWebIntentServices(error) || |
1354 !LoadOAuth2Info(error)) | 1892 !LoadOAuth2Info(error)) |
1355 return false; | 1893 return false; |
1356 | 1894 |
1357 return true; | 1895 return true; |
1358 } | 1896 } |
1359 | 1897 |
1360 bool Extension::LoadVersion(string16* error) { | 1898 bool Extension::LoadDescription(string16* error) { |
1361 std::string version_str; | 1899 if (manifest_->HasKey(keys::kDescription) && |
1362 if (!manifest_->GetString(keys::kVersion, &version_str)) { | 1900 !manifest_->GetString(keys::kDescription, &description_)) { |
1363 *error = ASCIIToUTF16(errors::kInvalidVersion); | 1901 *error = ASCIIToUTF16(errors::kInvalidDescription); |
1364 return false; | |
1365 } | |
1366 version_.reset(new Version(version_str)); | |
1367 if (!version_->IsValid() || version_->components().size() > 4) { | |
1368 *error = ASCIIToUTF16(errors::kInvalidVersion); | |
1369 return false; | 1902 return false; |
1370 } | 1903 } |
1371 return true; | 1904 return true; |
1372 } | 1905 } |
1373 | 1906 |
1374 bool Extension::LoadManifestVersion(string16* error) { | 1907 bool Extension::LoadManifestVersion(string16* error) { |
1375 // Get the original value out of the dictionary so that we can validate it | 1908 // Get the original value out of the dictionary so that we can validate it |
1376 // more strictly. | 1909 // more strictly. |
1377 if (manifest_->value()->HasKey(keys::kManifestVersion)) { | 1910 if (manifest_->value()->HasKey(keys::kManifestVersion)) { |
1378 int manifest_version = 1; | 1911 int manifest_version = 1; |
(...skipping 1060 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2439 return false; | 2972 return false; |
2440 } | 2973 } |
2441 | 2974 |
2442 browser_action_info_ = LoadExtensionActionInfoHelper( | 2975 browser_action_info_ = LoadExtensionActionInfoHelper( |
2443 browser_action_value, Extension::ActionInfo::TYPE_BROWSER, error); | 2976 browser_action_value, Extension::ActionInfo::TYPE_BROWSER, error); |
2444 if (!browser_action_info_.get()) | 2977 if (!browser_action_info_.get()) |
2445 return false; // Failed to parse browser action definition. | 2978 return false; // Failed to parse browser action definition. |
2446 return true; | 2979 return true; |
2447 } | 2980 } |
2448 | 2981 |
2449 bool Extension::LoadSystemIndicator(APIPermissionSet* api_permissions, | |
2450 string16* error) { | |
2451 if (!manifest_->HasKey(keys::kSystemIndicator)) { | |
2452 // There was no manifest entry for the system indicator. | |
2453 return true; | |
2454 } | |
2455 | |
2456 DictionaryValue* system_indicator_value = NULL; | |
2457 if (!manifest_->GetDictionary(keys::kSystemIndicator, | |
2458 &system_indicator_value)) { | |
2459 *error = ASCIIToUTF16(errors::kInvalidSystemIndicator); | |
2460 return false; | |
2461 } | |
2462 | |
2463 system_indicator_info_ = LoadExtensionActionInfoHelper( | |
2464 system_indicator_value, | |
2465 Extension::ActionInfo::TYPE_SYSTEM_INDICATOR, | |
2466 error); | |
2467 | |
2468 if (!system_indicator_info_.get()) { | |
2469 return false; | |
2470 } | |
2471 | |
2472 // Because the manifest was successfully parsed, auto-grant the permission. | |
2473 // TODO(dewittj) Add this for all extension action APIs. | |
2474 api_permissions->insert(APIPermission::kSystemIndicator); | |
2475 | |
2476 return true; | |
2477 } | |
2478 | |
2479 bool Extension::LoadScriptBadge(string16* error) { | 2982 bool Extension::LoadScriptBadge(string16* error) { |
2480 if (manifest_->HasKey(keys::kScriptBadge)) { | 2983 if (manifest_->HasKey(keys::kScriptBadge)) { |
2481 if (!FeatureSwitch::script_badges()->IsEnabled()) { | 2984 if (!FeatureSwitch::script_badges()->IsEnabled()) { |
2482 // So as to not confuse developers if they specify a script badge section | 2985 // So as to not confuse developers if they specify a script badge section |
2483 // in the manifest, show a warning if the script badge declaration isn't | 2986 // in the manifest, show a warning if the script badge declaration isn't |
2484 // going to have any effect. | 2987 // going to have any effect. |
2485 install_warnings_.push_back( | 2988 install_warnings_.push_back( |
2486 InstallWarning(InstallWarning::FORMAT_TEXT, | 2989 InstallWarning(InstallWarning::FORMAT_TEXT, |
2487 errors::kScriptBadgeRequiresFlag)); | 2990 errors::kScriptBadgeRequiresFlag)); |
2488 } | 2991 } |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2525 std::string path = icons().Get(extension_misc::kScriptBadgeIconSizes[i], | 3028 std::string path = icons().Get(extension_misc::kScriptBadgeIconSizes[i], |
2526 ExtensionIconSet::MATCH_BIGGER); | 3029 ExtensionIconSet::MATCH_BIGGER); |
2527 if (!path.empty()) | 3030 if (!path.empty()) |
2528 script_badge_info_->default_icon.Add( | 3031 script_badge_info_->default_icon.Add( |
2529 extension_misc::kScriptBadgeIconSizes[i], path); | 3032 extension_misc::kScriptBadgeIconSizes[i], path); |
2530 } | 3033 } |
2531 | 3034 |
2532 return true; | 3035 return true; |
2533 } | 3036 } |
2534 | 3037 |
| 3038 bool Extension::LoadSystemIndicator(APIPermissionSet* api_permissions, |
| 3039 string16* error) { |
| 3040 if (!manifest_->HasKey(keys::kSystemIndicator)) { |
| 3041 // There was no manifest entry for the system indicator. |
| 3042 return true; |
| 3043 } |
| 3044 |
| 3045 DictionaryValue* system_indicator_value = NULL; |
| 3046 if (!manifest_->GetDictionary(keys::kSystemIndicator, |
| 3047 &system_indicator_value)) { |
| 3048 *error = ASCIIToUTF16(errors::kInvalidSystemIndicator); |
| 3049 return false; |
| 3050 } |
| 3051 |
| 3052 system_indicator_info_ = LoadExtensionActionInfoHelper( |
| 3053 system_indicator_value, |
| 3054 Extension::ActionInfo::TYPE_SYSTEM_INDICATOR, |
| 3055 error); |
| 3056 |
| 3057 if (!system_indicator_info_.get()) { |
| 3058 return false; |
| 3059 } |
| 3060 |
| 3061 // Because the manifest was successfully parsed, auto-grant the permission. |
| 3062 // TODO(dewittj) Add this for all extension action APIs. |
| 3063 api_permissions->insert(APIPermission::kSystemIndicator); |
| 3064 |
| 3065 return true; |
| 3066 } |
| 3067 |
2535 bool Extension::LoadFileBrowserHandlers(string16* error) { | 3068 bool Extension::LoadFileBrowserHandlers(string16* error) { |
2536 if (!manifest_->HasKey(keys::kFileBrowserHandlers)) | 3069 if (!manifest_->HasKey(keys::kFileBrowserHandlers)) |
2537 return true; | 3070 return true; |
2538 ListValue* file_browser_handlers_value = NULL; | 3071 ListValue* file_browser_handlers_value = NULL; |
2539 if (!manifest_->GetList(keys::kFileBrowserHandlers, | 3072 if (!manifest_->GetList(keys::kFileBrowserHandlers, |
2540 &file_browser_handlers_value)) { | 3073 &file_browser_handlers_value)) { |
2541 *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler); | 3074 *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler); |
2542 return false; | 3075 return false; |
2543 } | 3076 } |
2544 file_browser_handlers_.reset( | 3077 file_browser_handlers_.reset( |
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2879 // TODO(abarth): Should we continue to let extensions override the | 3412 // TODO(abarth): Should we continue to let extensions override the |
2880 // default Content-Security-Policy? | 3413 // default Content-Security-Policy? |
2881 content_security_policy_ = is_platform_app() ? | 3414 content_security_policy_ = is_platform_app() ? |
2882 kDefaultPlatformAppContentSecurityPolicy : | 3415 kDefaultPlatformAppContentSecurityPolicy : |
2883 kDefaultContentSecurityPolicy; | 3416 kDefaultContentSecurityPolicy; |
2884 CHECK(ContentSecurityPolicyIsSecure(content_security_policy_, GetType())); | 3417 CHECK(ContentSecurityPolicyIsSecure(content_security_policy_, GetType())); |
2885 } | 3418 } |
2886 return true; | 3419 return true; |
2887 } | 3420 } |
2888 | 3421 |
2889 bool Extension::LoadAppIsolation(const APIPermissionSet& api_permissions, | |
2890 string16* error) { | |
2891 // Platform apps always get isolated storage. | |
2892 if (is_platform_app()) { | |
2893 is_storage_isolated_ = true; | |
2894 return true; | |
2895 } | |
2896 | |
2897 // Other apps only get it if it is requested _and_ experimental APIs are | |
2898 // enabled. | |
2899 if (!api_permissions.count(APIPermission::kExperimental) || !is_app()) | |
2900 return true; | |
2901 | |
2902 Value* tmp_isolation = NULL; | |
2903 if (!manifest_->Get(keys::kIsolation, &tmp_isolation)) | |
2904 return true; | |
2905 | |
2906 if (tmp_isolation->GetType() != Value::TYPE_LIST) { | |
2907 *error = ASCIIToUTF16(errors::kInvalidIsolation); | |
2908 return false; | |
2909 } | |
2910 | |
2911 ListValue* isolation_list = static_cast<ListValue*>(tmp_isolation); | |
2912 for (size_t i = 0; i < isolation_list->GetSize(); ++i) { | |
2913 std::string isolation_string; | |
2914 if (!isolation_list->GetString(i, &isolation_string)) { | |
2915 *error = ErrorUtils::FormatErrorMessageUTF16( | |
2916 errors::kInvalidIsolationValue, | |
2917 base::UintToString(i)); | |
2918 return false; | |
2919 } | |
2920 | |
2921 // Check for isolated storage. | |
2922 if (isolation_string == values::kIsolatedStorage) { | |
2923 is_storage_isolated_ = true; | |
2924 } else { | |
2925 DLOG(WARNING) << "Did not recognize isolation type: " << isolation_string; | |
2926 } | |
2927 } | |
2928 return true; | |
2929 } | |
2930 | |
2931 bool Extension::LoadThemeFeatures(string16* error) { | 3422 bool Extension::LoadThemeFeatures(string16* error) { |
2932 if (!manifest_->HasKey(keys::kTheme)) | 3423 if (!manifest_->HasKey(keys::kTheme)) |
2933 return true; | 3424 return true; |
2934 DictionaryValue* theme_value = NULL; | 3425 DictionaryValue* theme_value = NULL; |
2935 if (!manifest_->GetDictionary(keys::kTheme, &theme_value)) { | 3426 if (!manifest_->GetDictionary(keys::kTheme, &theme_value)) { |
2936 *error = ASCIIToUTF16(errors::kInvalidTheme); | 3427 *error = ASCIIToUTF16(errors::kInvalidTheme); |
2937 return false; | 3428 return false; |
2938 } | 3429 } |
2939 if (!LoadThemeImages(theme_value, error)) | 3430 if (!LoadThemeImages(theme_value, error)) |
2940 return false; | 3431 return false; |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3024 bool Extension::LoadThemeDisplayProperties(const DictionaryValue* theme_value, | 3515 bool Extension::LoadThemeDisplayProperties(const DictionaryValue* theme_value, |
3025 string16* error) { | 3516 string16* error) { |
3026 const DictionaryValue* display_properties_value = NULL; | 3517 const DictionaryValue* display_properties_value = NULL; |
3027 if (theme_value->GetDictionary(keys::kThemeDisplayProperties, | 3518 if (theme_value->GetDictionary(keys::kThemeDisplayProperties, |
3028 &display_properties_value)) { | 3519 &display_properties_value)) { |
3029 theme_display_properties_.reset( | 3520 theme_display_properties_.reset( |
3030 display_properties_value->DeepCopy()); | 3521 display_properties_value->DeepCopy()); |
3031 } | 3522 } |
3032 return true; | 3523 return true; |
3033 } | 3524 } |
3034 | |
3035 // static | |
3036 bool Extension::IsTrustedId(const std::string& id) { | |
3037 // See http://b/4946060 for more details. | |
3038 return id == std::string("nckgahadagoaajjgafhacjanaoiihapd"); | |
3039 } | |
3040 | |
3041 Extension::Extension(const FilePath& path, | |
3042 scoped_ptr<extensions::Manifest> manifest) | |
3043 : manifest_version_(0), | |
3044 incognito_split_mode_(false), | |
3045 offline_enabled_(false), | |
3046 converted_from_user_script_(false), | |
3047 background_page_is_persistent_(true), | |
3048 allow_background_js_access_(true), | |
3049 manifest_(manifest.release()), | |
3050 is_storage_isolated_(false), | |
3051 launch_container_(extension_misc::LAUNCH_TAB), | |
3052 launch_width_(0), | |
3053 launch_height_(0), | |
3054 display_in_launcher_(true), | |
3055 display_in_new_tab_page_(true), | |
3056 wants_file_access_(false), | |
3057 creation_flags_(0) { | |
3058 DCHECK(path.empty() || path.IsAbsolute()); | |
3059 path_ = MaybeNormalizePath(path); | |
3060 } | |
3061 | |
3062 Extension::~Extension() { | |
3063 } | |
3064 | |
3065 ExtensionResource Extension::GetResource( | |
3066 const std::string& relative_path) const { | |
3067 std::string new_path = relative_path; | |
3068 // We have some legacy data where resources have leading slashes. | |
3069 // See: http://crbug.com/121164 | |
3070 if (!new_path.empty() && new_path.at(0) == '/') | |
3071 new_path.erase(0, 1); | |
3072 #if defined(OS_POSIX) | |
3073 FilePath relative_file_path(new_path); | |
3074 #elif defined(OS_WIN) | |
3075 FilePath relative_file_path(UTF8ToWide(new_path)); | |
3076 #endif | |
3077 ExtensionResource r(id(), path(), relative_file_path); | |
3078 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { | |
3079 r.set_follow_symlinks_anywhere(); | |
3080 } | |
3081 return r; | |
3082 } | |
3083 | |
3084 ExtensionResource Extension::GetResource( | |
3085 const FilePath& relative_file_path) const { | |
3086 ExtensionResource r(id(), path(), relative_file_path); | |
3087 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { | |
3088 r.set_follow_symlinks_anywhere(); | |
3089 } | |
3090 return r; | |
3091 } | |
3092 | |
3093 // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a | |
3094 // util class in base: | |
3095 // http://code.google.com/p/chromium/issues/detail?id=13572 | |
3096 bool Extension::ParsePEMKeyBytes(const std::string& input, | |
3097 std::string* output) { | |
3098 DCHECK(output); | |
3099 if (!output) | |
3100 return false; | |
3101 if (input.length() == 0) | |
3102 return false; | |
3103 | |
3104 std::string working = input; | |
3105 if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) { | |
3106 working = CollapseWhitespaceASCII(working, true); | |
3107 size_t header_pos = working.find(kKeyInfoEndMarker, | |
3108 sizeof(kKeyBeginHeaderMarker) - 1); | |
3109 if (header_pos == std::string::npos) | |
3110 return false; | |
3111 size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1; | |
3112 size_t end_pos = working.rfind(kKeyBeginFooterMarker); | |
3113 if (end_pos == std::string::npos) | |
3114 return false; | |
3115 if (start_pos >= end_pos) | |
3116 return false; | |
3117 | |
3118 working = working.substr(start_pos, end_pos - start_pos); | |
3119 if (working.length() == 0) | |
3120 return false; | |
3121 } | |
3122 | |
3123 return base::Base64Decode(working, output); | |
3124 } | |
3125 | |
3126 bool Extension::ProducePEM(const std::string& input, std::string* output) { | |
3127 DCHECK(output); | |
3128 return (input.length() == 0) ? false : base::Base64Encode(input, output); | |
3129 } | |
3130 | |
3131 bool Extension::FormatPEMForFileOutput(const std::string& input, | |
3132 std::string* output, | |
3133 bool is_public) { | |
3134 DCHECK(output); | |
3135 if (input.length() == 0) | |
3136 return false; | |
3137 *output = ""; | |
3138 output->append(kKeyBeginHeaderMarker); | |
3139 output->append(" "); | |
3140 output->append(is_public ? kPublic : kPrivate); | |
3141 output->append(" "); | |
3142 output->append(kKeyInfoEndMarker); | |
3143 output->append("\n"); | |
3144 for (size_t i = 0; i < input.length(); ) { | |
3145 int slice = std::min<int>(input.length() - i, kPEMOutputColumns); | |
3146 output->append(input.substr(i, slice)); | |
3147 output->append("\n"); | |
3148 i += slice; | |
3149 } | |
3150 output->append(kKeyBeginFooterMarker); | |
3151 output->append(" "); | |
3152 output->append(is_public ? kPublic : kPrivate); | |
3153 output->append(" "); | |
3154 output->append(kKeyInfoEndMarker); | |
3155 output->append("\n"); | |
3156 | |
3157 return true; | |
3158 } | |
3159 | |
3160 // static | |
3161 void Extension::DecodeIcon(const Extension* extension, | |
3162 int preferred_icon_size, | |
3163 ExtensionIconSet::MatchType match_type, | |
3164 scoped_ptr<SkBitmap>* result) { | |
3165 std::string path = extension->icons().Get(preferred_icon_size, match_type); | |
3166 int size = extension->icons().GetIconSizeFromPath(path); | |
3167 ExtensionResource icon_resource = extension->GetResource(path); | |
3168 DecodeIconFromPath(icon_resource.GetFilePath(), size, result); | |
3169 } | |
3170 | |
3171 // static | |
3172 void Extension::DecodeIcon(const Extension* extension, | |
3173 int icon_size, | |
3174 scoped_ptr<SkBitmap>* result) { | |
3175 DecodeIcon(extension, icon_size, ExtensionIconSet::MATCH_EXACTLY, result); | |
3176 } | |
3177 | |
3178 // static | |
3179 void Extension::DecodeIconFromPath(const FilePath& icon_path, | |
3180 int icon_size, | |
3181 scoped_ptr<SkBitmap>* result) { | |
3182 if (icon_path.empty()) | |
3183 return; | |
3184 | |
3185 std::string file_contents; | |
3186 if (!file_util::ReadFileToString(icon_path, &file_contents)) { | |
3187 DLOG(ERROR) << "Could not read icon file: " << icon_path.LossyDisplayName(); | |
3188 return; | |
3189 } | |
3190 | |
3191 // Decode the image using WebKit's image decoder. | |
3192 const unsigned char* data = | |
3193 reinterpret_cast<const unsigned char*>(file_contents.data()); | |
3194 webkit_glue::ImageDecoder decoder; | |
3195 scoped_ptr<SkBitmap> decoded(new SkBitmap()); | |
3196 *decoded = decoder.Decode(data, file_contents.length()); | |
3197 if (decoded->empty()) { | |
3198 DLOG(ERROR) << "Could not decode icon file: " | |
3199 << icon_path.LossyDisplayName(); | |
3200 return; | |
3201 } | |
3202 | |
3203 if (decoded->width() != icon_size || decoded->height() != icon_size) { | |
3204 DLOG(ERROR) << "Icon file has unexpected size: " | |
3205 << base::IntToString(decoded->width()) << "x" | |
3206 << base::IntToString(decoded->height()); | |
3207 return; | |
3208 } | |
3209 | |
3210 result->swap(decoded); | |
3211 } | |
3212 | |
3213 // static | |
3214 const gfx::ImageSkia& Extension::GetDefaultIcon(bool is_app) { | |
3215 int id = is_app ? IDR_APP_DEFAULT_ICON : IDR_EXTENSION_DEFAULT_ICON; | |
3216 return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id); | |
3217 } | |
3218 | |
3219 // static | |
3220 GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { | |
3221 return GURL(std::string(extensions::kExtensionScheme) + | |
3222 content::kStandardSchemeSeparator + extension_id + "/"); | |
3223 } | |
3224 | |
3225 bool Extension::InitFromValue(int flags, string16* error) { | |
3226 DCHECK(error); | |
3227 | |
3228 base::AutoLock auto_lock(runtime_data_lock_); | |
3229 | |
3230 // Initialize permissions with an empty, default permission set. | |
3231 runtime_data_.SetActivePermissions(new PermissionSet()); | |
3232 optional_permission_set_ = new PermissionSet(); | |
3233 required_permission_set_ = new PermissionSet(); | |
3234 | |
3235 creation_flags_ = flags; | |
3236 | |
3237 // Important to load manifest version first because many other features | |
3238 // depend on its value. | |
3239 if (!LoadManifestVersion(error)) | |
3240 return false; | |
3241 | |
3242 // Validate minimum Chrome version. We don't need to store this, since the | |
3243 // extension is not valid if it is incorrect | |
3244 if (!CheckMinimumChromeVersion(error)) | |
3245 return false; | |
3246 | |
3247 if (!LoadRequiredFeatures(error)) | |
3248 return false; | |
3249 | |
3250 // We don't need to validate because InitExtensionID already did that. | |
3251 manifest_->GetString(keys::kPublicKey, &public_key_); | |
3252 | |
3253 extension_url_ = Extension::GetBaseURLFromExtensionId(id()); | |
3254 | |
3255 // Load App settings. LoadExtent at least has to be done before | |
3256 // ParsePermissions(), because the valid permissions depend on what type of | |
3257 // package this is. | |
3258 if (is_app() && !LoadAppFeatures(error)) | |
3259 return false; | |
3260 | |
3261 APIPermissionSet api_permissions; | |
3262 URLPatternSet host_permissions; | |
3263 if (!ParsePermissions(keys::kPermissions, | |
3264 error, | |
3265 &api_permissions, | |
3266 &host_permissions)) { | |
3267 return false; | |
3268 } | |
3269 | |
3270 // TODO(jeremya/kalman) do this via the features system by exposing the | |
3271 // app.window API to platform apps, with no dependency on any permissions. | |
3272 // See http://crbug.com/120069. | |
3273 if (is_platform_app()) { | |
3274 api_permissions.insert(APIPermission::kAppCurrentWindowInternal); | |
3275 api_permissions.insert(APIPermission::kAppRuntime); | |
3276 api_permissions.insert(APIPermission::kAppWindow); | |
3277 } | |
3278 | |
3279 if (from_webstore()) { | |
3280 details_url_ = | |
3281 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id()); | |
3282 } | |
3283 | |
3284 APIPermissionSet optional_api_permissions; | |
3285 URLPatternSet optional_host_permissions; | |
3286 if (!ParsePermissions(keys::kOptionalPermissions, | |
3287 error, | |
3288 &optional_api_permissions, | |
3289 &optional_host_permissions)) { | |
3290 return false; | |
3291 } | |
3292 | |
3293 if (!LoadAppIsolation(api_permissions, error)) | |
3294 return false; | |
3295 | |
3296 if (!LoadSharedFeatures(api_permissions, error)) | |
3297 return false; | |
3298 | |
3299 if (!LoadExtensionFeatures(&api_permissions, error)) | |
3300 return false; | |
3301 | |
3302 if (!LoadThemeFeatures(error)) | |
3303 return false; | |
3304 | |
3305 if (HasMultipleUISurfaces()) { | |
3306 *error = ASCIIToUTF16(errors::kOneUISurfaceOnly); | |
3307 return false; | |
3308 } | |
3309 | |
3310 runtime_data_.SetActivePermissions(new PermissionSet( | |
3311 this, api_permissions, host_permissions)); | |
3312 required_permission_set_ = new PermissionSet( | |
3313 this, api_permissions, host_permissions); | |
3314 optional_permission_set_ = new PermissionSet( | |
3315 optional_api_permissions, optional_host_permissions, URLPatternSet()); | |
3316 | |
3317 return true; | |
3318 } | |
3319 | |
3320 GURL Extension::GetHomepageURL() const { | |
3321 if (homepage_url_.is_valid()) | |
3322 return homepage_url_; | |
3323 | |
3324 return UpdatesFromGallery() ? | |
3325 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id()) : GURL(); | |
3326 } | |
3327 | |
3328 std::set<FilePath> Extension::GetBrowserImages() const { | |
3329 std::set<FilePath> image_paths; | |
3330 // TODO(viettrungluu): These |FilePath::FromWStringHack(UTF8ToWide())| | |
3331 // indicate that we're doing something wrong. | |
3332 | |
3333 // Extension icons. | |
3334 for (ExtensionIconSet::IconMap::const_iterator iter = icons().map().begin(); | |
3335 iter != icons().map().end(); ++iter) { | |
3336 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); | |
3337 } | |
3338 | |
3339 // Theme images. | |
3340 DictionaryValue* theme_images = GetThemeImages(); | |
3341 if (theme_images) { | |
3342 for (DictionaryValue::key_iterator it = theme_images->begin_keys(); | |
3343 it != theme_images->end_keys(); ++it) { | |
3344 std::string val; | |
3345 if (theme_images->GetStringWithoutPathExpansion(*it, &val)) | |
3346 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(val))); | |
3347 } | |
3348 } | |
3349 | |
3350 if (page_action_info() && !page_action_info()->default_icon.empty()) { | |
3351 for (ExtensionIconSet::IconMap::const_iterator iter = | |
3352 page_action_info()->default_icon.map().begin(); | |
3353 iter != page_action_info()->default_icon.map().end(); | |
3354 ++iter) { | |
3355 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); | |
3356 } | |
3357 } | |
3358 | |
3359 if (browser_action_info() && !browser_action_info()->default_icon.empty()) { | |
3360 for (ExtensionIconSet::IconMap::const_iterator iter = | |
3361 browser_action_info()->default_icon.map().begin(); | |
3362 iter != browser_action_info()->default_icon.map().end(); | |
3363 ++iter) { | |
3364 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); | |
3365 } | |
3366 } | |
3367 | |
3368 return image_paths; | |
3369 } | |
3370 | |
3371 GURL Extension::GetFullLaunchURL() const { | |
3372 return launch_local_path().empty() ? GURL(launch_web_url()) : | |
3373 url().Resolve(launch_local_path()); | |
3374 } | |
3375 | |
3376 static std::string SizeToString(const gfx::Size& max_size) { | |
3377 return base::IntToString(max_size.width()) + "x" + | |
3378 base::IntToString(max_size.height()); | |
3379 } | |
3380 | |
3381 // static | |
3382 void Extension::SetScriptingWhitelist( | |
3383 const Extension::ScriptingWhitelist& whitelist) { | |
3384 ScriptingWhitelist* current_whitelist = | |
3385 ExtensionConfig::GetInstance()->whitelist(); | |
3386 current_whitelist->clear(); | |
3387 for (ScriptingWhitelist::const_iterator it = whitelist.begin(); | |
3388 it != whitelist.end(); ++it) { | |
3389 current_whitelist->push_back(*it); | |
3390 } | |
3391 } | |
3392 | |
3393 // static | |
3394 const Extension::ScriptingWhitelist* Extension::GetScriptingWhitelist() { | |
3395 return ExtensionConfig::GetInstance()->whitelist(); | |
3396 } | |
3397 | |
3398 void Extension::SetCachedImage(const ExtensionResource& source, | |
3399 const SkBitmap& image, | |
3400 const gfx::Size& original_size) const { | |
3401 DCHECK(source.extension_root() == path()); // The resource must come from | |
3402 // this extension. | |
3403 const FilePath& path = source.relative_path(); | |
3404 gfx::Size actual_size(image.width(), image.height()); | |
3405 std::string location; | |
3406 if (actual_size != original_size) | |
3407 location = SizeToString(actual_size); | |
3408 image_cache_[ImageCacheKey(path, location)] = image; | |
3409 } | |
3410 | |
3411 bool Extension::HasCachedImage(const ExtensionResource& source, | |
3412 const gfx::Size& max_size) const { | |
3413 DCHECK(source.extension_root() == path()); // The resource must come from | |
3414 // this extension. | |
3415 return GetCachedImageImpl(source, max_size) != NULL; | |
3416 } | |
3417 | |
3418 SkBitmap Extension::GetCachedImage(const ExtensionResource& source, | |
3419 const gfx::Size& max_size) const { | |
3420 DCHECK(source.extension_root() == path()); // The resource must come from | |
3421 // this extension. | |
3422 SkBitmap* image = GetCachedImageImpl(source, max_size); | |
3423 return image ? *image : SkBitmap(); | |
3424 } | |
3425 | |
3426 SkBitmap* Extension::GetCachedImageImpl(const ExtensionResource& source, | 3525 SkBitmap* Extension::GetCachedImageImpl(const ExtensionResource& source, |
3427 const gfx::Size& max_size) const { | 3526 const gfx::Size& max_size) const { |
3428 const FilePath& path = source.relative_path(); | 3527 const FilePath& path = source.relative_path(); |
3429 | 3528 |
3430 // Look for exact size match. | 3529 // Look for exact size match. |
3431 ImageCache::iterator i = image_cache_.find( | 3530 ImageCache::iterator i = image_cache_.find( |
3432 ImageCacheKey(path, SizeToString(max_size))); | 3531 ImageCacheKey(path, SizeToString(max_size))); |
3433 if (i != image_cache_.end()) | 3532 if (i != image_cache_.end()) |
3434 return &(i->second); | 3533 return &(i->second); |
3435 | 3534 |
3436 // If we have the original size version cached, return that if it's small | 3535 // If we have the original size version cached, return that if it's small |
3437 // enough. | 3536 // enough. |
3438 i = image_cache_.find(ImageCacheKey(path, std::string())); | 3537 i = image_cache_.find(ImageCacheKey(path, std::string())); |
3439 if (i != image_cache_.end()) { | 3538 if (i != image_cache_.end()) { |
3440 const SkBitmap& image = i->second; | 3539 const SkBitmap& image = i->second; |
3441 if (image.width() <= max_size.width() && | 3540 if (image.width() <= max_size.width() && |
3442 image.height() <= max_size.height()) { | 3541 image.height() <= max_size.height()) { |
3443 return &(i->second); | 3542 return &(i->second); |
3444 } | 3543 } |
3445 } | 3544 } |
3446 | 3545 |
3447 return NULL; | 3546 return NULL; |
3448 } | 3547 } |
3449 | 3548 |
3450 ExtensionResource Extension::GetIconResource( | 3549 // Helper method that loads a UserScript object from a dictionary in the |
3451 int size, ExtensionIconSet::MatchType match_type) const { | 3550 // content_script list of the manifest. |
3452 std::string path = icons().Get(size, match_type); | 3551 bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, |
3453 return path.empty() ? ExtensionResource() : GetResource(path); | 3552 int definition_index, |
3454 } | 3553 string16* error, |
3455 | 3554 UserScript* result) { |
3456 GURL Extension::GetIconURL(int size, | 3555 // run_at |
3457 ExtensionIconSet::MatchType match_type) const { | 3556 if (content_script->HasKey(keys::kRunAt)) { |
3458 std::string path = icons().Get(size, match_type); | 3557 std::string run_location; |
3459 return path.empty() ? GURL() : GetResourceURL(path); | 3558 if (!content_script->GetString(keys::kRunAt, &run_location)) { |
3460 } | 3559 *error = ErrorUtils::FormatErrorMessageUTF16( |
3461 | 3560 errors::kInvalidRunAt, |
3462 bool Extension::ParsePermissions(const char* key, | 3561 base::IntToString(definition_index)); |
3463 string16* error, | 3562 return false; |
3464 APIPermissionSet* api_permissions, | 3563 } |
3465 URLPatternSet* host_permissions) { | 3564 |
3466 if (manifest_->HasKey(key)) { | 3565 if (run_location == values::kRunAtDocumentStart) { |
3467 ListValue* permissions = NULL; | 3566 result->set_run_location(UserScript::DOCUMENT_START); |
3468 if (!manifest_->GetList(key, &permissions)) { | 3567 } else if (run_location == values::kRunAtDocumentEnd) { |
3469 *error = ErrorUtils::FormatErrorMessageUTF16( | 3568 result->set_run_location(UserScript::DOCUMENT_END); |
3470 errors::kInvalidPermissions, ""); | 3569 } else if (run_location == values::kRunAtDocumentIdle) { |
3471 return false; | 3570 result->set_run_location(UserScript::DOCUMENT_IDLE); |
3472 } | 3571 } else { |
3473 | 3572 *error = ErrorUtils::FormatErrorMessageUTF16( |
3474 // NOTE: We need to get the APIPermission before we check if features | 3573 errors::kInvalidRunAt, |
3475 // associated with them are available because the feature system does not | 3574 base::IntToString(definition_index)); |
3476 // know about aliases. | 3575 return false; |
3477 | 3576 } |
3478 std::vector<std::string> host_data; | 3577 } |
3479 if (!APIPermissionSet::ParseFromJSON(permissions, api_permissions, | 3578 |
3480 error, &host_data)) | 3579 // all frames |
3481 return false; | 3580 if (content_script->HasKey(keys::kAllFrames)) { |
3482 | 3581 bool all_frames = false; |
3483 // Verify feature availability of permissions. | 3582 if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) { |
3484 std::vector<APIPermission::ID> to_remove; | 3583 *error = ErrorUtils::FormatErrorMessageUTF16( |
3485 SimpleFeatureProvider* permission_features = | 3584 errors::kInvalidAllFrames, base::IntToString(definition_index)); |
3486 SimpleFeatureProvider::GetPermissionFeatures(); | 3585 return false; |
3487 for (APIPermissionSet::const_iterator it = api_permissions->begin(); | 3586 } |
3488 it != api_permissions->end(); ++it) { | 3587 result->set_match_all_frames(all_frames); |
3489 extensions::Feature* feature = | 3588 } |
3490 permission_features->GetFeature(it->name()); | 3589 |
3491 | 3590 // matches (required) |
3492 // The feature should exist since we just got an APIPermission | 3591 const ListValue* matches = NULL; |
3493 // for it. The two systems should be updated together whenever a | 3592 if (!content_script->GetList(keys::kMatches, &matches)) { |
3494 // permission is added. | 3593 *error = ErrorUtils::FormatErrorMessageUTF16( |
3495 CHECK(feature); | 3594 errors::kInvalidMatches, |
3496 | 3595 base::IntToString(definition_index)); |
3497 Feature::Availability availability = | 3596 return false; |
3498 feature->IsAvailableToManifest( | 3597 } |
3499 id(), | 3598 |
3500 GetType(), | 3599 if (matches->GetSize() == 0) { |
3501 Feature::ConvertLocation(location()), | 3600 *error = ErrorUtils::FormatErrorMessageUTF16( |
3502 manifest_version()); | 3601 errors::kInvalidMatchCount, |
3503 if (!availability.is_available()) { | 3602 base::IntToString(definition_index)); |
3504 // Don't fail, but warn the developer that the manifest contains | 3603 return false; |
3505 // unrecognized permissions. This may happen legitimately if the | 3604 } |
3506 // extensions requests platform- or channel-specific permissions. | 3605 for (size_t j = 0; j < matches->GetSize(); ++j) { |
3507 install_warnings_.push_back(InstallWarning(InstallWarning::FORMAT_TEXT, | 3606 std::string match_str; |
3508 availability.message())); | 3607 if (!matches->GetString(j, &match_str)) { |
3509 to_remove.push_back(it->id()); | 3608 *error = ErrorUtils::FormatErrorMessageUTF16( |
3510 continue; | 3609 errors::kInvalidMatch, |
3511 } | 3610 base::IntToString(definition_index), |
3512 | 3611 base::IntToString(j), |
3513 if (it->id() == APIPermission::kExperimental) { | 3612 errors::kExpectString); |
3514 if (!CanSpecifyExperimentalPermission()) { | 3613 return false; |
3515 *error = ASCIIToUTF16(errors::kExperimentalFlagRequired); | 3614 } |
3516 return false; | 3615 |
| 3616 URLPattern pattern(UserScript::kValidUserScriptSchemes); |
| 3617 if (CanExecuteScriptEverywhere()) |
| 3618 pattern.SetValidSchemes(URLPattern::SCHEME_ALL); |
| 3619 |
| 3620 URLPattern::ParseResult parse_result = pattern.Parse(match_str); |
| 3621 if (parse_result != URLPattern::PARSE_SUCCESS) { |
| 3622 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3623 errors::kInvalidMatch, |
| 3624 base::IntToString(definition_index), |
| 3625 base::IntToString(j), |
| 3626 URLPattern::GetParseResultString(parse_result)); |
| 3627 return false; |
| 3628 } |
| 3629 |
| 3630 if (pattern.MatchesScheme(chrome::kFileScheme) && |
| 3631 !CanExecuteScriptEverywhere()) { |
| 3632 wants_file_access_ = true; |
| 3633 if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { |
| 3634 pattern.SetValidSchemes( |
| 3635 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); |
| 3636 } |
| 3637 } |
| 3638 |
| 3639 result->add_url_pattern(pattern); |
| 3640 } |
| 3641 |
| 3642 // exclude_matches |
| 3643 if (content_script->HasKey(keys::kExcludeMatches)) { // optional |
| 3644 const ListValue* exclude_matches = NULL; |
| 3645 if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) { |
| 3646 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3647 errors::kInvalidExcludeMatches, |
| 3648 base::IntToString(definition_index)); |
| 3649 return false; |
| 3650 } |
| 3651 |
| 3652 for (size_t j = 0; j < exclude_matches->GetSize(); ++j) { |
| 3653 std::string match_str; |
| 3654 if (!exclude_matches->GetString(j, &match_str)) { |
| 3655 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3656 errors::kInvalidExcludeMatch, |
| 3657 base::IntToString(definition_index), |
| 3658 base::IntToString(j), |
| 3659 errors::kExpectString); |
| 3660 return false; |
| 3661 } |
| 3662 |
| 3663 URLPattern pattern(UserScript::kValidUserScriptSchemes); |
| 3664 if (CanExecuteScriptEverywhere()) |
| 3665 pattern.SetValidSchemes(URLPattern::SCHEME_ALL); |
| 3666 URLPattern::ParseResult parse_result = pattern.Parse(match_str); |
| 3667 if (parse_result != URLPattern::PARSE_SUCCESS) { |
| 3668 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3669 errors::kInvalidExcludeMatch, |
| 3670 base::IntToString(definition_index), base::IntToString(j), |
| 3671 URLPattern::GetParseResultString(parse_result)); |
| 3672 return false; |
| 3673 } |
| 3674 |
| 3675 result->add_exclude_url_pattern(pattern); |
| 3676 } |
| 3677 } |
| 3678 |
| 3679 // include/exclude globs (mostly for Greasemonkey compatibility) |
| 3680 if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs, |
| 3681 error, &UserScript::add_glob, result)) { |
| 3682 return false; |
| 3683 } |
| 3684 |
| 3685 if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs, |
| 3686 error, &UserScript::add_exclude_glob, result)) { |
| 3687 return false; |
| 3688 } |
| 3689 |
| 3690 // js and css keys |
| 3691 const ListValue* js = NULL; |
| 3692 if (content_script->HasKey(keys::kJs) && |
| 3693 !content_script->GetList(keys::kJs, &js)) { |
| 3694 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3695 errors::kInvalidJsList, |
| 3696 base::IntToString(definition_index)); |
| 3697 return false; |
| 3698 } |
| 3699 |
| 3700 const ListValue* css = NULL; |
| 3701 if (content_script->HasKey(keys::kCss) && |
| 3702 !content_script->GetList(keys::kCss, &css)) { |
| 3703 *error = ErrorUtils:: |
| 3704 FormatErrorMessageUTF16(errors::kInvalidCssList, |
| 3705 base::IntToString(definition_index)); |
| 3706 return false; |
| 3707 } |
| 3708 |
| 3709 // The manifest needs to have at least one js or css user script definition. |
| 3710 if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) { |
| 3711 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3712 errors::kMissingFile, |
| 3713 base::IntToString(definition_index)); |
| 3714 return false; |
| 3715 } |
| 3716 |
| 3717 if (js) { |
| 3718 for (size_t script_index = 0; script_index < js->GetSize(); |
| 3719 ++script_index) { |
| 3720 const Value* value; |
| 3721 std::string relative; |
| 3722 if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) { |
| 3723 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3724 errors::kInvalidJs, |
| 3725 base::IntToString(definition_index), |
| 3726 base::IntToString(script_index)); |
| 3727 return false; |
| 3728 } |
| 3729 GURL url = GetResourceURL(relative); |
| 3730 ExtensionResource resource = GetResource(relative); |
| 3731 result->js_scripts().push_back(UserScript::File( |
| 3732 resource.extension_root(), resource.relative_path(), url)); |
| 3733 } |
| 3734 } |
| 3735 |
| 3736 if (css) { |
| 3737 for (size_t script_index = 0; script_index < css->GetSize(); |
| 3738 ++script_index) { |
| 3739 const Value* value; |
| 3740 std::string relative; |
| 3741 if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) { |
| 3742 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3743 errors::kInvalidCss, |
| 3744 base::IntToString(definition_index), |
| 3745 base::IntToString(script_index)); |
| 3746 return false; |
| 3747 } |
| 3748 GURL url = GetResourceURL(relative); |
| 3749 ExtensionResource resource = GetResource(relative); |
| 3750 result->css_scripts().push_back(UserScript::File( |
| 3751 resource.extension_root(), resource.relative_path(), url)); |
| 3752 } |
| 3753 } |
| 3754 |
| 3755 return true; |
| 3756 } |
| 3757 |
| 3758 bool Extension::LoadGlobsHelper( |
| 3759 const DictionaryValue* content_script, |
| 3760 int content_script_index, |
| 3761 const char* globs_property_name, |
| 3762 string16* error, |
| 3763 void(UserScript::*add_method)(const std::string& glob), |
| 3764 UserScript* instance) { |
| 3765 if (!content_script->HasKey(globs_property_name)) |
| 3766 return true; // they are optional |
| 3767 |
| 3768 const ListValue* list = NULL; |
| 3769 if (!content_script->GetList(globs_property_name, &list)) { |
| 3770 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3771 errors::kInvalidGlobList, |
| 3772 base::IntToString(content_script_index), |
| 3773 globs_property_name); |
| 3774 return false; |
| 3775 } |
| 3776 |
| 3777 for (size_t i = 0; i < list->GetSize(); ++i) { |
| 3778 std::string glob; |
| 3779 if (!list->GetString(i, &glob)) { |
| 3780 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3781 errors::kInvalidGlob, |
| 3782 base::IntToString(content_script_index), |
| 3783 globs_property_name, |
| 3784 base::IntToString(i)); |
| 3785 return false; |
| 3786 } |
| 3787 |
| 3788 (instance->*add_method)(glob); |
| 3789 } |
| 3790 |
| 3791 return true; |
| 3792 } |
| 3793 |
| 3794 scoped_ptr<Extension::ActionInfo> Extension::LoadExtensionActionInfoHelper( |
| 3795 const DictionaryValue* extension_action, |
| 3796 ActionInfo::Type action_type, |
| 3797 string16* error) { |
| 3798 scoped_ptr<ActionInfo> result(new ActionInfo()); |
| 3799 |
| 3800 if (manifest_version_ == 1) { |
| 3801 // kPageActionIcons is obsolete, and used by very few extensions. Continue |
| 3802 // loading it, but only take the first icon as the default_icon path. |
| 3803 const ListValue* icons = NULL; |
| 3804 if (extension_action->HasKey(keys::kPageActionIcons) && |
| 3805 extension_action->GetList(keys::kPageActionIcons, &icons)) { |
| 3806 for (ListValue::const_iterator iter = icons->begin(); |
| 3807 iter != icons->end(); ++iter) { |
| 3808 std::string path; |
| 3809 if (!(*iter)->GetAsString(&path) || !NormalizeAndValidatePath(&path)) { |
| 3810 *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); |
| 3811 return scoped_ptr<ActionInfo>(); |
3517 } | 3812 } |
3518 } | 3813 |
3519 } | 3814 result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, path); |
3520 | 3815 break; |
3521 // Remove permissions that are not available to this extension. | 3816 } |
3522 for (std::vector<APIPermission::ID>::const_iterator it = to_remove.begin(); | 3817 } |
3523 it != to_remove.end(); ++it) { | 3818 |
3524 api_permissions->erase(*it); | 3819 std::string id; |
3525 } | 3820 if (extension_action->HasKey(keys::kPageActionId)) { |
3526 | 3821 if (!extension_action->GetString(keys::kPageActionId, &id)) { |
3527 // Parse host pattern permissions. | 3822 *error = ASCIIToUTF16(errors::kInvalidPageActionId); |
3528 const int kAllowedSchemes = CanExecuteScriptEverywhere() ? | 3823 return scoped_ptr<ActionInfo>(); |
3529 URLPattern::SCHEME_ALL : kValidHostPermissionSchemes; | 3824 } |
3530 | 3825 result->id = id; |
3531 for (std::vector<std::string>::const_iterator it = host_data.begin(); | 3826 } |
3532 it != host_data.end(); ++it) { | 3827 } |
3533 const std::string& permission_str = *it; | 3828 |
3534 | 3829 // Read the page action |default_icon| (optional). |
3535 // Check if it's a host pattern permission. | 3830 // The |default_icon| value can be either dictionary {icon size -> icon path} |
3536 URLPattern pattern = URLPattern(kAllowedSchemes); | 3831 // or non empty string value. |
3537 URLPattern::ParseResult parse_result = pattern.Parse(permission_str); | 3832 if (extension_action->HasKey(keys::kPageActionDefaultIcon)) { |
3538 if (parse_result == URLPattern::PARSE_SUCCESS) { | 3833 const DictionaryValue* icons_value = NULL; |
3539 if (!CanSpecifyHostPermission(pattern, *api_permissions)) { | 3834 std::string default_icon; |
3540 *error = ErrorUtils::FormatErrorMessageUTF16( | 3835 if (extension_action->GetDictionary(keys::kPageActionDefaultIcon, |
3541 errors::kInvalidPermissionScheme, permission_str); | 3836 &icons_value)) { |
3542 return false; | 3837 if (!LoadIconsFromDictionary(icons_value, |
3543 } | 3838 extension_misc::kExtensionActionIconSizes, |
3544 | 3839 extension_misc::kNumExtensionActionIconSizes, |
3545 // The path component is not used for host permissions, so we force it | 3840 &result->default_icon, |
3546 // to match all paths. | 3841 error)) { |
3547 pattern.SetPath("/*"); | 3842 return scoped_ptr<ActionInfo>(); |
3548 | 3843 } |
3549 if (pattern.MatchesScheme(chrome::kFileScheme) && | 3844 } else if (extension_action->GetString(keys::kPageActionDefaultIcon, |
3550 !CanExecuteScriptEverywhere()) { | 3845 &default_icon) && |
3551 wants_file_access_ = true; | 3846 NormalizeAndValidatePath(&default_icon)) { |
3552 if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { | 3847 result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, |
3553 pattern.SetValidSchemes( | 3848 default_icon); |
3554 pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); | 3849 } else { |
3555 } | 3850 *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); |
3556 } | 3851 return scoped_ptr<ActionInfo>(); |
3557 | 3852 } |
3558 host_permissions->AddPattern(pattern); | 3853 } |
3559 continue; | 3854 |
3560 } | 3855 // Read the page action title from |default_title| if present, |name| if not |
3561 | 3856 // (both optional). |
3562 // It's probably an unknown API permission. Do not throw an error so | 3857 if (extension_action->HasKey(keys::kPageActionDefaultTitle)) { |
3563 // extensions can retain backwards compatability (http://crbug.com/42742). | 3858 if (!extension_action->GetString(keys::kPageActionDefaultTitle, |
3564 install_warnings_.push_back(InstallWarning( | 3859 &result->default_title)) { |
3565 InstallWarning::FORMAT_TEXT, | 3860 *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle); |
3566 base::StringPrintf( | 3861 return scoped_ptr<ActionInfo>(); |
3567 "Permission '%s' is unknown or URL pattern is malformed.", | 3862 } |
3568 permission_str.c_str()))); | 3863 } else if (manifest_version_ == 1 && extension_action->HasKey(keys::kName)) { |
3569 } | 3864 if (!extension_action->GetString(keys::kName, &result->default_title)) { |
3570 } | 3865 *error = ASCIIToUTF16(errors::kInvalidPageActionName); |
| 3866 return scoped_ptr<ActionInfo>(); |
| 3867 } |
| 3868 } |
| 3869 |
| 3870 // Read the action's |popup| (optional). |
| 3871 const char* popup_key = NULL; |
| 3872 if (extension_action->HasKey(keys::kPageActionDefaultPopup)) |
| 3873 popup_key = keys::kPageActionDefaultPopup; |
| 3874 |
| 3875 if (manifest_version_ == 1 && |
| 3876 extension_action->HasKey(keys::kPageActionPopup)) { |
| 3877 if (popup_key) { |
| 3878 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3879 errors::kInvalidPageActionOldAndNewKeys, |
| 3880 keys::kPageActionDefaultPopup, |
| 3881 keys::kPageActionPopup); |
| 3882 return scoped_ptr<ActionInfo>(); |
| 3883 } |
| 3884 popup_key = keys::kPageActionPopup; |
| 3885 } |
| 3886 |
| 3887 if (popup_key) { |
| 3888 const DictionaryValue* popup = NULL; |
| 3889 std::string url_str; |
| 3890 |
| 3891 if (extension_action->GetString(popup_key, &url_str)) { |
| 3892 // On success, |url_str| is set. Nothing else to do. |
| 3893 } else if (manifest_version_ == 1 && |
| 3894 extension_action->GetDictionary(popup_key, &popup)) { |
| 3895 if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) { |
| 3896 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3897 errors::kInvalidPageActionPopupPath, "<missing>"); |
| 3898 return scoped_ptr<ActionInfo>(); |
| 3899 } |
| 3900 } else { |
| 3901 *error = ASCIIToUTF16(errors::kInvalidPageActionPopup); |
| 3902 return scoped_ptr<ActionInfo>(); |
| 3903 } |
| 3904 |
| 3905 if (!url_str.empty()) { |
| 3906 // An empty string is treated as having no popup. |
| 3907 result->default_popup_url = GetResourceURL(url_str); |
| 3908 if (!result->default_popup_url.is_valid()) { |
| 3909 *error = ErrorUtils::FormatErrorMessageUTF16( |
| 3910 errors::kInvalidPageActionPopupPath, url_str); |
| 3911 return scoped_ptr<ActionInfo>(); |
| 3912 } |
| 3913 } else { |
| 3914 DCHECK(result->default_popup_url.is_empty()) |
| 3915 << "Shouldn't be possible for the popup to be set."; |
| 3916 } |
| 3917 } |
| 3918 |
| 3919 return result.Pass(); |
| 3920 } |
| 3921 |
| 3922 bool Extension::LoadOAuth2Info(string16* error) { |
| 3923 if (!manifest_->HasKey(keys::kOAuth2)) |
| 3924 return true; |
| 3925 |
| 3926 if (!manifest_->GetString(keys::kOAuth2ClientId, &oauth2_info_.client_id) || |
| 3927 oauth2_info_.client_id.empty()) { |
| 3928 *error = ASCIIToUTF16(errors::kInvalidOAuth2ClientId); |
| 3929 return false; |
| 3930 } |
| 3931 |
| 3932 ListValue* list = NULL; |
| 3933 if (!manifest_->GetList(keys::kOAuth2Scopes, &list)) { |
| 3934 *error = ASCIIToUTF16(errors::kInvalidOAuth2Scopes); |
| 3935 return false; |
| 3936 } |
| 3937 |
| 3938 for (size_t i = 0; i < list->GetSize(); ++i) { |
| 3939 std::string scope; |
| 3940 if (!list->GetString(i, &scope)) { |
| 3941 *error = ASCIIToUTF16(errors::kInvalidOAuth2Scopes); |
| 3942 return false; |
| 3943 } |
| 3944 oauth2_info_.scopes.push_back(scope); |
| 3945 } |
| 3946 |
3571 return true; | 3947 return true; |
3572 } | 3948 } |
3573 | 3949 |
3574 bool Extension::CanSilentlyIncreasePermissions() const { | 3950 bool Extension::HasMultipleUISurfaces() const { |
3575 return location() != INTERNAL; | 3951 int num_surfaces = 0; |
| 3952 |
| 3953 if (page_action_info()) |
| 3954 ++num_surfaces; |
| 3955 |
| 3956 if (browser_action_info()) |
| 3957 ++num_surfaces; |
| 3958 |
| 3959 if (is_app()) |
| 3960 ++num_surfaces; |
| 3961 |
| 3962 return num_surfaces > 1; |
| 3963 } |
| 3964 |
| 3965 void Extension::OverrideLaunchUrl(const GURL& override_url) { |
| 3966 GURL new_url(override_url); |
| 3967 if (!new_url.is_valid()) { |
| 3968 DLOG(WARNING) << "Invalid override url given for " << name(); |
| 3969 } else { |
| 3970 if (new_url.has_port()) { |
| 3971 DLOG(WARNING) << "Override URL passed for " << name() |
| 3972 << " should not contain a port. Removing it."; |
| 3973 |
| 3974 GURL::Replacements remove_port; |
| 3975 remove_port.ClearPort(); |
| 3976 new_url = new_url.ReplaceComponents(remove_port); |
| 3977 } |
| 3978 |
| 3979 launch_web_url_ = new_url.spec(); |
| 3980 |
| 3981 URLPattern pattern(kValidWebExtentSchemes); |
| 3982 URLPattern::ParseResult result = pattern.Parse(new_url.spec()); |
| 3983 DCHECK_EQ(result, URLPattern::PARSE_SUCCESS); |
| 3984 pattern.SetPath(pattern.path() + '*'); |
| 3985 extent_.AddPattern(pattern); |
| 3986 } |
| 3987 } |
| 3988 |
| 3989 bool Extension::CanSpecifyExperimentalPermission() const { |
| 3990 if (location() == Extension::COMPONENT) |
| 3991 return true; |
| 3992 |
| 3993 if (CommandLine::ForCurrentProcess()->HasSwitch( |
| 3994 switches::kEnableExperimentalExtensionApis)) { |
| 3995 return true; |
| 3996 } |
| 3997 |
| 3998 // We rely on the webstore to check access to experimental. This way we can |
| 3999 // whitelist extensions to have access to experimental in just the store, and |
| 4000 // not have to push a new version of the client. |
| 4001 if (from_webstore()) |
| 4002 return true; |
| 4003 |
| 4004 return false; |
3576 } | 4005 } |
3577 | 4006 |
3578 bool Extension::CanSpecifyHostPermission(const URLPattern& pattern, | 4007 bool Extension::CanSpecifyHostPermission(const URLPattern& pattern, |
3579 const APIPermissionSet& permissions) const { | 4008 const APIPermissionSet& permissions) const { |
3580 if (!pattern.match_all_urls() && | 4009 if (!pattern.match_all_urls() && |
3581 pattern.MatchesScheme(chrome::kChromeUIScheme)) { | 4010 pattern.MatchesScheme(chrome::kChromeUIScheme)) { |
3582 // Regular extensions are only allowed access to chrome://favicon. | 4011 // Regular extensions are only allowed access to chrome://favicon. |
3583 if (pattern.host() == chrome::kChromeUIFaviconHost) | 4012 if (pattern.host() == chrome::kChromeUIFaviconHost) |
3584 return true; | 4013 return true; |
3585 | 4014 |
3586 // Experimental extensions are also allowed chrome://thumb. | 4015 // Experimental extensions are also allowed chrome://thumb. |
3587 if (pattern.host() == chrome::kChromeUIThumbnailHost) { | 4016 if (pattern.host() == chrome::kChromeUIThumbnailHost) { |
3588 return permissions.find(APIPermission::kExperimental) != | 4017 return permissions.find(APIPermission::kExperimental) != |
3589 permissions.end(); | 4018 permissions.end(); |
3590 } | 4019 } |
3591 | 4020 |
3592 // Component extensions can have access to all of chrome://*. | 4021 // Component extensions can have access to all of chrome://*. |
3593 if (CanExecuteScriptEverywhere()) | 4022 if (CanExecuteScriptEverywhere()) |
3594 return true; | 4023 return true; |
3595 | 4024 |
3596 return false; | 4025 return false; |
3597 } | 4026 } |
3598 | 4027 |
3599 // Otherwise, the valid schemes were handled by URLPattern. | 4028 // Otherwise, the valid schemes were handled by URLPattern. |
3600 return true; | 4029 return true; |
3601 } | 4030 } |
3602 | 4031 |
3603 bool Extension::HasAPIPermission(APIPermission::ID permission) const { | |
3604 base::AutoLock auto_lock(runtime_data_lock_); | |
3605 return runtime_data_.GetActivePermissions()->HasAPIPermission(permission); | |
3606 } | |
3607 | |
3608 bool Extension::HasAPIPermission(const std::string& function_name) const { | |
3609 base::AutoLock auto_lock(runtime_data_lock_); | |
3610 return runtime_data_.GetActivePermissions()-> | |
3611 HasAccessToFunction(function_name); | |
3612 } | |
3613 | |
3614 bool Extension::HasAPIPermissionForTab(int tab_id, | |
3615 APIPermission::ID permission) const { | |
3616 base::AutoLock auto_lock(runtime_data_lock_); | |
3617 if (runtime_data_.GetActivePermissions()->HasAPIPermission(permission)) | |
3618 return true; | |
3619 scoped_refptr<const PermissionSet> tab_specific_permissions = | |
3620 runtime_data_.GetTabSpecificPermissions(tab_id); | |
3621 return tab_specific_permissions.get() && | |
3622 tab_specific_permissions->HasAPIPermission(permission); | |
3623 } | |
3624 | |
3625 bool Extension::CheckAPIPermissionWithParam(APIPermission::ID permission, | |
3626 const APIPermission::CheckParam* param) const { | |
3627 base::AutoLock auto_lock(runtime_data_lock_); | |
3628 return runtime_data_.GetActivePermissions()-> | |
3629 CheckAPIPermissionWithParam(permission, param); | |
3630 } | |
3631 | |
3632 const URLPatternSet& Extension::GetEffectiveHostPermissions() const { | |
3633 base::AutoLock auto_lock(runtime_data_lock_); | |
3634 return runtime_data_.GetActivePermissions()->effective_hosts(); | |
3635 } | |
3636 | |
3637 bool Extension::HasHostPermission(const GURL& url) const { | |
3638 if (url.SchemeIs(chrome::kChromeUIScheme) && | |
3639 url.host() != chrome::kChromeUIFaviconHost && | |
3640 url.host() != chrome::kChromeUIThumbnailHost && | |
3641 location() != Extension::COMPONENT) { | |
3642 return false; | |
3643 } | |
3644 | |
3645 base::AutoLock auto_lock(runtime_data_lock_); | |
3646 return runtime_data_.GetActivePermissions()-> | |
3647 HasExplicitAccessToOrigin(url); | |
3648 } | |
3649 | |
3650 bool Extension::HasEffectiveAccessToAllHosts() const { | |
3651 base::AutoLock auto_lock(runtime_data_lock_); | |
3652 return runtime_data_.GetActivePermissions()->HasEffectiveAccessToAllHosts(); | |
3653 } | |
3654 | |
3655 bool Extension::HasFullPermissions() const { | |
3656 base::AutoLock auto_lock(runtime_data_lock_); | |
3657 return runtime_data_.GetActivePermissions()->HasEffectiveFullAccess(); | |
3658 } | |
3659 | |
3660 PermissionMessages Extension::GetPermissionMessages() const { | |
3661 base::AutoLock auto_lock(runtime_data_lock_); | |
3662 if (IsTrustedId(id())) { | |
3663 return PermissionMessages(); | |
3664 } else { | |
3665 return runtime_data_.GetActivePermissions()->GetPermissionMessages( | |
3666 GetType()); | |
3667 } | |
3668 } | |
3669 | |
3670 std::vector<string16> Extension::GetPermissionMessageStrings() const { | |
3671 base::AutoLock auto_lock(runtime_data_lock_); | |
3672 if (IsTrustedId(id())) | |
3673 return std::vector<string16>(); | |
3674 else | |
3675 return runtime_data_.GetActivePermissions()->GetWarningMessages(GetType()); | |
3676 } | |
3677 | |
3678 bool Extension::ShouldSkipPermissionWarnings() const { | |
3679 return IsTrustedId(id()); | |
3680 } | |
3681 | |
3682 void Extension::SetActivePermissions( | |
3683 const PermissionSet* permissions) const { | |
3684 base::AutoLock auto_lock(runtime_data_lock_); | |
3685 runtime_data_.SetActivePermissions(permissions); | |
3686 } | |
3687 | |
3688 scoped_refptr<const PermissionSet> | |
3689 Extension::GetActivePermissions() const { | |
3690 base::AutoLock auto_lock(runtime_data_lock_); | |
3691 return runtime_data_.GetActivePermissions(); | |
3692 } | |
3693 | |
3694 bool Extension::HasMultipleUISurfaces() const { | |
3695 int num_surfaces = 0; | |
3696 | |
3697 if (page_action_info()) | |
3698 ++num_surfaces; | |
3699 | |
3700 if (browser_action_info()) | |
3701 ++num_surfaces; | |
3702 | |
3703 if (is_app()) | |
3704 ++num_surfaces; | |
3705 | |
3706 return num_surfaces > 1; | |
3707 } | |
3708 | |
3709 bool Extension::CanExecuteScriptOnPage(const GURL& document_url, | |
3710 const GURL& top_frame_url, | |
3711 int tab_id, | |
3712 const UserScript* script, | |
3713 std::string* error) const { | |
3714 base::AutoLock auto_lock(runtime_data_lock_); | |
3715 // The gallery is special-cased as a restricted URL for scripting to prevent | |
3716 // access to special JS bindings we expose to the gallery (and avoid things | |
3717 // like extensions removing the "report abuse" link). | |
3718 // TODO(erikkay): This seems like the wrong test. Shouldn't we we testing | |
3719 // against the store app extent? | |
3720 GURL store_url(extension_urls::GetWebstoreLaunchURL()); | |
3721 if ((document_url.host() == store_url.host()) && | |
3722 !CanExecuteScriptEverywhere() && | |
3723 !CommandLine::ForCurrentProcess()->HasSwitch( | |
3724 switches::kAllowScriptingGallery)) { | |
3725 if (error) | |
3726 *error = errors::kCannotScriptGallery; | |
3727 return false; | |
3728 } | |
3729 | |
3730 if (document_url.SchemeIs(chrome::kChromeUIScheme) && | |
3731 !CanExecuteScriptEverywhere()) { | |
3732 return false; | |
3733 } | |
3734 | |
3735 if (top_frame_url.SchemeIs(extensions::kExtensionScheme) && | |
3736 top_frame_url.GetOrigin() != | |
3737 GetBaseURLFromExtensionId(id()).GetOrigin() && | |
3738 !CanExecuteScriptEverywhere()) { | |
3739 return false; | |
3740 } | |
3741 | |
3742 // If a tab ID is specified, try the tab-specific permissions. | |
3743 if (tab_id >= 0) { | |
3744 scoped_refptr<const PermissionSet> tab_permissions = | |
3745 runtime_data_.GetTabSpecificPermissions(tab_id); | |
3746 if (tab_permissions.get() && | |
3747 tab_permissions->explicit_hosts().MatchesSecurityOrigin(document_url)) { | |
3748 return true; | |
3749 } | |
3750 } | |
3751 | |
3752 // If a script is specified, use its matches. | |
3753 if (script) | |
3754 return script->MatchesURL(document_url); | |
3755 | |
3756 // Otherwise, see if this extension has permission to execute script | |
3757 // programmatically on pages. | |
3758 if (runtime_data_.GetActivePermissions()->HasExplicitAccessToOrigin( | |
3759 document_url)) { | |
3760 return true; | |
3761 } | |
3762 | |
3763 if (error) { | |
3764 *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, | |
3765 document_url.spec()); | |
3766 } | |
3767 | |
3768 return false; | |
3769 } | |
3770 | |
3771 bool Extension::ShowConfigureContextMenus() const { | |
3772 // Don't show context menu for component extensions. We might want to show | |
3773 // options for component extension button but now there is no component | |
3774 // extension with options. All other menu items like uninstall have | |
3775 // no sense for component extensions. | |
3776 return location() != Extension::COMPONENT; | |
3777 } | |
3778 | |
3779 bool Extension::CanSpecifyExperimentalPermission() const { | |
3780 if (location() == Extension::COMPONENT) | |
3781 return true; | |
3782 | |
3783 if (CommandLine::ForCurrentProcess()->HasSwitch( | |
3784 switches::kEnableExperimentalExtensionApis)) { | |
3785 return true; | |
3786 } | |
3787 | |
3788 // We rely on the webstore to check access to experimental. This way we can | |
3789 // whitelist extensions to have access to experimental in just the store, and | |
3790 // not have to push a new version of the client. | |
3791 if (from_webstore()) | |
3792 return true; | |
3793 | |
3794 return false; | |
3795 } | |
3796 | |
3797 bool Extension::CanExecuteScriptEverywhere() const { | |
3798 if (location() == Extension::COMPONENT) | |
3799 return true; | |
3800 | |
3801 ScriptingWhitelist* whitelist = ExtensionConfig::GetInstance()->whitelist(); | |
3802 | |
3803 for (ScriptingWhitelist::const_iterator it = whitelist->begin(); | |
3804 it != whitelist->end(); ++it) { | |
3805 if (id() == *it) { | |
3806 return true; | |
3807 } | |
3808 } | |
3809 | |
3810 return false; | |
3811 } | |
3812 | |
3813 bool Extension::CanCaptureVisiblePage(const GURL& page_url, | |
3814 int tab_id, | |
3815 std::string* error) const { | |
3816 if (tab_id >= 0) { | |
3817 scoped_refptr<const PermissionSet> tab_permissions = | |
3818 GetTabSpecificPermissions(tab_id); | |
3819 if (tab_permissions.get() && | |
3820 tab_permissions->explicit_hosts().MatchesSecurityOrigin(page_url)) { | |
3821 return true; | |
3822 } | |
3823 } | |
3824 | |
3825 if (HasHostPermission(page_url) || page_url.GetOrigin() == url()) | |
3826 return true; | |
3827 | |
3828 if (error) { | |
3829 *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, | |
3830 page_url.spec()); | |
3831 } | |
3832 return false; | |
3833 } | |
3834 | |
3835 bool Extension::UpdatesFromGallery() const { | |
3836 return extension_urls::IsWebstoreUpdateUrl(update_url()); | |
3837 } | |
3838 | |
3839 bool Extension::OverlapsWithOrigin(const GURL& origin) const { | |
3840 if (url() == origin) | |
3841 return true; | |
3842 | |
3843 if (web_extent().is_empty()) | |
3844 return false; | |
3845 | |
3846 // Note: patterns and extents ignore port numbers. | |
3847 URLPattern origin_only_pattern(kValidWebExtentSchemes); | |
3848 if (!origin_only_pattern.SetScheme(origin.scheme())) | |
3849 return false; | |
3850 origin_only_pattern.SetHost(origin.host()); | |
3851 origin_only_pattern.SetPath("/*"); | |
3852 | |
3853 URLPatternSet origin_only_pattern_list; | |
3854 origin_only_pattern_list.AddPattern(origin_only_pattern); | |
3855 | |
3856 return web_extent().OverlapsWith(origin_only_pattern_list); | |
3857 } | |
3858 | |
3859 Extension::SyncType Extension::GetSyncType() const { | |
3860 if (!IsSyncable()) { | |
3861 // We have a non-standard location. | |
3862 return SYNC_TYPE_NONE; | |
3863 } | |
3864 | |
3865 // Disallow extensions with non-gallery auto-update URLs for now. | |
3866 // | |
3867 // TODO(akalin): Relax this restriction once we've put in UI to | |
3868 // approve synced extensions. | |
3869 if (!update_url().is_empty() && !UpdatesFromGallery()) | |
3870 return SYNC_TYPE_NONE; | |
3871 | |
3872 // Disallow extensions with native code plugins. | |
3873 // | |
3874 // TODO(akalin): Relax this restriction once we've put in UI to | |
3875 // approve synced extensions. | |
3876 if (!plugins().empty()) { | |
3877 return SYNC_TYPE_NONE; | |
3878 } | |
3879 | |
3880 switch (GetType()) { | |
3881 case Extension::TYPE_EXTENSION: | |
3882 return SYNC_TYPE_EXTENSION; | |
3883 | |
3884 case Extension::TYPE_USER_SCRIPT: | |
3885 // We only want to sync user scripts with gallery update URLs. | |
3886 if (UpdatesFromGallery()) | |
3887 return SYNC_TYPE_EXTENSION; | |
3888 else | |
3889 return SYNC_TYPE_NONE; | |
3890 | |
3891 case Extension::TYPE_HOSTED_APP: | |
3892 case Extension::TYPE_LEGACY_PACKAGED_APP: | |
3893 case Extension::TYPE_PLATFORM_APP: | |
3894 return SYNC_TYPE_APP; | |
3895 | |
3896 default: | |
3897 return SYNC_TYPE_NONE; | |
3898 } | |
3899 } | |
3900 | |
3901 bool Extension::IsSyncable() const { | |
3902 // TODO(akalin): Figure out if we need to allow some other types. | |
3903 | |
3904 // Default apps are not synced because otherwise they will pollute profiles | |
3905 // that don't already have them. Specially, if a user doesn't have default | |
3906 // apps, creates a new profile (which get default apps) and then enables sync | |
3907 // for it, then their profile everywhere gets the default apps. | |
3908 bool is_syncable = (location() == Extension::INTERNAL && | |
3909 !was_installed_by_default()); | |
3910 // Sync the chrome web store to maintain its position on the new tab page. | |
3911 is_syncable |= (id() == extension_misc::kWebStoreAppId); | |
3912 return is_syncable; | |
3913 } | |
3914 | |
3915 bool Extension::RequiresSortOrdinal() const { | |
3916 return is_app() && (display_in_launcher_ || display_in_new_tab_page_); | |
3917 } | |
3918 | |
3919 bool Extension::ShouldDisplayInAppLauncher() const { | |
3920 // Only apps should be displayed in the launcher. | |
3921 return is_app() && display_in_launcher_; | |
3922 } | |
3923 | |
3924 bool Extension::ShouldDisplayInNewTabPage() const { | |
3925 // Only apps should be displayed on the NTP. | |
3926 return is_app() && display_in_new_tab_page_; | |
3927 } | |
3928 | |
3929 bool Extension::InstallWarning::operator==(const InstallWarning& other) const { | |
3930 return format == other.format && message == other.message; | |
3931 } | |
3932 | |
3933 void PrintTo(const Extension::InstallWarning& warning, ::std::ostream* os) { | |
3934 *os << "InstallWarning("; | |
3935 switch (warning.format) { | |
3936 case Extension::InstallWarning::FORMAT_TEXT: | |
3937 *os << "FORMAT_TEXT, \""; | |
3938 break; | |
3939 case Extension::InstallWarning::FORMAT_HTML: | |
3940 *os << "FORMAT_HTML, \""; | |
3941 break; | |
3942 } | |
3943 // This is just for test error messages, so no need to escape '"' | |
3944 // characters inside the message. | |
3945 *os << warning.message << "\")"; | |
3946 } | |
3947 | |
3948 ExtensionInfo::ExtensionInfo(const DictionaryValue* manifest, | |
3949 const std::string& id, | |
3950 const FilePath& path, | |
3951 Extension::Location location) | |
3952 : extension_id(id), | |
3953 extension_path(path), | |
3954 extension_location(location) { | |
3955 if (manifest) | |
3956 extension_manifest.reset(manifest->DeepCopy()); | |
3957 } | |
3958 | |
3959 bool Extension::ShouldDisplayInExtensionSettings() const { | |
3960 // Don't show for themes since the settings UI isn't really useful for them. | |
3961 if (is_theme()) | |
3962 return false; | |
3963 | |
3964 // Don't show component extensions because they are only extensions as an | |
3965 // implementation detail of Chrome. | |
3966 if (location() == Extension::COMPONENT && | |
3967 !CommandLine::ForCurrentProcess()->HasSwitch( | |
3968 switches::kShowComponentExtensionOptions)) { | |
3969 return false; | |
3970 } | |
3971 | |
3972 // Always show unpacked extensions and apps. | |
3973 if (location() == Extension::LOAD) | |
3974 return true; | |
3975 | |
3976 // Unless they are unpacked, never show hosted apps. Note: We intentionally | |
3977 // show packaged apps and platform apps because there are some pieces of | |
3978 // functionality that are only available in chrome://extensions/ but which | |
3979 // are needed for packaged and platform apps. For example, inspecting | |
3980 // background pages. See http://crbug.com/116134. | |
3981 if (is_hosted_app()) | |
3982 return false; | |
3983 | |
3984 return true; | |
3985 } | |
3986 | |
3987 bool Extension::HasContentScriptAtURL(const GURL& url) const { | |
3988 for (UserScriptList::const_iterator it = content_scripts_.begin(); | |
3989 it != content_scripts_.end(); ++it) { | |
3990 if (it->MatchesURL(url)) | |
3991 return true; | |
3992 } | |
3993 return false; | |
3994 } | |
3995 | |
3996 scoped_refptr<const PermissionSet> Extension::GetTabSpecificPermissions( | |
3997 int tab_id) const { | |
3998 base::AutoLock auto_lock(runtime_data_lock_); | |
3999 return runtime_data_.GetTabSpecificPermissions(tab_id); | |
4000 } | |
4001 | |
4002 void Extension::UpdateTabSpecificPermissions( | |
4003 int tab_id, | |
4004 scoped_refptr<const PermissionSet> permissions) const { | |
4005 base::AutoLock auto_lock(runtime_data_lock_); | |
4006 runtime_data_.UpdateTabSpecificPermissions(tab_id, permissions); | |
4007 } | |
4008 | |
4009 void Extension::ClearTabSpecificPermissions(int tab_id) const { | |
4010 base::AutoLock auto_lock(runtime_data_lock_); | |
4011 runtime_data_.ClearTabSpecificPermissions(tab_id); | |
4012 } | |
4013 | |
4014 bool Extension::CheckMinimumChromeVersion(string16* error) const { | 4032 bool Extension::CheckMinimumChromeVersion(string16* error) const { |
4015 if (!manifest_->HasKey(keys::kMinimumChromeVersion)) | 4033 if (!manifest_->HasKey(keys::kMinimumChromeVersion)) |
4016 return true; | 4034 return true; |
4017 std::string minimum_version_string; | 4035 std::string minimum_version_string; |
4018 if (!manifest_->GetString(keys::kMinimumChromeVersion, | 4036 if (!manifest_->GetString(keys::kMinimumChromeVersion, |
4019 &minimum_version_string)) { | 4037 &minimum_version_string)) { |
4020 *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion); | 4038 *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion); |
4021 return false; | 4039 return false; |
4022 } | 4040 } |
4023 | 4041 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4069 bool Extension::CheckConflictingFeatures(std::string* utf8_error) const { | 4087 bool Extension::CheckConflictingFeatures(std::string* utf8_error) const { |
4070 if (has_lazy_background_page() && | 4088 if (has_lazy_background_page() && |
4071 HasAPIPermission(APIPermission::kWebRequest)) { | 4089 HasAPIPermission(APIPermission::kWebRequest)) { |
4072 *utf8_error = errors::kWebRequestConflictsWithLazyBackground; | 4090 *utf8_error = errors::kWebRequestConflictsWithLazyBackground; |
4073 return false; | 4091 return false; |
4074 } | 4092 } |
4075 | 4093 |
4076 return true; | 4094 return true; |
4077 } | 4095 } |
4078 | 4096 |
| 4097 void PrintTo(const Extension::InstallWarning& warning, ::std::ostream* os) { |
| 4098 *os << "InstallWarning("; |
| 4099 switch (warning.format) { |
| 4100 case Extension::InstallWarning::FORMAT_TEXT: |
| 4101 *os << "FORMAT_TEXT, \""; |
| 4102 break; |
| 4103 case Extension::InstallWarning::FORMAT_HTML: |
| 4104 *os << "FORMAT_HTML, \""; |
| 4105 break; |
| 4106 } |
| 4107 // This is just for test error messages, so no need to escape '"' |
| 4108 // characters inside the message. |
| 4109 *os << warning.message << "\")"; |
| 4110 } |
| 4111 |
| 4112 ExtensionInfo::ExtensionInfo(const DictionaryValue* manifest, |
| 4113 const std::string& id, |
| 4114 const FilePath& path, |
| 4115 Extension::Location location) |
| 4116 : extension_id(id), |
| 4117 extension_path(path), |
| 4118 extension_location(location) { |
| 4119 if (manifest) |
| 4120 extension_manifest.reset(manifest->DeepCopy()); |
| 4121 } |
| 4122 |
4079 ExtensionInfo::~ExtensionInfo() {} | 4123 ExtensionInfo::~ExtensionInfo() {} |
4080 | 4124 |
4081 Extension::RuntimeData::RuntimeData() {} | |
4082 Extension::RuntimeData::RuntimeData(const PermissionSet* active) | |
4083 : active_permissions_(active) {} | |
4084 Extension::RuntimeData::~RuntimeData() {} | |
4085 | |
4086 scoped_refptr<const PermissionSet> | |
4087 Extension::RuntimeData::GetActivePermissions() const { | |
4088 return active_permissions_; | |
4089 } | |
4090 | |
4091 void Extension::RuntimeData::SetActivePermissions( | |
4092 const PermissionSet* active) { | |
4093 active_permissions_ = active; | |
4094 } | |
4095 | |
4096 scoped_refptr<const PermissionSet> | |
4097 Extension::RuntimeData::GetTabSpecificPermissions(int tab_id) const { | |
4098 CHECK_GE(tab_id, 0); | |
4099 TabPermissionsMap::const_iterator it = tab_specific_permissions_.find(tab_id); | |
4100 return (it != tab_specific_permissions_.end()) ? it->second : NULL; | |
4101 } | |
4102 | |
4103 void Extension::RuntimeData::UpdateTabSpecificPermissions( | |
4104 int tab_id, | |
4105 scoped_refptr<const PermissionSet> permissions) { | |
4106 CHECK_GE(tab_id, 0); | |
4107 if (tab_specific_permissions_.count(tab_id)) { | |
4108 tab_specific_permissions_[tab_id] = PermissionSet::CreateUnion( | |
4109 tab_specific_permissions_[tab_id], | |
4110 permissions.get()); | |
4111 } else { | |
4112 tab_specific_permissions_[tab_id] = permissions; | |
4113 } | |
4114 } | |
4115 | |
4116 void Extension::RuntimeData::ClearTabSpecificPermissions(int tab_id) { | |
4117 CHECK_GE(tab_id, 0); | |
4118 tab_specific_permissions_.erase(tab_id); | |
4119 } | |
4120 | |
4121 UnloadedExtensionInfo::UnloadedExtensionInfo( | 4125 UnloadedExtensionInfo::UnloadedExtensionInfo( |
4122 const Extension* extension, | 4126 const Extension* extension, |
4123 extension_misc::UnloadedExtensionReason reason) | 4127 extension_misc::UnloadedExtensionReason reason) |
4124 : reason(reason), | 4128 : reason(reason), |
4125 already_disabled(false), | 4129 already_disabled(false), |
4126 extension(extension) {} | 4130 extension(extension) {} |
4127 | 4131 |
4128 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( | 4132 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( |
4129 const Extension* extension, | 4133 const Extension* extension, |
4130 const PermissionSet* permissions, | 4134 const PermissionSet* permissions, |
4131 Reason reason) | 4135 Reason reason) |
4132 : reason(reason), | 4136 : reason(reason), |
4133 extension(extension), | 4137 extension(extension), |
4134 permissions(permissions) {} | 4138 permissions(permissions) {} |
4135 | 4139 |
4136 } // namespace extensions | 4140 } // namespace extensions |
OLD | NEW |