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