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