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