Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / extensions / common / extension.cc
blob9328ba2f2d85bb8c4191d0d185d848b4ef20a234
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/incognito_info.h"
32 #include "extensions/common/manifest_handlers/permissions_parser.h"
33 #include "extensions/common/permissions/permission_set.h"
34 #include "extensions/common/permissions/permissions_data.h"
35 #include "extensions/common/permissions/permissions_info.h"
36 #include "extensions/common/switches.h"
37 #include "extensions/common/url_pattern.h"
38 #include "net/base/filename_util.h"
39 #include "url/url_util.h"
41 namespace extensions {
43 namespace keys = manifest_keys;
44 namespace values = manifest_values;
45 namespace errors = manifest_errors;
47 namespace {
49 const int kModernManifestVersion = 2;
50 const int kPEMOutputColumns = 64;
52 // KEY MARKERS
53 const char kKeyBeginHeaderMarker[] = "-----BEGIN";
54 const char kKeyBeginFooterMarker[] = "-----END";
55 const char kKeyInfoEndMarker[] = "KEY-----";
56 const char kPublic[] = "PUBLIC";
57 const char kPrivate[] = "PRIVATE";
59 bool ContainsReservedCharacters(const base::FilePath& path) {
60 // We should disallow backslash '\\' as file path separator even on Windows,
61 // because the backslash is not regarded as file path separator on Linux/Mac.
62 // Extensions are cross-platform.
63 // Since FilePath uses backslash '\\' as file path separator on Windows, so we
64 // need to check manually.
65 if (path.value().find('\\') != path.value().npos)
66 return true;
67 return !net::IsSafePortableRelativePath(path);
70 } // namespace
72 const int Extension::kInitFromValueFlagBits = 13;
74 const char Extension::kMimeType[] = "application/x-chrome-extension";
76 const int Extension::kValidWebExtentSchemes =
77 URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS;
79 const int Extension::kValidBookmarkAppSchemes = URLPattern::SCHEME_HTTP |
80 URLPattern::SCHEME_HTTPS |
81 URLPattern::SCHEME_EXTENSION;
83 const int Extension::kValidHostPermissionSchemes = URLPattern::SCHEME_CHROMEUI |
84 URLPattern::SCHEME_HTTP |
85 URLPattern::SCHEME_HTTPS |
86 URLPattern::SCHEME_FILE |
87 URLPattern::SCHEME_FTP;
90 // Extension
93 // static
94 scoped_refptr<Extension> Extension::Create(const base::FilePath& path,
95 Manifest::Location location,
96 const base::DictionaryValue& value,
97 int flags,
98 std::string* utf8_error) {
99 return Extension::Create(path,
100 location,
101 value,
102 flags,
103 std::string(), // ID is ignored if empty.
104 utf8_error);
107 // TODO(sungguk): Continue removing std::string errors and replacing
108 // with base::string16. See http://crbug.com/71980.
109 scoped_refptr<Extension> Extension::Create(const base::FilePath& path,
110 Manifest::Location location,
111 const base::DictionaryValue& value,
112 int flags,
113 const std::string& explicit_id,
114 std::string* utf8_error) {
115 DCHECK(utf8_error);
116 base::string16 error;
117 scoped_ptr<extensions::Manifest> manifest(
118 new extensions::Manifest(
119 location, scoped_ptr<base::DictionaryValue>(value.DeepCopy())));
121 if (!InitExtensionID(manifest.get(), path, explicit_id, flags, &error)) {
122 *utf8_error = base::UTF16ToUTF8(error);
123 return NULL;
126 std::vector<InstallWarning> install_warnings;
127 if (!manifest->ValidateManifest(utf8_error, &install_warnings)) {
128 return NULL;
131 scoped_refptr<Extension> extension = new Extension(path, manifest.Pass());
132 extension->install_warnings_.swap(install_warnings);
134 if (!extension->InitFromValue(flags, &error)) {
135 *utf8_error = base::UTF16ToUTF8(error);
136 return NULL;
139 return extension;
142 Manifest::Type Extension::GetType() const {
143 return converted_from_user_script() ?
144 Manifest::TYPE_USER_SCRIPT : manifest_->type();
147 // static
148 GURL Extension::GetResourceURL(const GURL& extension_url,
149 const std::string& relative_path) {
150 DCHECK(extension_url.SchemeIs(extensions::kExtensionScheme));
151 DCHECK_EQ("/", extension_url.path());
153 std::string path = relative_path;
155 // If the relative path starts with "/", it is "absolute" relative to the
156 // extension base directory, but extension_url is already specified to refer
157 // to that base directory, so strip the leading "/" if present.
158 if (relative_path.size() > 0 && relative_path[0] == '/')
159 path = relative_path.substr(1);
161 GURL ret_val = GURL(extension_url.spec() + path);
162 DCHECK(base::StartsWith(ret_val.spec(), extension_url.spec(),
163 base::CompareCase::INSENSITIVE_ASCII));
165 return ret_val;
168 bool Extension::ResourceMatches(const URLPatternSet& pattern_set,
169 const std::string& resource) const {
170 return pattern_set.MatchesURL(extension_url_.Resolve(resource));
173 ExtensionResource Extension::GetResource(
174 const std::string& relative_path) const {
175 std::string new_path = relative_path;
176 // We have some legacy data where resources have leading slashes.
177 // See: http://crbug.com/121164
178 if (!new_path.empty() && new_path.at(0) == '/')
179 new_path.erase(0, 1);
180 base::FilePath relative_file_path = base::FilePath::FromUTF8Unsafe(new_path);
181 if (ContainsReservedCharacters(relative_file_path))
182 return ExtensionResource();
183 ExtensionResource r(id(), path(), relative_file_path);
184 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) {
185 r.set_follow_symlinks_anywhere();
187 return r;
190 ExtensionResource Extension::GetResource(
191 const base::FilePath& relative_file_path) const {
192 if (ContainsReservedCharacters(relative_file_path))
193 return ExtensionResource();
194 ExtensionResource r(id(), path(), relative_file_path);
195 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) {
196 r.set_follow_symlinks_anywhere();
198 return r;
201 // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a
202 // util class in base:
203 // http://code.google.com/p/chromium/issues/detail?id=13572
204 // static
205 bool Extension::ParsePEMKeyBytes(const std::string& input,
206 std::string* output) {
207 DCHECK(output);
208 if (!output)
209 return false;
210 if (input.length() == 0)
211 return false;
213 std::string working = input;
214 if (base::StartsWith(working, kKeyBeginHeaderMarker,
215 base::CompareCase::SENSITIVE)) {
216 working = base::CollapseWhitespaceASCII(working, true);
217 size_t header_pos = working.find(kKeyInfoEndMarker,
218 sizeof(kKeyBeginHeaderMarker) - 1);
219 if (header_pos == std::string::npos)
220 return false;
221 size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1;
222 size_t end_pos = working.rfind(kKeyBeginFooterMarker);
223 if (end_pos == std::string::npos)
224 return false;
225 if (start_pos >= end_pos)
226 return false;
228 working = working.substr(start_pos, end_pos - start_pos);
229 if (working.length() == 0)
230 return false;
233 return base::Base64Decode(working, output);
236 // static
237 bool Extension::ProducePEM(const std::string& input, std::string* output) {
238 DCHECK(output);
239 if (input.empty())
240 return false;
241 base::Base64Encode(input, output);
242 return true;
245 // static
246 bool Extension::FormatPEMForFileOutput(const std::string& input,
247 std::string* output,
248 bool is_public) {
249 DCHECK(output);
250 if (input.length() == 0)
251 return false;
252 *output = "";
253 output->append(kKeyBeginHeaderMarker);
254 output->append(" ");
255 output->append(is_public ? kPublic : kPrivate);
256 output->append(" ");
257 output->append(kKeyInfoEndMarker);
258 output->append("\n");
259 for (size_t i = 0; i < input.length(); ) {
260 int slice = std::min<int>(input.length() - i, kPEMOutputColumns);
261 output->append(input.substr(i, slice));
262 output->append("\n");
263 i += slice;
265 output->append(kKeyBeginFooterMarker);
266 output->append(" ");
267 output->append(is_public ? kPublic : kPrivate);
268 output->append(" ");
269 output->append(kKeyInfoEndMarker);
270 output->append("\n");
272 return true;
275 // static
276 GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) {
277 return GURL(std::string(extensions::kExtensionScheme) +
278 url::kStandardSchemeSeparator + extension_id + "/");
281 bool Extension::ShowConfigureContextMenus() const {
282 // Normally we don't show a context menu for component actions, but when
283 // re-design is enabled we show them in the toolbar (if they have an action),
284 // and it is weird to have a random button that has no context menu when the
285 // rest do.
286 if (location() == Manifest::COMPONENT ||
287 location() == Manifest::EXTERNAL_COMPONENT)
288 return FeatureSwitch::extension_action_redesign()->IsEnabled();
290 return true;
293 bool Extension::OverlapsWithOrigin(const GURL& origin) const {
294 if (url() == origin)
295 return true;
297 if (web_extent().is_empty())
298 return false;
300 // Note: patterns and extents ignore port numbers.
301 URLPattern origin_only_pattern(kValidWebExtentSchemes);
302 if (!origin_only_pattern.SetScheme(origin.scheme()))
303 return false;
304 origin_only_pattern.SetHost(origin.host());
305 origin_only_pattern.SetPath("/*");
307 URLPatternSet origin_only_pattern_list;
308 origin_only_pattern_list.AddPattern(origin_only_pattern);
310 return web_extent().OverlapsWith(origin_only_pattern_list);
313 bool Extension::RequiresSortOrdinal() const {
314 return is_app() && (display_in_launcher_ || display_in_new_tab_page_);
317 bool Extension::ShouldDisplayInAppLauncher() const {
318 // Only apps should be displayed in the launcher.
319 return is_app() && display_in_launcher_;
322 bool Extension::ShouldDisplayInNewTabPage() const {
323 // Only apps should be displayed on the NTP.
324 return is_app() && display_in_new_tab_page_;
327 bool Extension::ShouldDisplayInExtensionSettings() const {
328 // Don't show for themes since the settings UI isn't really useful for them.
329 if (is_theme())
330 return false;
332 // Don't show component extensions and invisible apps.
333 if (ShouldNotBeVisible())
334 return false;
336 // Always show unpacked extensions and apps.
337 if (Manifest::IsUnpackedLocation(location()))
338 return true;
340 // Unless they are unpacked, never show hosted apps. Note: We intentionally
341 // show packaged apps and platform apps because there are some pieces of
342 // functionality that are only available in chrome://extensions/ but which
343 // are needed for packaged and platform apps. For example, inspecting
344 // background pages. See http://crbug.com/116134.
345 if (is_hosted_app())
346 return false;
348 return true;
351 bool Extension::ShouldNotBeVisible() const {
352 // Don't show component extensions because they are only extensions as an
353 // implementation detail of Chrome.
354 if (extensions::Manifest::IsComponentLocation(location()) &&
355 !base::CommandLine::ForCurrentProcess()->HasSwitch(
356 switches::kShowComponentExtensionOptions)) {
357 return true;
360 // Always show unpacked extensions and apps.
361 if (Manifest::IsUnpackedLocation(location()))
362 return false;
364 // Don't show apps that aren't visible in either launcher or ntp.
365 if (is_app() && !ShouldDisplayInAppLauncher() && !ShouldDisplayInNewTabPage())
366 return true;
368 return false;
371 Extension::ManifestData* Extension::GetManifestData(const std::string& key)
372 const {
373 DCHECK(finished_parsing_manifest_ || thread_checker_.CalledOnValidThread());
374 ManifestDataMap::const_iterator iter = manifest_data_.find(key);
375 if (iter != manifest_data_.end())
376 return iter->second.get();
377 return NULL;
380 void Extension::SetManifestData(const std::string& key,
381 Extension::ManifestData* data) {
382 DCHECK(!finished_parsing_manifest_ && thread_checker_.CalledOnValidThread());
383 manifest_data_[key] = linked_ptr<ManifestData>(data);
386 Manifest::Location Extension::location() const {
387 return manifest_->location();
390 const std::string& Extension::id() const {
391 return manifest_->extension_id();
394 const std::string Extension::VersionString() const {
395 return version()->GetString();
398 const std::string Extension::GetVersionForDisplay() const {
399 if (version_name_.size() > 0)
400 return version_name_;
401 return VersionString();
404 void Extension::AddInstallWarning(const InstallWarning& new_warning) {
405 install_warnings_.push_back(new_warning);
408 void Extension::AddInstallWarnings(
409 const std::vector<InstallWarning>& new_warnings) {
410 install_warnings_.insert(install_warnings_.end(),
411 new_warnings.begin(), new_warnings.end());
414 bool Extension::is_app() const {
415 return manifest()->is_app();
418 bool Extension::is_platform_app() const {
419 return manifest()->is_platform_app();
422 bool Extension::is_hosted_app() const {
423 return manifest()->is_hosted_app();
426 bool Extension::is_legacy_packaged_app() const {
427 return manifest()->is_legacy_packaged_app();
430 bool Extension::is_extension() const {
431 return manifest()->is_extension();
434 bool Extension::is_shared_module() const {
435 return manifest()->is_shared_module();
438 bool Extension::is_theme() const {
439 return manifest()->is_theme();
442 void Extension::AddWebExtentPattern(const URLPattern& pattern) {
443 // Bookmark apps are permissionless.
444 if (from_bookmark())
445 return;
447 extent_.AddPattern(pattern);
450 // static
451 bool Extension::InitExtensionID(extensions::Manifest* manifest,
452 const base::FilePath& path,
453 const std::string& explicit_id,
454 int creation_flags,
455 base::string16* error) {
456 if (!explicit_id.empty()) {
457 manifest->set_extension_id(explicit_id);
458 return true;
461 if (manifest->HasKey(keys::kPublicKey)) {
462 std::string public_key;
463 std::string public_key_bytes;
464 if (!manifest->GetString(keys::kPublicKey, &public_key) ||
465 !ParsePEMKeyBytes(public_key, &public_key_bytes)) {
466 *error = base::ASCIIToUTF16(errors::kInvalidKey);
467 return false;
469 std::string extension_id = crx_file::id_util::GenerateId(public_key_bytes);
470 manifest->set_extension_id(extension_id);
471 return true;
474 if (creation_flags & REQUIRE_KEY) {
475 *error = base::ASCIIToUTF16(errors::kInvalidKey);
476 return false;
477 } else {
478 // If there is a path, we generate the ID from it. This is useful for
479 // development mode, because it keeps the ID stable across restarts and
480 // reloading the extension.
481 std::string extension_id = crx_file::id_util::GenerateIdForPath(path);
482 if (extension_id.empty()) {
483 NOTREACHED() << "Could not create ID from path.";
484 return false;
486 manifest->set_extension_id(extension_id);
487 return true;
491 Extension::Extension(const base::FilePath& path,
492 scoped_ptr<extensions::Manifest> manifest)
493 : manifest_version_(0),
494 converted_from_user_script_(false),
495 manifest_(manifest.release()),
496 finished_parsing_manifest_(false),
497 display_in_launcher_(true),
498 display_in_new_tab_page_(true),
499 wants_file_access_(false),
500 creation_flags_(0) {
501 DCHECK(path.empty() || path.IsAbsolute());
502 path_ = crx_file::id_util::MaybeNormalizePath(path);
505 Extension::~Extension() {
508 bool Extension::InitFromValue(int flags, base::string16* error) {
509 DCHECK(error);
511 creation_flags_ = flags;
513 // Important to load manifest version first because many other features
514 // depend on its value.
515 if (!LoadManifestVersion(error))
516 return false;
518 if (!LoadRequiredFeatures(error))
519 return false;
521 // We don't need to validate because InitExtensionID already did that.
522 manifest_->GetString(keys::kPublicKey, &public_key_);
524 extension_url_ = Extension::GetBaseURLFromExtensionId(id());
526 // Load App settings. LoadExtent at least has to be done before
527 // ParsePermissions(), because the valid permissions depend on what type of
528 // package this is.
529 if (is_app() && !LoadAppFeatures(error))
530 return false;
532 permissions_parser_.reset(new PermissionsParser());
533 if (!permissions_parser_->Parse(this, error))
534 return false;
536 if (manifest_->HasKey(keys::kConvertedFromUserScript)) {
537 manifest_->GetBoolean(keys::kConvertedFromUserScript,
538 &converted_from_user_script_);
541 if (!LoadSharedFeatures(error))
542 return false;
544 permissions_parser_->Finalize(this);
545 permissions_parser_.reset();
547 finished_parsing_manifest_ = true;
549 permissions_data_.reset(new PermissionsData(this));
551 return true;
554 bool Extension::LoadRequiredFeatures(base::string16* error) {
555 if (!LoadName(error) ||
556 !LoadVersion(error))
557 return false;
558 return true;
561 bool Extension::LoadName(base::string16* error) {
562 base::string16 localized_name;
563 if (!manifest_->GetString(keys::kName, &localized_name)) {
564 *error = base::ASCIIToUTF16(errors::kInvalidName);
565 return false;
567 non_localized_name_ = base::UTF16ToUTF8(localized_name);
568 base::i18n::AdjustStringForLocaleDirection(&localized_name);
569 name_ = base::UTF16ToUTF8(localized_name);
570 return true;
573 bool Extension::LoadVersion(base::string16* error) {
574 std::string version_str;
575 if (!manifest_->GetString(keys::kVersion, &version_str)) {
576 *error = base::ASCIIToUTF16(errors::kInvalidVersion);
577 return false;
579 version_.reset(new Version(version_str));
580 if (!version_->IsValid() || version_->components().size() > 4) {
581 *error = base::ASCIIToUTF16(errors::kInvalidVersion);
582 return false;
584 if (manifest_->HasKey(keys::kVersionName)) {
585 if (!manifest_->GetString(keys::kVersionName, &version_name_)) {
586 *error = base::ASCIIToUTF16(errors::kInvalidVersionName);
587 return false;
590 return true;
593 bool Extension::LoadAppFeatures(base::string16* error) {
594 if (!LoadExtent(keys::kWebURLs, &extent_,
595 errors::kInvalidWebURLs, errors::kInvalidWebURL, error)) {
596 return false;
598 if (manifest_->HasKey(keys::kDisplayInLauncher) &&
599 !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) {
600 *error = base::ASCIIToUTF16(errors::kInvalidDisplayInLauncher);
601 return false;
603 if (manifest_->HasKey(keys::kDisplayInNewTabPage)) {
604 if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage,
605 &display_in_new_tab_page_)) {
606 *error = base::ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage);
607 return false;
609 } else {
610 // Inherit default from display_in_launcher property.
611 display_in_new_tab_page_ = display_in_launcher_;
613 return true;
616 bool Extension::LoadExtent(const char* key,
617 URLPatternSet* extent,
618 const char* list_error,
619 const char* value_error,
620 base::string16* error) {
621 const base::Value* temp_pattern_value = NULL;
622 if (!manifest_->Get(key, &temp_pattern_value))
623 return true;
625 const base::ListValue* pattern_list = NULL;
626 if (!temp_pattern_value->GetAsList(&pattern_list)) {
627 *error = base::ASCIIToUTF16(list_error);
628 return false;
631 for (size_t i = 0; i < pattern_list->GetSize(); ++i) {
632 std::string pattern_string;
633 if (!pattern_list->GetString(i, &pattern_string)) {
634 *error = ErrorUtils::FormatErrorMessageUTF16(value_error,
635 base::UintToString(i),
636 errors::kExpectString);
637 return false;
640 URLPattern pattern(kValidWebExtentSchemes);
641 URLPattern::ParseResult parse_result = pattern.Parse(pattern_string);
642 if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) {
643 pattern_string += "/";
644 parse_result = pattern.Parse(pattern_string);
647 if (parse_result != URLPattern::PARSE_SUCCESS) {
648 *error = ErrorUtils::FormatErrorMessageUTF16(
649 value_error,
650 base::UintToString(i),
651 URLPattern::GetParseResultString(parse_result));
652 return false;
655 // Do not allow authors to claim "<all_urls>".
656 if (pattern.match_all_urls()) {
657 *error = ErrorUtils::FormatErrorMessageUTF16(
658 value_error,
659 base::UintToString(i),
660 errors::kCannotClaimAllURLsInExtent);
661 return false;
664 // Do not allow authors to claim "*" for host.
665 if (pattern.host().empty()) {
666 *error = ErrorUtils::FormatErrorMessageUTF16(
667 value_error,
668 base::UintToString(i),
669 errors::kCannotClaimAllHostsInExtent);
670 return false;
673 // We do not allow authors to put wildcards in their paths. Instead, we
674 // imply one at the end.
675 if (pattern.path().find('*') != std::string::npos) {
676 *error = ErrorUtils::FormatErrorMessageUTF16(
677 value_error,
678 base::UintToString(i),
679 errors::kNoWildCardsInPaths);
680 return false;
682 pattern.SetPath(pattern.path() + '*');
684 extent->AddPattern(pattern);
687 return true;
690 bool Extension::LoadSharedFeatures(base::string16* error) {
691 if (!LoadDescription(error) ||
692 !ManifestHandler::ParseExtension(this, error) ||
693 !LoadShortName(error))
694 return false;
696 return true;
699 bool Extension::LoadDescription(base::string16* error) {
700 if (manifest_->HasKey(keys::kDescription) &&
701 !manifest_->GetString(keys::kDescription, &description_)) {
702 *error = base::ASCIIToUTF16(errors::kInvalidDescription);
703 return false;
705 return true;
708 bool Extension::LoadManifestVersion(base::string16* error) {
709 // Get the original value out of the dictionary so that we can validate it
710 // more strictly.
711 if (manifest_->value()->HasKey(keys::kManifestVersion)) {
712 int manifest_version = 1;
713 if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) ||
714 manifest_version < 1) {
715 *error = base::ASCIIToUTF16(errors::kInvalidManifestVersion);
716 return false;
720 manifest_version_ = manifest_->GetManifestVersion();
721 if (manifest_version_ < kModernManifestVersion &&
722 ((creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION &&
723 !base::CommandLine::ForCurrentProcess()->HasSwitch(
724 switches::kAllowLegacyExtensionManifests)) ||
725 GetType() == Manifest::TYPE_PLATFORM_APP)) {
726 *error = ErrorUtils::FormatErrorMessageUTF16(
727 errors::kInvalidManifestVersionOld,
728 base::IntToString(kModernManifestVersion),
729 is_platform_app() ? "apps" : "extensions");
730 return false;
733 return true;
736 bool Extension::LoadShortName(base::string16* error) {
737 if (manifest_->HasKey(keys::kShortName)) {
738 base::string16 localized_short_name;
739 if (!manifest_->GetString(keys::kShortName, &localized_short_name) ||
740 localized_short_name.empty()) {
741 *error = base::ASCIIToUTF16(errors::kInvalidShortName);
742 return false;
745 base::i18n::AdjustStringForLocaleDirection(&localized_short_name);
746 short_name_ = base::UTF16ToUTF8(localized_short_name);
747 } else {
748 short_name_ = name_;
750 return true;
753 ExtensionInfo::ExtensionInfo(const base::DictionaryValue* manifest,
754 const std::string& id,
755 const base::FilePath& path,
756 Manifest::Location location)
757 : extension_id(id),
758 extension_path(path),
759 extension_location(location) {
760 if (manifest)
761 extension_manifest.reset(manifest->DeepCopy());
764 ExtensionInfo::~ExtensionInfo() {}
766 InstalledExtensionInfo::InstalledExtensionInfo(
767 const Extension* extension,
768 bool is_update,
769 bool from_ephemeral,
770 const std::string& old_name)
771 : extension(extension),
772 is_update(is_update),
773 from_ephemeral(from_ephemeral),
774 old_name(old_name) {}
776 UnloadedExtensionInfo::UnloadedExtensionInfo(
777 const Extension* extension,
778 UnloadedExtensionInfo::Reason reason)
779 : reason(reason),
780 extension(extension) {}
782 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo(
783 const Extension* extension,
784 const PermissionSet* permissions,
785 Reason reason)
786 : reason(reason),
787 extension(extension),
788 permissions(permissions) {}
790 } // namespace extensions