1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/common/extensions/extension.h" 6 7 #include "base/base64.h" 8 #include "base/basictypes.h" 9 #include "base/command_line.h" 10 #include "base/files/file_path.h" 11 #include "base/i18n/rtl.h" 12 #include "base/logging.h" 13 #include "base/memory/singleton.h" 14 #include "base/stl_util.h" 15 #include "base/strings/string16.h" 16 #include "base/strings/string_number_conversions.h" 17 #include "base/strings/string_piece.h" 18 #include "base/strings/string_util.h" 19 #include "base/strings/stringprintf.h" 20 #include "base/strings/utf_string_conversions.h" 21 #include "base/values.h" 22 #include "base/version.h" 23 #include "chrome/common/extensions/extension_manifest_constants.h" 24 #include "chrome/common/extensions/manifest.h" 25 #include "chrome/common/extensions/manifest_handler.h" 26 #include "chrome/common/extensions/permissions/api_permission_set.h" 27 #include "chrome/common/extensions/permissions/permission_set.h" 28 #include "chrome/common/extensions/permissions/permissions_data.h" 29 #include "chrome/common/extensions/permissions/permissions_info.h" 30 #include "content/public/common/url_constants.h" 31 #include "extensions/common/constants.h" 32 #include "extensions/common/error_utils.h" 33 #include "extensions/common/id_util.h" 34 #include "extensions/common/switches.h" 35 #include "extensions/common/url_pattern_set.h" 36 #include "grit/chromium_strings.h" 37 #include "grit/theme_resources.h" 38 #include "third_party/skia/include/core/SkBitmap.h" 39 #include "url/url_util.h" 40 41 #if defined(OS_WIN) 42 #include "grit/generated_resources.h" 43 #endif 44 45 namespace keys = extension_manifest_keys; 46 namespace values = extension_manifest_values; 47 namespace errors = extension_manifest_errors; 48 49 namespace extensions { 50 51 namespace { 52 53 const int kModernManifestVersion = 2; 54 const int kPEMOutputColumns = 65; 55 56 // KEY MARKERS 57 const char kKeyBeginHeaderMarker[] = "-----BEGIN"; 58 const char kKeyBeginFooterMarker[] = "-----END"; 59 const char kKeyInfoEndMarker[] = "KEY-----"; 60 const char kPublic[] = "PUBLIC"; 61 const char kPrivate[] = "PRIVATE"; 62 63 const int kRSAKeySize = 1024; 64 65 // A singleton object containing global data needed by the extension objects. 66 class ExtensionConfig { 67 public: 68 static ExtensionConfig* GetInstance() { 69 return Singleton<ExtensionConfig>::get(); 70 } 71 72 Extension::ScriptingWhitelist* whitelist() { return &scripting_whitelist_; } 73 74 private: 75 friend struct DefaultSingletonTraits<ExtensionConfig>; 76 77 ExtensionConfig() { 78 // Whitelist ChromeVox, an accessibility extension from Google that needs 79 // the ability to script webui pages. This is temporary and is not 80 // meant to be a general solution. 81 // TODO(dmazzoni): remove this once we have an extension API that 82 // allows any extension to request read-only access to webui pages. 83 scripting_whitelist_.push_back(extension_misc::kChromeVoxExtensionId); 84 85 // Whitelist "Discover DevTools Companion" extension from Google that 86 // needs the ability to script DevTools pages. Companion will assist 87 // online courses and will be needed while the online educational programs 88 // are in place. 89 scripting_whitelist_.push_back("angkfkebojeancgemegoedelbnjgcgme"); 90 } 91 ~ExtensionConfig() { } 92 93 // A whitelist of extensions that can script anywhere. Do not add to this 94 // list (except in tests) without consulting the Extensions team first. 95 // Note: Component extensions have this right implicitly and do not need to be 96 // added to this list. 97 Extension::ScriptingWhitelist scripting_whitelist_; 98 }; 99 100 } // namespace 101 102 #if defined(OS_WIN) 103 const char Extension::kExtensionRegistryPath[] = 104 "Software\\Google\\Chrome\\Extensions"; 105 #endif 106 107 const char Extension::kMimeType[] = "application/x-chrome-extension"; 108 109 const int Extension::kValidWebExtentSchemes = 110 URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; 111 112 const int Extension::kValidHostPermissionSchemes = URLPattern::SCHEME_CHROMEUI | 113 URLPattern::SCHEME_HTTP | 114 URLPattern::SCHEME_HTTPS | 115 URLPattern::SCHEME_FILE | 116 URLPattern::SCHEME_FTP; 117 118 // 119 // Extension 120 // 121 122 // static 123 scoped_refptr<Extension> Extension::Create(const base::FilePath& path, 124 Manifest::Location location, 125 const base::DictionaryValue& value, 126 int flags, 127 std::string* utf8_error) { 128 return Extension::Create(path, 129 location, 130 value, 131 flags, 132 std::string(), // ID is ignored if empty. 133 utf8_error); 134 } 135 136 // TODO(sungguk): Continue removing std::string errors and replacing 137 // with string16. See http://crbug.com/71980. 138 scoped_refptr<Extension> Extension::Create(const base::FilePath& path, 139 Manifest::Location location, 140 const base::DictionaryValue& value, 141 int flags, 142 const std::string& explicit_id, 143 std::string* utf8_error) { 144 DCHECK(utf8_error); 145 string16 error; 146 scoped_ptr<extensions::Manifest> manifest( 147 new extensions::Manifest( 148 location, scoped_ptr<base::DictionaryValue>(value.DeepCopy()))); 149 150 if (!InitExtensionID(manifest.get(), path, explicit_id, flags, &error)) { 151 *utf8_error = UTF16ToUTF8(error); 152 return NULL; 153 } 154 155 std::vector<InstallWarning> install_warnings; 156 if (!manifest->ValidateManifest(utf8_error, &install_warnings)) { 157 return NULL; 158 } 159 160 scoped_refptr<Extension> extension = new Extension(path, manifest.Pass()); 161 extension->install_warnings_.swap(install_warnings); 162 163 if (!extension->InitFromValue(flags, &error)) { 164 *utf8_error = UTF16ToUTF8(error); 165 return NULL; 166 } 167 168 return extension; 169 } 170 171 // static 172 bool Extension::IdIsValid(const std::string& id) { 173 // Verify that the id is legal. 174 if (id.size() != (id_util::kIdSize * 2)) 175 return false; 176 177 // We only support lowercase IDs, because IDs can be used as URL components 178 // (where GURL will lowercase it). 179 std::string temp = StringToLowerASCII(id); 180 for (size_t i = 0; i < temp.size(); i++) 181 if (temp[i] < 'a' || temp[i] > 'p') 182 return false; 183 184 return true; 185 } 186 187 Manifest::Type Extension::GetType() const { 188 return converted_from_user_script() ? 189 Manifest::TYPE_USER_SCRIPT : manifest_->type(); 190 } 191 192 // static 193 GURL Extension::GetResourceURL(const GURL& extension_url, 194 const std::string& relative_path) { 195 DCHECK(extension_url.SchemeIs(extensions::kExtensionScheme)); 196 DCHECK_EQ("/", extension_url.path()); 197 198 std::string path = relative_path; 199 200 // If the relative path starts with "/", it is "absolute" relative to the 201 // extension base directory, but extension_url is already specified to refer 202 // to that base directory, so strip the leading "/" if present. 203 if (relative_path.size() > 0 && relative_path[0] == '/') 204 path = relative_path.substr(1); 205 206 GURL ret_val = GURL(extension_url.spec() + path); 207 DCHECK(StartsWithASCII(ret_val.spec(), extension_url.spec(), false)); 208 209 return ret_val; 210 } 211 212 bool Extension::ResourceMatches(const URLPatternSet& pattern_set, 213 const std::string& resource) const { 214 return pattern_set.MatchesURL(extension_url_.Resolve(resource)); 215 } 216 217 ExtensionResource Extension::GetResource( 218 const std::string& relative_path) const { 219 std::string new_path = relative_path; 220 // We have some legacy data where resources have leading slashes. 221 // See: http://crbug.com/121164 222 if (!new_path.empty() && new_path.at(0) == '/') 223 new_path.erase(0, 1); 224 base::FilePath relative_file_path = base::FilePath::FromUTF8Unsafe(new_path); 225 ExtensionResource r(id(), path(), relative_file_path); 226 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { 227 r.set_follow_symlinks_anywhere(); 228 } 229 return r; 230 } 231 232 ExtensionResource Extension::GetResource( 233 const base::FilePath& relative_file_path) const { 234 ExtensionResource r(id(), path(), relative_file_path); 235 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { 236 r.set_follow_symlinks_anywhere(); 237 } 238 return r; 239 } 240 241 // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a 242 // util class in base: 243 // http://code.google.com/p/chromium/issues/detail?id=13572 244 // static 245 bool Extension::ParsePEMKeyBytes(const std::string& input, 246 std::string* output) { 247 DCHECK(output); 248 if (!output) 249 return false; 250 if (input.length() == 0) 251 return false; 252 253 std::string working = input; 254 if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) { 255 working = CollapseWhitespaceASCII(working, true); 256 size_t header_pos = working.find(kKeyInfoEndMarker, 257 sizeof(kKeyBeginHeaderMarker) - 1); 258 if (header_pos == std::string::npos) 259 return false; 260 size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1; 261 size_t end_pos = working.rfind(kKeyBeginFooterMarker); 262 if (end_pos == std::string::npos) 263 return false; 264 if (start_pos >= end_pos) 265 return false; 266 267 working = working.substr(start_pos, end_pos - start_pos); 268 if (working.length() == 0) 269 return false; 270 } 271 272 return base::Base64Decode(working, output); 273 } 274 275 // static 276 bool Extension::ProducePEM(const std::string& input, std::string* output) { 277 DCHECK(output); 278 return (input.length() == 0) ? false : base::Base64Encode(input, output); 279 } 280 281 // static 282 bool Extension::FormatPEMForFileOutput(const std::string& input, 283 std::string* output, 284 bool is_public) { 285 DCHECK(output); 286 if (input.length() == 0) 287 return false; 288 *output = ""; 289 output->append(kKeyBeginHeaderMarker); 290 output->append(" "); 291 output->append(is_public ? kPublic : kPrivate); 292 output->append(" "); 293 output->append(kKeyInfoEndMarker); 294 output->append("\n"); 295 for (size_t i = 0; i < input.length(); ) { 296 int slice = std::min<int>(input.length() - i, kPEMOutputColumns); 297 output->append(input.substr(i, slice)); 298 output->append("\n"); 299 i += slice; 300 } 301 output->append(kKeyBeginFooterMarker); 302 output->append(" "); 303 output->append(is_public ? kPublic : kPrivate); 304 output->append(" "); 305 output->append(kKeyInfoEndMarker); 306 output->append("\n"); 307 308 return true; 309 } 310 311 // static 312 GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { 313 return GURL(std::string(extensions::kExtensionScheme) + 314 content::kStandardSchemeSeparator + extension_id + "/"); 315 } 316 317 // static 318 void Extension::SetScriptingWhitelist( 319 const Extension::ScriptingWhitelist& whitelist) { 320 ScriptingWhitelist* current_whitelist = 321 ExtensionConfig::GetInstance()->whitelist(); 322 current_whitelist->clear(); 323 for (ScriptingWhitelist::const_iterator it = whitelist.begin(); 324 it != whitelist.end(); ++it) { 325 current_whitelist->push_back(*it); 326 } 327 } 328 329 // static 330 const Extension::ScriptingWhitelist* Extension::GetScriptingWhitelist() { 331 return ExtensionConfig::GetInstance()->whitelist(); 332 } 333 334 bool Extension::HasAPIPermission(APIPermission::ID permission) const { 335 return PermissionsData::HasAPIPermission(this, permission); 336 } 337 338 bool Extension::HasAPIPermission(const std::string& permission_name) const { 339 return PermissionsData::HasAPIPermission(this, permission_name); 340 } 341 342 scoped_refptr<const PermissionSet> Extension::GetActivePermissions() const { 343 return PermissionsData::GetActivePermissions(this); 344 } 345 346 bool Extension::ShowConfigureContextMenus() const { 347 // Don't show context menu for component extensions. We might want to show 348 // options for component extension button but now there is no component 349 // extension with options. All other menu items like uninstall have 350 // no sense for component extensions. 351 return location() != Manifest::COMPONENT; 352 } 353 354 bool Extension::OverlapsWithOrigin(const GURL& origin) const { 355 if (url() == origin) 356 return true; 357 358 if (web_extent().is_empty()) 359 return false; 360 361 // Note: patterns and extents ignore port numbers. 362 URLPattern origin_only_pattern(kValidWebExtentSchemes); 363 if (!origin_only_pattern.SetScheme(origin.scheme())) 364 return false; 365 origin_only_pattern.SetHost(origin.host()); 366 origin_only_pattern.SetPath("/*"); 367 368 URLPatternSet origin_only_pattern_list; 369 origin_only_pattern_list.AddPattern(origin_only_pattern); 370 371 return web_extent().OverlapsWith(origin_only_pattern_list); 372 } 373 374 bool Extension::RequiresSortOrdinal() const { 375 return is_app() && (display_in_launcher_ || display_in_new_tab_page_); 376 } 377 378 bool Extension::ShouldDisplayInAppLauncher() const { 379 // Only apps should be displayed in the launcher. 380 return is_app() && display_in_launcher_; 381 } 382 383 bool Extension::ShouldDisplayInNewTabPage() const { 384 // Only apps should be displayed on the NTP. 385 return is_app() && display_in_new_tab_page_; 386 } 387 388 bool Extension::ShouldDisplayInExtensionSettings() const { 389 // Don't show for themes since the settings UI isn't really useful for them. 390 if (is_theme()) 391 return false; 392 393 // Don't show component extensions and invisible apps. 394 if (ShouldNotBeVisible()) 395 return false; 396 397 // Always show unpacked extensions and apps. 398 if (Manifest::IsUnpackedLocation(location())) 399 return true; 400 401 // Unless they are unpacked, never show hosted apps. Note: We intentionally 402 // show packaged apps and platform apps because there are some pieces of 403 // functionality that are only available in chrome://extensions/ but which 404 // are needed for packaged and platform apps. For example, inspecting 405 // background pages. See http://crbug.com/116134. 406 if (is_hosted_app()) 407 return false; 408 409 return true; 410 } 411 412 bool Extension::ShouldNotBeVisible() const { 413 // Don't show component extensions because they are only extensions as an 414 // implementation detail of Chrome. 415 if (location() == Manifest::COMPONENT && 416 !CommandLine::ForCurrentProcess()->HasSwitch( 417 switches::kShowComponentExtensionOptions)) { 418 return true; 419 } 420 421 // Always show unpacked extensions and apps. 422 if (Manifest::IsUnpackedLocation(location())) 423 return false; 424 425 // Don't show apps that aren't visible in either launcher or ntp. 426 if (is_app() && !ShouldDisplayInAppLauncher() && !ShouldDisplayInNewTabPage()) 427 return true; 428 429 return false; 430 } 431 432 Extension::ManifestData* Extension::GetManifestData(const std::string& key) 433 const { 434 DCHECK(finished_parsing_manifest_ || thread_checker_.CalledOnValidThread()); 435 ManifestDataMap::const_iterator iter = manifest_data_.find(key); 436 if (iter != manifest_data_.end()) 437 return iter->second.get(); 438 return NULL; 439 } 440 441 void Extension::SetManifestData(const std::string& key, 442 Extension::ManifestData* data) { 443 DCHECK(!finished_parsing_manifest_ && thread_checker_.CalledOnValidThread()); 444 manifest_data_[key] = linked_ptr<ManifestData>(data); 445 } 446 447 Manifest::Location Extension::location() const { 448 return manifest_->location(); 449 } 450 451 const std::string& Extension::id() const { 452 return manifest_->extension_id(); 453 } 454 455 const std::string Extension::VersionString() const { 456 return version()->GetString(); 457 } 458 459 void Extension::AddInstallWarning(const InstallWarning& new_warning) { 460 install_warnings_.push_back(new_warning); 461 } 462 463 void Extension::AddInstallWarnings( 464 const std::vector<InstallWarning>& new_warnings) { 465 install_warnings_.insert(install_warnings_.end(), 466 new_warnings.begin(), new_warnings.end()); 467 } 468 469 bool Extension::is_app() const { 470 return manifest_->is_app(); 471 } 472 473 bool Extension::is_platform_app() const { 474 return manifest_->is_platform_app(); 475 } 476 477 bool Extension::is_hosted_app() const { 478 return manifest()->is_hosted_app(); 479 } 480 481 bool Extension::is_legacy_packaged_app() const { 482 return manifest()->is_legacy_packaged_app(); 483 } 484 485 bool Extension::is_extension() const { 486 return manifest()->is_extension(); 487 } 488 489 bool Extension::can_be_incognito_enabled() const { 490 // Only component platform apps are supported in incognito. 491 return !is_platform_app() || location() == Manifest::COMPONENT; 492 } 493 494 bool Extension::force_incognito_enabled() const { 495 return PermissionsData::HasAPIPermission(this, APIPermission::kProxy); 496 } 497 498 void Extension::AddWebExtentPattern(const URLPattern& pattern) { 499 extent_.AddPattern(pattern); 500 } 501 502 bool Extension::is_theme() const { 503 return manifest()->is_theme(); 504 } 505 506 // static 507 bool Extension::InitExtensionID(extensions::Manifest* manifest, 508 const base::FilePath& path, 509 const std::string& explicit_id, 510 int creation_flags, 511 string16* error) { 512 if (!explicit_id.empty()) { 513 manifest->set_extension_id(explicit_id); 514 return true; 515 } 516 517 if (manifest->HasKey(keys::kPublicKey)) { 518 std::string public_key; 519 std::string public_key_bytes; 520 if (!manifest->GetString(keys::kPublicKey, &public_key) || 521 !ParsePEMKeyBytes(public_key, &public_key_bytes)) { 522 *error = ASCIIToUTF16(errors::kInvalidKey); 523 return false; 524 } 525 std::string extension_id = id_util::GenerateId(public_key_bytes); 526 manifest->set_extension_id(extension_id); 527 return true; 528 } 529 530 if (creation_flags & REQUIRE_KEY) { 531 *error = ASCIIToUTF16(errors::kInvalidKey); 532 return false; 533 } else { 534 // If there is a path, we generate the ID from it. This is useful for 535 // development mode, because it keeps the ID stable across restarts and 536 // reloading the extension. 537 std::string extension_id = id_util::GenerateIdForPath(path); 538 if (extension_id.empty()) { 539 NOTREACHED() << "Could not create ID from path."; 540 return false; 541 } 542 manifest->set_extension_id(extension_id); 543 return true; 544 } 545 } 546 547 Extension::Extension(const base::FilePath& path, 548 scoped_ptr<extensions::Manifest> manifest) 549 : manifest_version_(0), 550 converted_from_user_script_(false), 551 manifest_(manifest.release()), 552 finished_parsing_manifest_(false), 553 display_in_launcher_(true), 554 display_in_new_tab_page_(true), 555 wants_file_access_(false), 556 creation_flags_(0) { 557 DCHECK(path.empty() || path.IsAbsolute()); 558 path_ = id_util::MaybeNormalizePath(path); 559 } 560 561 Extension::~Extension() { 562 } 563 564 bool Extension::InitFromValue(int flags, string16* error) { 565 DCHECK(error); 566 567 creation_flags_ = flags; 568 569 // Important to load manifest version first because many other features 570 // depend on its value. 571 if (!LoadManifestVersion(error)) 572 return false; 573 574 if (!LoadRequiredFeatures(error)) 575 return false; 576 577 // We don't need to validate because InitExtensionID already did that. 578 manifest_->GetString(keys::kPublicKey, &public_key_); 579 580 extension_url_ = Extension::GetBaseURLFromExtensionId(id()); 581 582 // Load App settings. LoadExtent at least has to be done before 583 // ParsePermissions(), because the valid permissions depend on what type of 584 // package this is. 585 if (is_app() && !LoadAppFeatures(error)) 586 return false; 587 588 permissions_data_.reset(new PermissionsData); 589 if (!permissions_data_->ParsePermissions(this, error)) 590 return false; 591 592 if (manifest_->HasKey(keys::kConvertedFromUserScript)) { 593 manifest_->GetBoolean(keys::kConvertedFromUserScript, 594 &converted_from_user_script_); 595 } 596 597 if (!LoadSharedFeatures(error)) 598 return false; 599 600 finished_parsing_manifest_ = true; 601 602 permissions_data_->FinalizePermissions(this); 603 604 return true; 605 } 606 607 bool Extension::LoadRequiredFeatures(string16* error) { 608 if (!LoadName(error) || 609 !LoadVersion(error)) 610 return false; 611 return true; 612 } 613 614 bool Extension::LoadName(string16* error) { 615 string16 localized_name; 616 if (!manifest_->GetString(keys::kName, &localized_name)) { 617 *error = ASCIIToUTF16(errors::kInvalidName); 618 return false; 619 } 620 non_localized_name_ = UTF16ToUTF8(localized_name); 621 base::i18n::AdjustStringForLocaleDirection(&localized_name); 622 name_ = UTF16ToUTF8(localized_name); 623 return true; 624 } 625 626 bool Extension::LoadVersion(string16* error) { 627 std::string version_str; 628 if (!manifest_->GetString(keys::kVersion, &version_str)) { 629 *error = ASCIIToUTF16(errors::kInvalidVersion); 630 return false; 631 } 632 version_.reset(new Version(version_str)); 633 if (!version_->IsValid() || version_->components().size() > 4) { 634 *error = ASCIIToUTF16(errors::kInvalidVersion); 635 return false; 636 } 637 return true; 638 } 639 640 bool Extension::LoadAppFeatures(string16* error) { 641 if (!LoadExtent(keys::kWebURLs, &extent_, 642 errors::kInvalidWebURLs, errors::kInvalidWebURL, error)) { 643 return false; 644 } 645 if (manifest_->HasKey(keys::kDisplayInLauncher) && 646 !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) { 647 *error = ASCIIToUTF16(errors::kInvalidDisplayInLauncher); 648 return false; 649 } 650 if (manifest_->HasKey(keys::kDisplayInNewTabPage)) { 651 if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage, 652 &display_in_new_tab_page_)) { 653 *error = ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage); 654 return false; 655 } 656 } else { 657 // Inherit default from display_in_launcher property. 658 display_in_new_tab_page_ = display_in_launcher_; 659 } 660 return true; 661 } 662 663 bool Extension::LoadExtent(const char* key, 664 URLPatternSet* extent, 665 const char* list_error, 666 const char* value_error, 667 string16* error) { 668 const base::Value* temp_pattern_value = NULL; 669 if (!manifest_->Get(key, &temp_pattern_value)) 670 return true; 671 672 const base::ListValue* pattern_list = NULL; 673 if (!temp_pattern_value->GetAsList(&pattern_list)) { 674 *error = ASCIIToUTF16(list_error); 675 return false; 676 } 677 678 for (size_t i = 0; i < pattern_list->GetSize(); ++i) { 679 std::string pattern_string; 680 if (!pattern_list->GetString(i, &pattern_string)) { 681 *error = ErrorUtils::FormatErrorMessageUTF16(value_error, 682 base::UintToString(i), 683 errors::kExpectString); 684 return false; 685 } 686 687 URLPattern pattern(kValidWebExtentSchemes); 688 URLPattern::ParseResult parse_result = pattern.Parse(pattern_string); 689 if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) { 690 pattern_string += "/"; 691 parse_result = pattern.Parse(pattern_string); 692 } 693 694 if (parse_result != URLPattern::PARSE_SUCCESS) { 695 *error = ErrorUtils::FormatErrorMessageUTF16( 696 value_error, 697 base::UintToString(i), 698 URLPattern::GetParseResultString(parse_result)); 699 return false; 700 } 701 702 // Do not allow authors to claim "<all_urls>". 703 if (pattern.match_all_urls()) { 704 *error = ErrorUtils::FormatErrorMessageUTF16( 705 value_error, 706 base::UintToString(i), 707 errors::kCannotClaimAllURLsInExtent); 708 return false; 709 } 710 711 // Do not allow authors to claim "*" for host. 712 if (pattern.host().empty()) { 713 *error = ErrorUtils::FormatErrorMessageUTF16( 714 value_error, 715 base::UintToString(i), 716 errors::kCannotClaimAllHostsInExtent); 717 return false; 718 } 719 720 // We do not allow authors to put wildcards in their paths. Instead, we 721 // imply one at the end. 722 if (pattern.path().find('*') != std::string::npos) { 723 *error = ErrorUtils::FormatErrorMessageUTF16( 724 value_error, 725 base::UintToString(i), 726 errors::kNoWildCardsInPaths); 727 return false; 728 } 729 pattern.SetPath(pattern.path() + '*'); 730 731 extent->AddPattern(pattern); 732 } 733 734 return true; 735 } 736 737 bool Extension::LoadSharedFeatures(string16* error) { 738 if (!LoadDescription(error) || 739 !ManifestHandler::ParseExtension(this, error)) 740 return false; 741 742 return true; 743 } 744 745 bool Extension::LoadDescription(string16* error) { 746 if (manifest_->HasKey(keys::kDescription) && 747 !manifest_->GetString(keys::kDescription, &description_)) { 748 *error = ASCIIToUTF16(errors::kInvalidDescription); 749 return false; 750 } 751 return true; 752 } 753 754 bool Extension::LoadManifestVersion(string16* error) { 755 // Get the original value out of the dictionary so that we can validate it 756 // more strictly. 757 if (manifest_->value()->HasKey(keys::kManifestVersion)) { 758 int manifest_version = 1; 759 if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) || 760 manifest_version < 1) { 761 *error = ASCIIToUTF16(errors::kInvalidManifestVersion); 762 return false; 763 } 764 } 765 766 manifest_version_ = manifest_->GetManifestVersion(); 767 if (creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION && 768 manifest_version_ < kModernManifestVersion && 769 !CommandLine::ForCurrentProcess()->HasSwitch( 770 switches::kAllowLegacyExtensionManifests)) { 771 *error = ErrorUtils::FormatErrorMessageUTF16( 772 errors::kInvalidManifestVersionOld, 773 base::IntToString(kModernManifestVersion)); 774 return false; 775 } 776 777 return true; 778 } 779 780 ExtensionInfo::ExtensionInfo(const base::DictionaryValue* manifest, 781 const std::string& id, 782 const base::FilePath& path, 783 Manifest::Location location) 784 : extension_id(id), 785 extension_path(path), 786 extension_location(location) { 787 if (manifest) 788 extension_manifest.reset(manifest->DeepCopy()); 789 } 790 791 ExtensionInfo::~ExtensionInfo() {} 792 793 InstalledExtensionInfo::InstalledExtensionInfo( 794 const Extension* extension, 795 bool is_update, 796 const std::string& old_name) 797 : extension(extension), 798 is_update(is_update), 799 old_name(old_name) {} 800 801 UnloadedExtensionInfo::UnloadedExtensionInfo( 802 const Extension* extension, 803 extension_misc::UnloadedExtensionReason reason) 804 : reason(reason), 805 extension(extension) {} 806 807 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( 808 const Extension* extension, 809 const PermissionSet* permissions, 810 Reason reason) 811 : reason(reason), 812 extension(extension), 813 permissions(permissions) {} 814 815 } // namespace extensions 816