Fix sort order of unlaunched apps on app list start page.
[chromium-blink-merge.git] / extensions / common / extension.cc
blob1e973fccc82b2e57b533dae0168271be8fa4a3b9
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.
5 #include "extensions/common/extension.h"
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/feature_switch.h"
28 #include "extensions/common/manifest.h"
29 #include "extensions/common/manifest_constants.h"
30 #include "extensions/common/manifest_handler.h"
31 #include "extensions/common/manifest_handlers/permissions_parser.h"
32 #include "extensions/common/permissions/permission_set.h"
33 #include "extensions/common/permissions/permissions_data.h"
34 #include "extensions/common/permissions/permissions_info.h"
35 #include "extensions/common/switches.h"
36 #include "extensions/common/url_pattern.h"
37 #include "net/base/filename_util.h"
38 #include "url/url_util.h"
40 namespace extensions {
42 namespace keys = manifest_keys;
43 namespace values = manifest_values;
44 namespace errors = manifest_errors;
46 namespace {
48 const int kModernManifestVersion = 2;
49 const int kPEMOutputColumns = 64;
51 // KEY MARKERS
52 const char kKeyBeginHeaderMarker[] = "-----BEGIN";
53 const char kKeyBeginFooterMarker[] = "-----END";
54 const char kKeyInfoEndMarker[] = "KEY-----";
55 const char kPublic[] = "PUBLIC";
56 const char kPrivate[] = "PRIVATE";
58 bool ContainsReservedCharacters(const base::FilePath& path) {
59 // We should disallow backslash '\\' as file path separator even on Windows,
60 // because the backslash is not regarded as file path separator on Linux/Mac.
61 // Extensions are cross-platform.
62 // Since FilePath uses backslash '\\' as file path separator on Windows, so we
63 // need to check manually.
64 if (path.value().find('\\') != path.value().npos)
65 return true;
66 return !net::IsSafePortableRelativePath(path);
69 } // namespace
71 const int Extension::kInitFromValueFlagBits = 13;
73 const char Extension::kMimeType[] = "application/x-chrome-extension";
75 const int Extension::kValidWebExtentSchemes =
76 URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS;
78 const int Extension::kValidHostPermissionSchemes = URLPattern::SCHEME_CHROMEUI |
79 URLPattern::SCHEME_HTTP |
80 URLPattern::SCHEME_HTTPS |
81 URLPattern::SCHEME_FILE |
82 URLPattern::SCHEME_FTP;
85 // Extension
88 // static
89 scoped_refptr<Extension> Extension::Create(const base::FilePath& path,
90 Manifest::Location location,
91 const base::DictionaryValue& value,
92 int flags,
93 std::string* utf8_error) {
94 return Extension::Create(path,
95 location,
96 value,
97 flags,
98 std::string(), // ID is ignored if empty.
99 utf8_error);
102 // TODO(sungguk): Continue removing std::string errors and replacing
103 // with base::string16. See http://crbug.com/71980.
104 scoped_refptr<Extension> Extension::Create(const base::FilePath& path,
105 Manifest::Location location,
106 const base::DictionaryValue& value,
107 int flags,
108 const std::string& explicit_id,
109 std::string* utf8_error) {
110 DCHECK(utf8_error);
111 base::string16 error;
112 scoped_ptr<extensions::Manifest> manifest(
113 new extensions::Manifest(
114 location, scoped_ptr<base::DictionaryValue>(value.DeepCopy())));
116 if (!InitExtensionID(manifest.get(), path, explicit_id, flags, &error)) {
117 *utf8_error = base::UTF16ToUTF8(error);
118 return NULL;
121 std::vector<InstallWarning> install_warnings;
122 if (!manifest->ValidateManifest(utf8_error, &install_warnings)) {
123 return NULL;
126 scoped_refptr<Extension> extension = new Extension(path, manifest.Pass());
127 extension->install_warnings_.swap(install_warnings);
129 if (!extension->InitFromValue(flags, &error)) {
130 *utf8_error = base::UTF16ToUTF8(error);
131 return NULL;
134 return extension;
137 Manifest::Type Extension::GetType() const {
138 return converted_from_user_script() ?
139 Manifest::TYPE_USER_SCRIPT : manifest_->type();
142 // static
143 GURL Extension::GetResourceURL(const GURL& extension_url,
144 const std::string& relative_path) {
145 DCHECK(extension_url.SchemeIs(extensions::kExtensionScheme));
146 DCHECK_EQ("/", extension_url.path());
148 std::string path = relative_path;
150 // If the relative path starts with "/", it is "absolute" relative to the
151 // extension base directory, but extension_url is already specified to refer
152 // to that base directory, so strip the leading "/" if present.
153 if (relative_path.size() > 0 && relative_path[0] == '/')
154 path = relative_path.substr(1);
156 GURL ret_val = GURL(extension_url.spec() + path);
157 DCHECK(StartsWithASCII(ret_val.spec(), extension_url.spec(), false));
159 return ret_val;
162 bool Extension::ResourceMatches(const URLPatternSet& pattern_set,
163 const std::string& resource) const {
164 return pattern_set.MatchesURL(extension_url_.Resolve(resource));
167 ExtensionResource Extension::GetResource(
168 const std::string& relative_path) const {
169 std::string new_path = relative_path;
170 // We have some legacy data where resources have leading slashes.
171 // See: http://crbug.com/121164
172 if (!new_path.empty() && new_path.at(0) == '/')
173 new_path.erase(0, 1);
174 base::FilePath relative_file_path = base::FilePath::FromUTF8Unsafe(new_path);
175 if (ContainsReservedCharacters(relative_file_path))
176 return ExtensionResource();
177 ExtensionResource r(id(), path(), relative_file_path);
178 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) {
179 r.set_follow_symlinks_anywhere();
181 return r;
184 ExtensionResource Extension::GetResource(
185 const base::FilePath& relative_file_path) const {
186 if (ContainsReservedCharacters(relative_file_path))
187 return ExtensionResource();
188 ExtensionResource r(id(), path(), relative_file_path);
189 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) {
190 r.set_follow_symlinks_anywhere();
192 return r;
195 // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a
196 // util class in base:
197 // http://code.google.com/p/chromium/issues/detail?id=13572
198 // static
199 bool Extension::ParsePEMKeyBytes(const std::string& input,
200 std::string* output) {
201 DCHECK(output);
202 if (!output)
203 return false;
204 if (input.length() == 0)
205 return false;
207 std::string working = input;
208 if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) {
209 working = base::CollapseWhitespaceASCII(working, true);
210 size_t header_pos = working.find(kKeyInfoEndMarker,
211 sizeof(kKeyBeginHeaderMarker) - 1);
212 if (header_pos == std::string::npos)
213 return false;
214 size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1;
215 size_t end_pos = working.rfind(kKeyBeginFooterMarker);
216 if (end_pos == std::string::npos)
217 return false;
218 if (start_pos >= end_pos)
219 return false;
221 working = working.substr(start_pos, end_pos - start_pos);
222 if (working.length() == 0)
223 return false;
226 return base::Base64Decode(working, output);
229 // static
230 bool Extension::ProducePEM(const std::string& input, std::string* output) {
231 DCHECK(output);
232 if (input.empty())
233 return false;
234 base::Base64Encode(input, output);
235 return true;
238 // static
239 bool Extension::FormatPEMForFileOutput(const std::string& input,
240 std::string* output,
241 bool is_public) {
242 DCHECK(output);
243 if (input.length() == 0)
244 return false;
245 *output = "";
246 output->append(kKeyBeginHeaderMarker);
247 output->append(" ");
248 output->append(is_public ? kPublic : kPrivate);
249 output->append(" ");
250 output->append(kKeyInfoEndMarker);
251 output->append("\n");
252 for (size_t i = 0; i < input.length(); ) {
253 int slice = std::min<int>(input.length() - i, kPEMOutputColumns);
254 output->append(input.substr(i, slice));
255 output->append("\n");
256 i += slice;
258 output->append(kKeyBeginFooterMarker);
259 output->append(" ");
260 output->append(is_public ? kPublic : kPrivate);
261 output->append(" ");
262 output->append(kKeyInfoEndMarker);
263 output->append("\n");
265 return true;
268 // static
269 GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) {
270 return GURL(std::string(extensions::kExtensionScheme) +
271 url::kStandardSchemeSeparator + extension_id + "/");
274 bool Extension::ShowConfigureContextMenus() const {
275 // Normally we don't show a context menu for component actions, but when
276 // re-design is enabled we show them in the toolbar (if they have an action),
277 // and it is weird to have a random button that has no context menu when the
278 // rest do.
279 if (location() == Manifest::COMPONENT ||
280 location() == Manifest::EXTERNAL_COMPONENT)
281 return FeatureSwitch::extension_action_redesign()->IsEnabled();
283 return true;
286 bool Extension::OverlapsWithOrigin(const GURL& origin) const {
287 if (url() == origin)
288 return true;
290 if (web_extent().is_empty())
291 return false;
293 // Note: patterns and extents ignore port numbers.
294 URLPattern origin_only_pattern(kValidWebExtentSchemes);
295 if (!origin_only_pattern.SetScheme(origin.scheme()))
296 return false;
297 origin_only_pattern.SetHost(origin.host());
298 origin_only_pattern.SetPath("/*");
300 URLPatternSet origin_only_pattern_list;
301 origin_only_pattern_list.AddPattern(origin_only_pattern);
303 return web_extent().OverlapsWith(origin_only_pattern_list);
306 bool Extension::RequiresSortOrdinal() const {
307 return is_app() && (display_in_launcher_ || display_in_new_tab_page_);
310 bool Extension::ShouldDisplayInAppLauncher() const {
311 // Only apps should be displayed in the launcher.
312 return is_app() && display_in_launcher_;
315 bool Extension::ShouldDisplayInNewTabPage() const {
316 // Only apps should be displayed on the NTP.
317 return is_app() && display_in_new_tab_page_;
320 bool Extension::ShouldDisplayInExtensionSettings() const {
321 // Don't show for themes since the settings UI isn't really useful for them.
322 if (is_theme())
323 return false;
325 // Don't show component extensions and invisible apps.
326 if (ShouldNotBeVisible())
327 return false;
329 // Always show unpacked extensions and apps.
330 if (Manifest::IsUnpackedLocation(location()))
331 return true;
333 // Unless they are unpacked, never show hosted apps. Note: We intentionally
334 // show packaged apps and platform apps because there are some pieces of
335 // functionality that are only available in chrome://extensions/ but which
336 // are needed for packaged and platform apps. For example, inspecting
337 // background pages. See http://crbug.com/116134.
338 if (is_hosted_app())
339 return false;
341 return true;
344 bool Extension::ShouldNotBeVisible() const {
345 // Don't show component extensions because they are only extensions as an
346 // implementation detail of Chrome.
347 if (extensions::Manifest::IsComponentLocation(location()) &&
348 !base::CommandLine::ForCurrentProcess()->HasSwitch(
349 switches::kShowComponentExtensionOptions)) {
350 return true;
353 // Always show unpacked extensions and apps.
354 if (Manifest::IsUnpackedLocation(location()))
355 return false;
357 // Don't show apps that aren't visible in either launcher or ntp.
358 if (is_app() && !ShouldDisplayInAppLauncher() && !ShouldDisplayInNewTabPage())
359 return true;
361 return false;
364 Extension::ManifestData* Extension::GetManifestData(const std::string& key)
365 const {
366 DCHECK(finished_parsing_manifest_ || thread_checker_.CalledOnValidThread());
367 ManifestDataMap::const_iterator iter = manifest_data_.find(key);
368 if (iter != manifest_data_.end())
369 return iter->second.get();
370 return NULL;
373 void Extension::SetManifestData(const std::string& key,
374 Extension::ManifestData* data) {
375 DCHECK(!finished_parsing_manifest_ && thread_checker_.CalledOnValidThread());
376 manifest_data_[key] = linked_ptr<ManifestData>(data);
379 Manifest::Location Extension::location() const {
380 return manifest_->location();
383 const std::string& Extension::id() const {
384 return manifest_->extension_id();
387 const std::string Extension::VersionString() const {
388 return version()->GetString();
391 const std::string Extension::GetVersionForDisplay() const {
392 if (version_name_.size() > 0)
393 return version_name_;
394 return VersionString();
397 void Extension::AddInstallWarning(const InstallWarning& new_warning) {
398 install_warnings_.push_back(new_warning);
401 void Extension::AddInstallWarnings(
402 const std::vector<InstallWarning>& new_warnings) {
403 install_warnings_.insert(install_warnings_.end(),
404 new_warnings.begin(), new_warnings.end());
407 bool Extension::is_app() const {
408 return manifest()->is_app();
411 bool Extension::is_platform_app() const {
412 return manifest()->is_platform_app();
415 bool Extension::is_hosted_app() const {
416 return manifest()->is_hosted_app();
419 bool Extension::is_legacy_packaged_app() const {
420 return manifest()->is_legacy_packaged_app();
423 bool Extension::is_extension() const {
424 return manifest()->is_extension();
427 bool Extension::is_shared_module() const {
428 return manifest()->is_shared_module();
431 bool Extension::is_theme() const {
432 return manifest()->is_theme();
435 bool Extension::can_be_incognito_enabled() const {
436 // Only component platform apps are supported in incognito.
437 return !is_platform_app() || location() == Manifest::COMPONENT;
440 void Extension::AddWebExtentPattern(const URLPattern& pattern) {
441 // Bookmark apps are permissionless.
442 if (from_bookmark())
443 return;
445 extent_.AddPattern(pattern);
448 // static
449 bool Extension::InitExtensionID(extensions::Manifest* manifest,
450 const base::FilePath& path,
451 const std::string& explicit_id,
452 int creation_flags,
453 base::string16* error) {
454 if (!explicit_id.empty()) {
455 manifest->set_extension_id(explicit_id);
456 return true;
459 if (manifest->HasKey(keys::kPublicKey)) {
460 std::string public_key;
461 std::string public_key_bytes;
462 if (!manifest->GetString(keys::kPublicKey, &public_key) ||
463 !ParsePEMKeyBytes(public_key, &public_key_bytes)) {
464 *error = base::ASCIIToUTF16(errors::kInvalidKey);
465 return false;
467 std::string extension_id = crx_file::id_util::GenerateId(public_key_bytes);
468 manifest->set_extension_id(extension_id);
469 return true;
472 if (creation_flags & REQUIRE_KEY) {
473 *error = base::ASCIIToUTF16(errors::kInvalidKey);
474 return false;
475 } else {
476 // If there is a path, we generate the ID from it. This is useful for
477 // development mode, because it keeps the ID stable across restarts and
478 // reloading the extension.
479 std::string extension_id = crx_file::id_util::GenerateIdForPath(path);
480 if (extension_id.empty()) {
481 NOTREACHED() << "Could not create ID from path.";
482 return false;
484 manifest->set_extension_id(extension_id);
485 return true;
489 Extension::Extension(const base::FilePath& path,
490 scoped_ptr<extensions::Manifest> manifest)
491 : manifest_version_(0),
492 converted_from_user_script_(false),
493 manifest_(manifest.release()),
494 finished_parsing_manifest_(false),
495 display_in_launcher_(true),
496 display_in_new_tab_page_(true),
497 wants_file_access_(false),
498 creation_flags_(0) {
499 DCHECK(path.empty() || path.IsAbsolute());
500 path_ = crx_file::id_util::MaybeNormalizePath(path);
503 Extension::~Extension() {
506 bool Extension::InitFromValue(int flags, base::string16* error) {
507 DCHECK(error);
509 creation_flags_ = flags;
511 // Important to load manifest version first because many other features
512 // depend on its value.
513 if (!LoadManifestVersion(error))
514 return false;
516 if (!LoadRequiredFeatures(error))
517 return false;
519 // We don't need to validate because InitExtensionID already did that.
520 manifest_->GetString(keys::kPublicKey, &public_key_);
522 extension_url_ = Extension::GetBaseURLFromExtensionId(id());
524 // Load App settings. LoadExtent at least has to be done before
525 // ParsePermissions(), because the valid permissions depend on what type of
526 // package this is.
527 if (is_app() && !LoadAppFeatures(error))
528 return false;
530 permissions_parser_.reset(new PermissionsParser());
531 if (!permissions_parser_->Parse(this, error))
532 return false;
534 if (manifest_->HasKey(keys::kConvertedFromUserScript)) {
535 manifest_->GetBoolean(keys::kConvertedFromUserScript,
536 &converted_from_user_script_);
539 if (!LoadSharedFeatures(error))
540 return false;
542 permissions_parser_->Finalize(this);
543 permissions_parser_.reset();
545 finished_parsing_manifest_ = true;
547 permissions_data_.reset(new PermissionsData(this));
549 return true;
552 bool Extension::LoadRequiredFeatures(base::string16* error) {
553 if (!LoadName(error) ||
554 !LoadVersion(error))
555 return false;
556 return true;
559 bool Extension::LoadName(base::string16* error) {
560 base::string16 localized_name;
561 if (!manifest_->GetString(keys::kName, &localized_name)) {
562 *error = base::ASCIIToUTF16(errors::kInvalidName);
563 return false;
565 non_localized_name_ = base::UTF16ToUTF8(localized_name);
566 base::i18n::AdjustStringForLocaleDirection(&localized_name);
567 name_ = base::UTF16ToUTF8(localized_name);
568 return true;
571 bool Extension::LoadVersion(base::string16* error) {
572 std::string version_str;
573 if (!manifest_->GetString(keys::kVersion, &version_str)) {
574 *error = base::ASCIIToUTF16(errors::kInvalidVersion);
575 return false;
577 version_.reset(new Version(version_str));
578 if (!version_->IsValid() || version_->components().size() > 4) {
579 *error = base::ASCIIToUTF16(errors::kInvalidVersion);
580 return false;
582 if (manifest_->HasKey(keys::kVersionName)) {
583 if (!manifest_->GetString(keys::kVersionName, &version_name_)) {
584 *error = base::ASCIIToUTF16(errors::kInvalidVersionName);
585 return false;
588 return true;
591 bool Extension::LoadAppFeatures(base::string16* error) {
592 if (!LoadExtent(keys::kWebURLs, &extent_,
593 errors::kInvalidWebURLs, errors::kInvalidWebURL, error)) {
594 return false;
596 if (manifest_->HasKey(keys::kDisplayInLauncher) &&
597 !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) {
598 *error = base::ASCIIToUTF16(errors::kInvalidDisplayInLauncher);
599 return false;
601 if (manifest_->HasKey(keys::kDisplayInNewTabPage)) {
602 if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage,
603 &display_in_new_tab_page_)) {
604 *error = base::ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage);
605 return false;
607 } else {
608 // Inherit default from display_in_launcher property.
609 display_in_new_tab_page_ = display_in_launcher_;
611 return true;
614 bool Extension::LoadExtent(const char* key,
615 URLPatternSet* extent,
616 const char* list_error,
617 const char* value_error,
618 base::string16* error) {
619 const base::Value* temp_pattern_value = NULL;
620 if (!manifest_->Get(key, &temp_pattern_value))
621 return true;
623 const base::ListValue* pattern_list = NULL;
624 if (!temp_pattern_value->GetAsList(&pattern_list)) {
625 *error = base::ASCIIToUTF16(list_error);
626 return false;
629 for (size_t i = 0; i < pattern_list->GetSize(); ++i) {
630 std::string pattern_string;
631 if (!pattern_list->GetString(i, &pattern_string)) {
632 *error = ErrorUtils::FormatErrorMessageUTF16(value_error,
633 base::UintToString(i),
634 errors::kExpectString);
635 return false;
638 URLPattern pattern(kValidWebExtentSchemes);
639 URLPattern::ParseResult parse_result = pattern.Parse(pattern_string);
640 if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) {
641 pattern_string += "/";
642 parse_result = pattern.Parse(pattern_string);
645 if (parse_result != URLPattern::PARSE_SUCCESS) {
646 *error = ErrorUtils::FormatErrorMessageUTF16(
647 value_error,
648 base::UintToString(i),
649 URLPattern::GetParseResultString(parse_result));
650 return false;
653 // Do not allow authors to claim "<all_urls>".
654 if (pattern.match_all_urls()) {
655 *error = ErrorUtils::FormatErrorMessageUTF16(
656 value_error,
657 base::UintToString(i),
658 errors::kCannotClaimAllURLsInExtent);
659 return false;
662 // Do not allow authors to claim "*" for host.
663 if (pattern.host().empty()) {
664 *error = ErrorUtils::FormatErrorMessageUTF16(
665 value_error,
666 base::UintToString(i),
667 errors::kCannotClaimAllHostsInExtent);
668 return false;
671 // We do not allow authors to put wildcards in their paths. Instead, we
672 // imply one at the end.
673 if (pattern.path().find('*') != std::string::npos) {
674 *error = ErrorUtils::FormatErrorMessageUTF16(
675 value_error,
676 base::UintToString(i),
677 errors::kNoWildCardsInPaths);
678 return false;
680 pattern.SetPath(pattern.path() + '*');
682 extent->AddPattern(pattern);
685 return true;
688 bool Extension::LoadSharedFeatures(base::string16* error) {
689 if (!LoadDescription(error) ||
690 !ManifestHandler::ParseExtension(this, error) ||
691 !LoadShortName(error))
692 return false;
694 return true;
697 bool Extension::LoadDescription(base::string16* error) {
698 if (manifest_->HasKey(keys::kDescription) &&
699 !manifest_->GetString(keys::kDescription, &description_)) {
700 *error = base::ASCIIToUTF16(errors::kInvalidDescription);
701 return false;
703 return true;
706 bool Extension::LoadManifestVersion(base::string16* error) {
707 // Get the original value out of the dictionary so that we can validate it
708 // more strictly.
709 if (manifest_->value()->HasKey(keys::kManifestVersion)) {
710 int manifest_version = 1;
711 if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) ||
712 manifest_version < 1) {
713 *error = base::ASCIIToUTF16(errors::kInvalidManifestVersion);
714 return false;
718 manifest_version_ = manifest_->GetManifestVersion();
719 if (manifest_version_ < kModernManifestVersion &&
720 ((creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION &&
721 !base::CommandLine::ForCurrentProcess()->HasSwitch(
722 switches::kAllowLegacyExtensionManifests)) ||
723 GetType() == Manifest::TYPE_PLATFORM_APP)) {
724 *error = ErrorUtils::FormatErrorMessageUTF16(
725 errors::kInvalidManifestVersionOld,
726 base::IntToString(kModernManifestVersion),
727 is_platform_app() ? "apps" : "extensions");
728 return false;
731 return true;
734 bool Extension::LoadShortName(base::string16* error) {
735 if (manifest_->HasKey(keys::kShortName)) {
736 base::string16 localized_short_name;
737 if (!manifest_->GetString(keys::kShortName, &localized_short_name) ||
738 localized_short_name.empty()) {
739 *error = base::ASCIIToUTF16(errors::kInvalidShortName);
740 return false;
743 base::i18n::AdjustStringForLocaleDirection(&localized_short_name);
744 short_name_ = base::UTF16ToUTF8(localized_short_name);
745 } else {
746 short_name_ = name_;
748 return true;
751 ExtensionInfo::ExtensionInfo(const base::DictionaryValue* manifest,
752 const std::string& id,
753 const base::FilePath& path,
754 Manifest::Location location)
755 : extension_id(id),
756 extension_path(path),
757 extension_location(location) {
758 if (manifest)
759 extension_manifest.reset(manifest->DeepCopy());
762 ExtensionInfo::~ExtensionInfo() {}
764 InstalledExtensionInfo::InstalledExtensionInfo(
765 const Extension* extension,
766 bool is_update,
767 bool from_ephemeral,
768 const std::string& old_name)
769 : extension(extension),
770 is_update(is_update),
771 from_ephemeral(from_ephemeral),
772 old_name(old_name) {}
774 UnloadedExtensionInfo::UnloadedExtensionInfo(
775 const Extension* extension,
776 UnloadedExtensionInfo::Reason reason)
777 : reason(reason),
778 extension(extension) {}
780 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo(
781 const Extension* extension,
782 const PermissionSet* permissions,
783 Reason reason)
784 : reason(reason),
785 extension(extension),
786 permissions(permissions) {}
788 } // namespace extensions