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
;
48 const int kModernManifestVersion
= 2;
49 const int kPEMOutputColumns
= 64;
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
)
66 return !net::IsSafePortableRelativePath(path
);
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
;
93 scoped_refptr
<Extension
> Extension::Create(const base::FilePath
& path
,
94 Manifest::Location location
,
95 const base::DictionaryValue
& value
,
97 std::string
* utf8_error
) {
98 return Extension::Create(path
,
102 std::string(), // ID is ignored if empty.
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
,
112 const std::string
& explicit_id
,
113 std::string
* 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
);
125 std::vector
<InstallWarning
> install_warnings
;
126 if (!manifest
->ValidateManifest(utf8_error
, &install_warnings
)) {
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
);
141 Manifest::Type
Extension::GetType() const {
142 return converted_from_user_script() ?
143 Manifest::TYPE_USER_SCRIPT
: manifest_
->type();
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
));
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();
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();
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
204 bool Extension::ParsePEMKeyBytes(const std::string
& input
,
205 std::string
* output
) {
209 if (input
.length() == 0)
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
)
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
)
224 if (start_pos
>= end_pos
)
227 working
= working
.substr(start_pos
, end_pos
- start_pos
);
228 if (working
.length() == 0)
232 return base::Base64Decode(working
, output
);
236 bool Extension::ProducePEM(const std::string
& input
, std::string
* output
) {
240 base::Base64Encode(input
, output
);
245 bool Extension::FormatPEMForFileOutput(const std::string
& input
,
249 if (input
.length() == 0)
252 output
->append(kKeyBeginHeaderMarker
);
254 output
->append(is_public
? kPublic
: kPrivate
);
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");
264 output
->append(kKeyBeginFooterMarker
);
266 output
->append(is_public
? kPublic
: kPrivate
);
268 output
->append(kKeyInfoEndMarker
);
269 output
->append("\n");
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
285 if (location() == Manifest::COMPONENT
||
286 location() == Manifest::EXTERNAL_COMPONENT
)
287 return FeatureSwitch::extension_action_redesign()->IsEnabled();
292 bool Extension::OverlapsWithOrigin(const GURL
& origin
) const {
296 if (web_extent().is_empty())
299 // Note: patterns and extents ignore port numbers.
300 URLPattern
origin_only_pattern(kValidWebExtentSchemes
);
301 if (!origin_only_pattern
.SetScheme(origin
.scheme()))
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.
331 // Don't show component extensions and invisible apps.
332 if (ShouldNotBeVisible())
335 // Always show unpacked extensions and apps.
336 if (Manifest::IsUnpackedLocation(location()))
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.
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
)) {
359 // Always show unpacked extensions and apps.
360 if (Manifest::IsUnpackedLocation(location()))
363 // Don't show apps that aren't visible in either launcher or ntp.
364 if (is_app() && !ShouldDisplayInAppLauncher() && !ShouldDisplayInNewTabPage())
370 Extension::ManifestData
* Extension::GetManifestData(const std::string
& key
)
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();
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.
451 extent_
.AddPattern(pattern
);
455 bool Extension::InitExtensionID(extensions::Manifest
* manifest
,
456 const base::FilePath
& path
,
457 const std::string
& explicit_id
,
459 base::string16
* error
) {
460 if (!explicit_id
.empty()) {
461 manifest
->set_extension_id(explicit_id
);
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
);
473 std::string extension_id
= crx_file::id_util::GenerateId(public_key_bytes
);
474 manifest
->set_extension_id(extension_id
);
478 if (creation_flags
& REQUIRE_KEY
) {
479 *error
= base::ASCIIToUTF16(errors::kInvalidKey
);
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.";
490 manifest
->set_extension_id(extension_id
);
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),
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
) {
515 creation_flags_
= flags
;
517 // Important to load manifest version first because many other features
518 // depend on its value.
519 if (!LoadManifestVersion(error
))
522 if (!LoadRequiredFeatures(error
))
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
533 if (is_app() && !LoadAppFeatures(error
))
536 permissions_parser_
.reset(new PermissionsParser());
537 if (!permissions_parser_
->Parse(this, error
))
540 if (manifest_
->HasKey(keys::kConvertedFromUserScript
)) {
541 manifest_
->GetBoolean(keys::kConvertedFromUserScript
,
542 &converted_from_user_script_
);
545 if (!LoadSharedFeatures(error
))
548 permissions_parser_
->Finalize(this);
549 permissions_parser_
.reset();
551 finished_parsing_manifest_
= true;
553 permissions_data_
.reset(new PermissionsData(this));
558 bool Extension::LoadRequiredFeatures(base::string16
* error
) {
559 if (!LoadName(error
) ||
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
);
571 non_localized_name_
= base::UTF16ToUTF8(localized_name
);
572 base::i18n::AdjustStringForLocaleDirection(&localized_name
);
573 name_
= base::UTF16ToUTF8(localized_name
);
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
);
583 version_
.reset(new Version(version_str
));
584 if (!version_
->IsValid() || version_
->components().size() > 4) {
585 *error
= base::ASCIIToUTF16(errors::kInvalidVersion
);
588 if (manifest_
->HasKey(keys::kVersionName
)) {
589 if (!manifest_
->GetString(keys::kVersionName
, &version_name_
)) {
590 *error
= base::ASCIIToUTF16(errors::kInvalidVersionName
);
597 bool Extension::LoadAppFeatures(base::string16
* error
) {
598 if (!LoadExtent(keys::kWebURLs
, &extent_
,
599 errors::kInvalidWebURLs
, errors::kInvalidWebURL
, error
)) {
602 if (manifest_
->HasKey(keys::kDisplayInLauncher
) &&
603 !manifest_
->GetBoolean(keys::kDisplayInLauncher
, &display_in_launcher_
)) {
604 *error
= base::ASCIIToUTF16(errors::kInvalidDisplayInLauncher
);
607 if (manifest_
->HasKey(keys::kDisplayInNewTabPage
)) {
608 if (!manifest_
->GetBoolean(keys::kDisplayInNewTabPage
,
609 &display_in_new_tab_page_
)) {
610 *error
= base::ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage
);
614 // Inherit default from display_in_launcher property.
615 display_in_new_tab_page_
= display_in_launcher_
;
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
))
629 const base::ListValue
* pattern_list
= NULL
;
630 if (!temp_pattern_value
->GetAsList(&pattern_list
)) {
631 *error
= base::ASCIIToUTF16(list_error
);
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
);
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(
654 base::UintToString(i
),
655 URLPattern::GetParseResultString(parse_result
));
659 // Do not allow authors to claim "<all_urls>".
660 if (pattern
.match_all_urls()) {
661 *error
= ErrorUtils::FormatErrorMessageUTF16(
663 base::UintToString(i
),
664 errors::kCannotClaimAllURLsInExtent
);
668 // Do not allow authors to claim "*" for host.
669 if (pattern
.host().empty()) {
670 *error
= ErrorUtils::FormatErrorMessageUTF16(
672 base::UintToString(i
),
673 errors::kCannotClaimAllHostsInExtent
);
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(
682 base::UintToString(i
),
683 errors::kNoWildCardsInPaths
);
686 pattern
.SetPath(pattern
.path() + '*');
688 extent
->AddPattern(pattern
);
694 bool Extension::LoadSharedFeatures(base::string16
* error
) {
695 if (!LoadDescription(error
) ||
696 !ManifestHandler::ParseExtension(this, error
) ||
697 !LoadShortName(error
))
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
);
712 bool Extension::LoadManifestVersion(base::string16
* error
) {
713 // Get the original value out of the dictionary so that we can validate it
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
);
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");
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
);
749 base::i18n::AdjustStringForLocaleDirection(&localized_short_name
);
750 short_name_
= base::UTF16ToUTF8(localized_short_name
);
757 ExtensionInfo::ExtensionInfo(const base::DictionaryValue
* manifest
,
758 const std::string
& id
,
759 const base::FilePath
& path
,
760 Manifest::Location location
)
762 extension_path(path
),
763 extension_location(location
) {
765 extension_manifest
.reset(manifest
->DeepCopy());
768 ExtensionInfo::~ExtensionInfo() {}
770 InstalledExtensionInfo::InstalledExtensionInfo(
771 const Extension
* extension
,
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
)
784 extension(extension
) {}
786 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo(
787 const Extension
* extension
,
788 const PermissionSet
* permissions
,
791 extension(extension
),
792 permissions(permissions
) {}
794 } // namespace extensions