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 39namespace extensions { 40 41namespace keys = manifest_keys; 42namespace values = manifest_values; 43namespace errors = manifest_errors; 44 45namespace { 46 47const int kModernManifestVersion = 2; 48const int kPEMOutputColumns = 64; 49 50// KEY MARKERS 51const char kKeyBeginHeaderMarker[] = "-----BEGIN"; 52const char kKeyBeginFooterMarker[] = "-----END"; 53const char kKeyInfoEndMarker[] = "KEY-----"; 54const char kPublic[] = "PUBLIC"; 55const char kPrivate[] = "PRIVATE"; 56 57bool 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 70const int Extension::kInitFromValueFlagBits = 13; 71 72const char Extension::kMimeType[] = "application/x-chrome-extension"; 73 74const int Extension::kValidWebExtentSchemes = 75 URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; 76 77const 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 88scoped_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. 103scoped_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 136Manifest::Type Extension::GetType() const { 137 return converted_from_user_script() ? 138 Manifest::TYPE_USER_SCRIPT : manifest_->type(); 139} 140 141// static 142GURL 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 161bool Extension::ResourceMatches(const URLPatternSet& pattern_set, 162 const std::string& resource) const { 163 return pattern_set.MatchesURL(extension_url_.Resolve(resource)); 164} 165 166ExtensionResource 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 183ExtensionResource 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 198bool 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 229bool 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 238bool 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 268GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { 269 return GURL(std::string(extensions::kExtensionScheme) + 270 url::kStandardSchemeSeparator + extension_id + "/"); 271} 272 273bool 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 282bool 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 302bool Extension::RequiresSortOrdinal() const { 303 return is_app() && (display_in_launcher_ || display_in_new_tab_page_); 304} 305 306bool Extension::ShouldDisplayInAppLauncher() const { 307 // Only apps should be displayed in the launcher. 308 return is_app() && display_in_launcher_; 309} 310 311bool Extension::ShouldDisplayInNewTabPage() const { 312 // Only apps should be displayed on the NTP. 313 return is_app() && display_in_new_tab_page_; 314} 315 316bool 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 340bool 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 360Extension::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 369void 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 375Manifest::Location Extension::location() const { 376 return manifest_->location(); 377} 378 379const std::string& Extension::id() const { 380 return manifest_->extension_id(); 381} 382 383const std::string Extension::VersionString() const { 384 return version()->GetString(); 385} 386 387void Extension::AddInstallWarning(const InstallWarning& new_warning) { 388 install_warnings_.push_back(new_warning); 389} 390 391void 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 397bool Extension::is_app() const { 398 return manifest()->is_app(); 399} 400 401bool Extension::is_platform_app() const { 402 return manifest()->is_platform_app(); 403} 404 405bool Extension::is_hosted_app() const { 406 return manifest()->is_hosted_app(); 407} 408 409bool Extension::is_legacy_packaged_app() const { 410 return manifest()->is_legacy_packaged_app(); 411} 412 413bool Extension::is_extension() const { 414 return manifest()->is_extension(); 415} 416 417bool Extension::is_shared_module() const { 418 return manifest()->is_shared_module(); 419} 420 421bool Extension::is_theme() const { 422 return manifest()->is_theme(); 423} 424 425bool 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 430void 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 439bool 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 479Extension::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 493Extension::~Extension() { 494} 495 496bool 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 542bool Extension::LoadRequiredFeatures(base::string16* error) { 543 if (!LoadName(error) || 544 !LoadVersion(error)) 545 return false; 546 return true; 547} 548 549bool 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 561bool 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 575bool 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 598bool 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 672bool 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 681bool 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 690bool 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 718bool 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 735ExtensionInfo::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 746ExtensionInfo::~ExtensionInfo() {} 747 748InstalledExtensionInfo::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 758UnloadedExtensionInfo::UnloadedExtensionInfo( 759 const Extension* extension, 760 UnloadedExtensionInfo::Reason reason) 761 : reason(reason), 762 extension(extension) {} 763 764UpdatedExtensionPermissionsInfo::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