Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / extensions / common / extension.cc
blob8aefc6af954bef0c8ff3b23e2b83debd4f06667a
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::kValidBookmarkAppSchemes = URLPattern::SCHEME_HTTP |
79 URLPattern::SCHEME_HTTPS |
80 URLPattern::SCHEME_EXTENSION;
82 const int Extension::kValidHostPermissionSchemes = URLPattern::SCHEME_CHROMEUI |
83 URLPattern::SCHEME_HTTP |
84 URLPattern::SCHEME_HTTPS |
85 URLPattern::SCHEME_FILE |
86 URLPattern::SCHEME_FTP;
89 // Extension
92 // static
93 scoped_refptr<Extension> Extension::Create(const base::FilePath& path,
94 Manifest::Location location,
95 const base::DictionaryValue& value,
96 int flags,
97 std::string* utf8_error) {
98 return Extension::Create(path,
99 location,
100 value,
101 flags,
102 std::string(), // ID is ignored if empty.
103 utf8_error);
106 // TODO(sungguk): Continue removing std::string errors and replacing
107 // with base::string16. See http://crbug.com/71980.
108 scoped_refptr<Extension> Extension::Create(const base::FilePath& path,
109 Manifest::Location location,
110 const base::DictionaryValue& value,
111 int flags,
112 const std::string& explicit_id,
113 std::string* utf8_error) {
114 DCHECK(utf8_error);
115 base::string16 error;
116 scoped_ptr<extensions::Manifest> manifest(
117 new extensions::Manifest(
118 location, scoped_ptr<base::DictionaryValue>(value.DeepCopy())));
120 if (!InitExtensionID(manifest.get(), path, explicit_id, flags, &error)) {
121 *utf8_error = base::UTF16ToUTF8(error);
122 return NULL;
125 std::vector<InstallWarning> install_warnings;
126 if (!manifest->ValidateManifest(utf8_error, &install_warnings)) {
127 return NULL;
130 scoped_refptr<Extension> extension = new Extension(path, manifest.Pass());
131 extension->install_warnings_.swap(install_warnings);
133 if (!extension->InitFromValue(flags, &error)) {
134 *utf8_error = base::UTF16ToUTF8(error);
135 return NULL;
138 return extension;
141 Manifest::Type Extension::GetType() const {
142 return converted_from_user_script() ?
143 Manifest::TYPE_USER_SCRIPT : manifest_->type();
146 // static
147 GURL Extension::GetResourceURL(const GURL& extension_url,
148 const std::string& relative_path) {
149 DCHECK(extension_url.SchemeIs(extensions::kExtensionScheme));
150 DCHECK_EQ("/", extension_url.path());
152 std::string path = relative_path;
154 // If the relative path starts with "/", it is "absolute" relative to the
155 // extension base directory, but extension_url is already specified to refer
156 // to that base directory, so strip the leading "/" if present.
157 if (relative_path.size() > 0 && relative_path[0] == '/')
158 path = relative_path.substr(1);
160 GURL ret_val = GURL(extension_url.spec() + path);
161 DCHECK(base::StartsWith(ret_val.spec(), extension_url.spec(),
162 base::CompareCase::INSENSITIVE_ASCII));
164 return ret_val;
167 bool Extension::ResourceMatches(const URLPatternSet& pattern_set,
168 const std::string& resource) const {
169 return pattern_set.MatchesURL(extension_url_.Resolve(resource));
172 ExtensionResource Extension::GetResource(
173 const std::string& relative_path) const {
174 std::string new_path = relative_path;
175 // We have some legacy data where resources have leading slashes.
176 // See: http://crbug.com/121164
177 if (!new_path.empty() && new_path.at(0) == '/')
178 new_path.erase(0, 1);
179 base::FilePath relative_file_path = base::FilePath::FromUTF8Unsafe(new_path);
180 if (ContainsReservedCharacters(relative_file_path))
181 return ExtensionResource();
182 ExtensionResource r(id(), path(), relative_file_path);
183 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) {
184 r.set_follow_symlinks_anywhere();
186 return r;
189 ExtensionResource Extension::GetResource(
190 const base::FilePath& relative_file_path) const {
191 if (ContainsReservedCharacters(relative_file_path))
192 return ExtensionResource();
193 ExtensionResource r(id(), path(), relative_file_path);
194 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) {
195 r.set_follow_symlinks_anywhere();
197 return r;
200 // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a
201 // util class in base:
202 // http://code.google.com/p/chromium/issues/detail?id=13572
203 // static
204 bool Extension::ParsePEMKeyBytes(const std::string& input,
205 std::string* output) {
206 DCHECK(output);
207 if (!output)
208 return false;
209 if (input.length() == 0)
210 return false;
212 std::string working = input;
213 if (base::StartsWith(working, kKeyBeginHeaderMarker,
214 base::CompareCase::SENSITIVE)) {
215 working = base::CollapseWhitespaceASCII(working, true);
216 size_t header_pos = working.find(kKeyInfoEndMarker,
217 sizeof(kKeyBeginHeaderMarker) - 1);
218 if (header_pos == std::string::npos)
219 return false;
220 size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1;
221 size_t end_pos = working.rfind(kKeyBeginFooterMarker);
222 if (end_pos == std::string::npos)
223 return false;
224 if (start_pos >= end_pos)
225 return false;
227 working = working.substr(start_pos, end_pos - start_pos);
228 if (working.length() == 0)
229 return false;
232 return base::Base64Decode(working, output);
235 // static
236 bool Extension::ProducePEM(const std::string& input, std::string* output) {
237 DCHECK(output);
238 if (input.empty())
239 return false;
240 base::Base64Encode(input, output);
241 return true;
244 // static
245 bool Extension::FormatPEMForFileOutput(const std::string& input,
246 std::string* output,
247 bool is_public) {
248 DCHECK(output);
249 if (input.length() == 0)
250 return false;
251 *output = "";
252 output->append(kKeyBeginHeaderMarker);
253 output->append(" ");
254 output->append(is_public ? kPublic : kPrivate);
255 output->append(" ");
256 output->append(kKeyInfoEndMarker);
257 output->append("\n");
258 for (size_t i = 0; i < input.length(); ) {
259 int slice = std::min<int>(input.length() - i, kPEMOutputColumns);
260 output->append(input.substr(i, slice));
261 output->append("\n");
262 i += slice;
264 output->append(kKeyBeginFooterMarker);
265 output->append(" ");
266 output->append(is_public ? kPublic : kPrivate);
267 output->append(" ");
268 output->append(kKeyInfoEndMarker);
269 output->append("\n");
271 return true;
274 // static
275 GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) {
276 return GURL(std::string(extensions::kExtensionScheme) +
277 url::kStandardSchemeSeparator + extension_id + "/");
280 bool Extension::ShowConfigureContextMenus() const {
281 // Normally we don't show a context menu for component actions, but when
282 // re-design is enabled we show them in the toolbar (if they have an action),
283 // and it is weird to have a random button that has no context menu when the
284 // rest do.
285 if (location() == Manifest::COMPONENT ||
286 location() == Manifest::EXTERNAL_COMPONENT)
287 return FeatureSwitch::extension_action_redesign()->IsEnabled();
289 return true;
292 bool Extension::OverlapsWithOrigin(const GURL& origin) const {
293 if (url() == origin)
294 return true;
296 if (web_extent().is_empty())
297 return false;
299 // Note: patterns and extents ignore port numbers.
300 URLPattern origin_only_pattern(kValidWebExtentSchemes);
301 if (!origin_only_pattern.SetScheme(origin.scheme()))
302 return false;
303 origin_only_pattern.SetHost(origin.host());
304 origin_only_pattern.SetPath("/*");
306 URLPatternSet origin_only_pattern_list;
307 origin_only_pattern_list.AddPattern(origin_only_pattern);
309 return web_extent().OverlapsWith(origin_only_pattern_list);
312 bool Extension::RequiresSortOrdinal() const {
313 return is_app() && (display_in_launcher_ || display_in_new_tab_page_);
316 bool Extension::ShouldDisplayInAppLauncher() const {
317 // Only apps should be displayed in the launcher.
318 return is_app() && display_in_launcher_;
321 bool Extension::ShouldDisplayInNewTabPage() const {
322 // Only apps should be displayed on the NTP.
323 return is_app() && display_in_new_tab_page_;
326 bool Extension::ShouldDisplayInExtensionSettings() const {
327 // Don't show for themes since the settings UI isn't really useful for them.
328 if (is_theme())
329 return false;
331 // Don't show component extensions and invisible apps.
332 if (ShouldNotBeVisible())
333 return false;
335 // Always show unpacked extensions and apps.
336 if (Manifest::IsUnpackedLocation(location()))
337 return true;
339 // Unless they are unpacked, never show hosted apps. Note: We intentionally
340 // show packaged apps and platform apps because there are some pieces of
341 // functionality that are only available in chrome://extensions/ but which
342 // are needed for packaged and platform apps. For example, inspecting
343 // background pages. See http://crbug.com/116134.
344 if (is_hosted_app())
345 return false;
347 return true;
350 bool Extension::ShouldNotBeVisible() const {
351 // Don't show component extensions because they are only extensions as an
352 // implementation detail of Chrome.
353 if (extensions::Manifest::IsComponentLocation(location()) &&
354 !base::CommandLine::ForCurrentProcess()->HasSwitch(
355 switches::kShowComponentExtensionOptions)) {
356 return true;
359 // Always show unpacked extensions and apps.
360 if (Manifest::IsUnpackedLocation(location()))
361 return false;
363 // Don't show apps that aren't visible in either launcher or ntp.
364 if (is_app() && !ShouldDisplayInAppLauncher() && !ShouldDisplayInNewTabPage())
365 return true;
367 return false;
370 Extension::ManifestData* Extension::GetManifestData(const std::string& key)
371 const {
372 DCHECK(finished_parsing_manifest_ || thread_checker_.CalledOnValidThread());
373 ManifestDataMap::const_iterator iter = manifest_data_.find(key);
374 if (iter != manifest_data_.end())
375 return iter->second.get();
376 return NULL;
379 void Extension::SetManifestData(const std::string& key,
380 Extension::ManifestData* data) {
381 DCHECK(!finished_parsing_manifest_ && thread_checker_.CalledOnValidThread());
382 manifest_data_[key] = linked_ptr<ManifestData>(data);
385 Manifest::Location Extension::location() const {
386 return manifest_->location();
389 const std::string& Extension::id() const {
390 return manifest_->extension_id();
393 const std::string Extension::VersionString() const {
394 return version()->GetString();
397 const std::string Extension::GetVersionForDisplay() const {
398 if (version_name_.size() > 0)
399 return version_name_;
400 return VersionString();
403 void Extension::AddInstallWarning(const InstallWarning& new_warning) {
404 install_warnings_.push_back(new_warning);
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());
413 bool Extension::is_app() const {
414 return manifest()->is_app();
417 bool Extension::is_platform_app() const {
418 return manifest()->is_platform_app();
421 bool Extension::is_hosted_app() const {
422 return manifest()->is_hosted_app();
425 bool Extension::is_legacy_packaged_app() const {
426 return manifest()->is_legacy_packaged_app();
429 bool Extension::is_extension() const {
430 return manifest()->is_extension();
433 bool Extension::is_shared_module() const {
434 return manifest()->is_shared_module();
437 bool Extension::is_theme() const {
438 return manifest()->is_theme();
441 bool Extension::can_be_incognito_enabled() const {
442 // Only component platform apps are supported in incognito.
443 return !is_platform_app() || location() == Manifest::COMPONENT;
446 void Extension::AddWebExtentPattern(const URLPattern& pattern) {
447 // Bookmark apps are permissionless.
448 if (from_bookmark())
449 return;
451 extent_.AddPattern(pattern);
454 // static
455 bool Extension::InitExtensionID(extensions::Manifest* manifest,
456 const base::FilePath& path,
457 const std::string& explicit_id,
458 int creation_flags,
459 base::string16* error) {
460 if (!explicit_id.empty()) {
461 manifest->set_extension_id(explicit_id);
462 return true;
465 if (manifest->HasKey(keys::kPublicKey)) {
466 std::string public_key;
467 std::string public_key_bytes;
468 if (!manifest->GetString(keys::kPublicKey, &public_key) ||
469 !ParsePEMKeyBytes(public_key, &public_key_bytes)) {
470 *error = base::ASCIIToUTF16(errors::kInvalidKey);
471 return false;
473 std::string extension_id = crx_file::id_util::GenerateId(public_key_bytes);
474 manifest->set_extension_id(extension_id);
475 return true;
478 if (creation_flags & REQUIRE_KEY) {
479 *error = base::ASCIIToUTF16(errors::kInvalidKey);
480 return false;
481 } else {
482 // If there is a path, we generate the ID from it. This is useful for
483 // development mode, because it keeps the ID stable across restarts and
484 // reloading the extension.
485 std::string extension_id = crx_file::id_util::GenerateIdForPath(path);
486 if (extension_id.empty()) {
487 NOTREACHED() << "Could not create ID from path.";
488 return false;
490 manifest->set_extension_id(extension_id);
491 return true;
495 Extension::Extension(const base::FilePath& path,
496 scoped_ptr<extensions::Manifest> manifest)
497 : manifest_version_(0),
498 converted_from_user_script_(false),
499 manifest_(manifest.release()),
500 finished_parsing_manifest_(false),
501 display_in_launcher_(true),
502 display_in_new_tab_page_(true),
503 wants_file_access_(false),
504 creation_flags_(0) {
505 DCHECK(path.empty() || path.IsAbsolute());
506 path_ = crx_file::id_util::MaybeNormalizePath(path);
509 Extension::~Extension() {
512 bool Extension::InitFromValue(int flags, base::string16* error) {
513 DCHECK(error);
515 creation_flags_ = flags;
517 // Important to load manifest version first because many other features
518 // depend on its value.
519 if (!LoadManifestVersion(error))
520 return false;
522 if (!LoadRequiredFeatures(error))
523 return false;
525 // We don't need to validate because InitExtensionID already did that.
526 manifest_->GetString(keys::kPublicKey, &public_key_);
528 extension_url_ = Extension::GetBaseURLFromExtensionId(id());
530 // Load App settings. LoadExtent at least has to be done before
531 // ParsePermissions(), because the valid permissions depend on what type of
532 // package this is.
533 if (is_app() && !LoadAppFeatures(error))
534 return false;
536 permissions_parser_.reset(new PermissionsParser());
537 if (!permissions_parser_->Parse(this, error))
538 return false;
540 if (manifest_->HasKey(keys::kConvertedFromUserScript)) {
541 manifest_->GetBoolean(keys::kConvertedFromUserScript,
542 &converted_from_user_script_);
545 if (!LoadSharedFeatures(error))
546 return false;
548 permissions_parser_->Finalize(this);
549 permissions_parser_.reset();
551 finished_parsing_manifest_ = true;
553 permissions_data_.reset(new PermissionsData(this));
555 return true;
558 bool Extension::LoadRequiredFeatures(base::string16* error) {
559 if (!LoadName(error) ||
560 !LoadVersion(error))
561 return false;
562 return true;
565 bool Extension::LoadName(base::string16* error) {
566 base::string16 localized_name;
567 if (!manifest_->GetString(keys::kName, &localized_name)) {
568 *error = base::ASCIIToUTF16(errors::kInvalidName);
569 return false;
571 non_localized_name_ = base::UTF16ToUTF8(localized_name);
572 base::i18n::AdjustStringForLocaleDirection(&localized_name);
573 name_ = base::UTF16ToUTF8(localized_name);
574 return true;
577 bool Extension::LoadVersion(base::string16* error) {
578 std::string version_str;
579 if (!manifest_->GetString(keys::kVersion, &version_str)) {
580 *error = base::ASCIIToUTF16(errors::kInvalidVersion);
581 return false;
583 version_.reset(new Version(version_str));
584 if (!version_->IsValid() || version_->components().size() > 4) {
585 *error = base::ASCIIToUTF16(errors::kInvalidVersion);
586 return false;
588 if (manifest_->HasKey(keys::kVersionName)) {
589 if (!manifest_->GetString(keys::kVersionName, &version_name_)) {
590 *error = base::ASCIIToUTF16(errors::kInvalidVersionName);
591 return false;
594 return true;
597 bool Extension::LoadAppFeatures(base::string16* error) {
598 if (!LoadExtent(keys::kWebURLs, &extent_,
599 errors::kInvalidWebURLs, errors::kInvalidWebURL, error)) {
600 return false;
602 if (manifest_->HasKey(keys::kDisplayInLauncher) &&
603 !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) {
604 *error = base::ASCIIToUTF16(errors::kInvalidDisplayInLauncher);
605 return false;
607 if (manifest_->HasKey(keys::kDisplayInNewTabPage)) {
608 if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage,
609 &display_in_new_tab_page_)) {
610 *error = base::ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage);
611 return false;
613 } else {
614 // Inherit default from display_in_launcher property.
615 display_in_new_tab_page_ = display_in_launcher_;
617 return true;
620 bool Extension::LoadExtent(const char* key,
621 URLPatternSet* extent,
622 const char* list_error,
623 const char* value_error,
624 base::string16* error) {
625 const base::Value* temp_pattern_value = NULL;
626 if (!manifest_->Get(key, &temp_pattern_value))
627 return true;
629 const base::ListValue* pattern_list = NULL;
630 if (!temp_pattern_value->GetAsList(&pattern_list)) {
631 *error = base::ASCIIToUTF16(list_error);
632 return false;
635 for (size_t i = 0; i < pattern_list->GetSize(); ++i) {
636 std::string pattern_string;
637 if (!pattern_list->GetString(i, &pattern_string)) {
638 *error = ErrorUtils::FormatErrorMessageUTF16(value_error,
639 base::UintToString(i),
640 errors::kExpectString);
641 return false;
644 URLPattern pattern(kValidWebExtentSchemes);
645 URLPattern::ParseResult parse_result = pattern.Parse(pattern_string);
646 if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) {
647 pattern_string += "/";
648 parse_result = pattern.Parse(pattern_string);
651 if (parse_result != URLPattern::PARSE_SUCCESS) {
652 *error = ErrorUtils::FormatErrorMessageUTF16(
653 value_error,
654 base::UintToString(i),
655 URLPattern::GetParseResultString(parse_result));
656 return false;
659 // Do not allow authors to claim "<all_urls>".
660 if (pattern.match_all_urls()) {
661 *error = ErrorUtils::FormatErrorMessageUTF16(
662 value_error,
663 base::UintToString(i),
664 errors::kCannotClaimAllURLsInExtent);
665 return false;
668 // Do not allow authors to claim "*" for host.
669 if (pattern.host().empty()) {
670 *error = ErrorUtils::FormatErrorMessageUTF16(
671 value_error,
672 base::UintToString(i),
673 errors::kCannotClaimAllHostsInExtent);
674 return false;
677 // We do not allow authors to put wildcards in their paths. Instead, we
678 // imply one at the end.
679 if (pattern.path().find('*') != std::string::npos) {
680 *error = ErrorUtils::FormatErrorMessageUTF16(
681 value_error,
682 base::UintToString(i),
683 errors::kNoWildCardsInPaths);
684 return false;
686 pattern.SetPath(pattern.path() + '*');
688 extent->AddPattern(pattern);
691 return true;
694 bool Extension::LoadSharedFeatures(base::string16* error) {
695 if (!LoadDescription(error) ||
696 !ManifestHandler::ParseExtension(this, error) ||
697 !LoadShortName(error))
698 return false;
700 return true;
703 bool Extension::LoadDescription(base::string16* error) {
704 if (manifest_->HasKey(keys::kDescription) &&
705 !manifest_->GetString(keys::kDescription, &description_)) {
706 *error = base::ASCIIToUTF16(errors::kInvalidDescription);
707 return false;
709 return true;
712 bool Extension::LoadManifestVersion(base::string16* error) {
713 // Get the original value out of the dictionary so that we can validate it
714 // more strictly.
715 if (manifest_->value()->HasKey(keys::kManifestVersion)) {
716 int manifest_version = 1;
717 if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) ||
718 manifest_version < 1) {
719 *error = base::ASCIIToUTF16(errors::kInvalidManifestVersion);
720 return false;
724 manifest_version_ = manifest_->GetManifestVersion();
725 if (manifest_version_ < kModernManifestVersion &&
726 ((creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION &&
727 !base::CommandLine::ForCurrentProcess()->HasSwitch(
728 switches::kAllowLegacyExtensionManifests)) ||
729 GetType() == Manifest::TYPE_PLATFORM_APP)) {
730 *error = ErrorUtils::FormatErrorMessageUTF16(
731 errors::kInvalidManifestVersionOld,
732 base::IntToString(kModernManifestVersion),
733 is_platform_app() ? "apps" : "extensions");
734 return false;
737 return true;
740 bool Extension::LoadShortName(base::string16* error) {
741 if (manifest_->HasKey(keys::kShortName)) {
742 base::string16 localized_short_name;
743 if (!manifest_->GetString(keys::kShortName, &localized_short_name) ||
744 localized_short_name.empty()) {
745 *error = base::ASCIIToUTF16(errors::kInvalidShortName);
746 return false;
749 base::i18n::AdjustStringForLocaleDirection(&localized_short_name);
750 short_name_ = base::UTF16ToUTF8(localized_short_name);
751 } else {
752 short_name_ = name_;
754 return true;
757 ExtensionInfo::ExtensionInfo(const base::DictionaryValue* manifest,
758 const std::string& id,
759 const base::FilePath& path,
760 Manifest::Location location)
761 : extension_id(id),
762 extension_path(path),
763 extension_location(location) {
764 if (manifest)
765 extension_manifest.reset(manifest->DeepCopy());
768 ExtensionInfo::~ExtensionInfo() {}
770 InstalledExtensionInfo::InstalledExtensionInfo(
771 const Extension* extension,
772 bool is_update,
773 bool from_ephemeral,
774 const std::string& old_name)
775 : extension(extension),
776 is_update(is_update),
777 from_ephemeral(from_ephemeral),
778 old_name(old_name) {}
780 UnloadedExtensionInfo::UnloadedExtensionInfo(
781 const Extension* extension,
782 UnloadedExtensionInfo::Reason reason)
783 : reason(reason),
784 extension(extension) {}
786 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo(
787 const Extension* extension,
788 const PermissionSet* permissions,
789 Reason reason)
790 : reason(reason),
791 extension(extension),
792 permissions(permissions) {}
794 } // namespace extensions