1 // Copyright (c) 2012 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 "chrome/browser/extensions/installed_loader.h"
7 #include "base/files/file_path.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/threading/thread_restrictions.h"
12 #include "base/values.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/extensions/api/runtime/runtime_api.h"
15 #include "chrome/browser/extensions/extension_action_manager.h"
16 #include "chrome/browser/extensions/extension_prefs.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_system.h"
19 #include "chrome/browser/profiles/profile_manager.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/extensions/api/managed_mode_private/managed_mode_handler.h"
22 #include "chrome/common/extensions/extension_file_util.h"
23 #include "chrome/common/extensions/extension_l10n_util.h"
24 #include "chrome/common/extensions/manifest_url_handler.h"
25 #include "chrome/common/pref_names.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/user_metrics.h"
28 #include "extensions/browser/management_policy.h"
29 #include "extensions/common/extension.h"
30 #include "extensions/common/manifest.h"
31 #include "extensions/common/manifest_constants.h"
32 #include "extensions/common/manifest_handlers/background_info.h"
34 using base::UserMetricsAction
;
35 using content::BrowserThread
;
37 namespace extensions
{
39 namespace errors
= manifest_errors
;
43 // The following enumeration is used in histograms matching
44 // Extensions.ManifestReload* . Values may be added, as long as existing
45 // values are not changed.
46 enum ManifestReloadReason
{
47 NOT_NEEDED
= 0, // Reload not needed.
48 UNPACKED_DIR
, // Unpacked directory.
49 NEEDS_RELOCALIZATION
, // The locale has changed since we read this extension.
50 CORRUPT_PREFERENCES
, // The manifest in the preferences is corrupt.
51 NUM_MANIFEST_RELOAD_REASONS
54 // Used in histogram Extension.BackgroundPageType. Values may be added, as
55 // long as existing values are not changed.
56 enum BackgroundPageType
{
57 NO_BACKGROUND_PAGE
= 0,
58 BACKGROUND_PAGE_PERSISTENT
= 1,
62 // Used in histogram Extensions.ExternalItemState. Values may be added, as
63 // long as existing values are not changed.
64 enum ExternalItemState
{
65 DEPRECATED_EXTERNAL_ITEM_DISABLED
= 0,
66 DEPRECATED_EXTERNAL_ITEM_ENABLED
= 1,
67 EXTERNAL_ITEM_WEBSTORE_DISABLED
= 2,
68 EXTERNAL_ITEM_WEBSTORE_ENABLED
= 3,
69 EXTERNAL_ITEM_NONWEBSTORE_DISABLED
= 4,
70 EXTERNAL_ITEM_NONWEBSTORE_ENABLED
= 5,
71 EXTERNAL_ITEM_WEBSTORE_UNINSTALLED
= 6,
72 EXTERNAL_ITEM_NONWEBSTORE_UNINSTALLED
= 7,
73 EXTERNAL_ITEM_MAX_ITEMS
= 8
76 bool IsManifestCorrupt(const base::DictionaryValue
* manifest
) {
80 // Because of bug #272524 sometimes manifests got mangled in the preferences
81 // file, one particularly bad case resulting in having both a background page
82 // and background scripts values. In those situations we want to reload the
83 // manifest from the extension to fix this.
84 const base::Value
* background_page
;
85 const base::Value
* background_scripts
;
86 return manifest
->Get(manifest_keys::kBackgroundPage
, &background_page
) &&
87 manifest
->Get(manifest_keys::kBackgroundScripts
, &background_scripts
);
90 ManifestReloadReason
ShouldReloadExtensionManifest(const ExtensionInfo
& info
) {
91 // Always reload manifests of unpacked extensions, because they can change
92 // on disk independent of the manifest in our prefs.
93 if (Manifest::IsUnpackedLocation(info
.extension_location
))
96 // Reload the manifest if it needs to be relocalized.
97 if (extension_l10n_util::ShouldRelocalizeManifest(
98 info
.extension_manifest
.get()))
99 return NEEDS_RELOCALIZATION
;
101 // Reload if the copy of the manifest in the preferences is corrupt.
102 if (IsManifestCorrupt(info
.extension_manifest
.get()))
103 return CORRUPT_PREFERENCES
;
108 BackgroundPageType
GetBackgroundPageType(const Extension
* extension
) {
109 if (!BackgroundInfo::HasBackgroundPage(extension
))
110 return NO_BACKGROUND_PAGE
;
111 if (BackgroundInfo::HasPersistentBackgroundPage(extension
))
112 return BACKGROUND_PAGE_PERSISTENT
;
118 InstalledLoader::InstalledLoader(ExtensionService
* extension_service
)
119 : extension_service_(extension_service
),
120 extension_prefs_(extension_service
->extension_prefs()) {
123 InstalledLoader::~InstalledLoader() {
126 void InstalledLoader::Load(const ExtensionInfo
& info
, bool write_to_prefs
) {
128 scoped_refptr
<const Extension
> extension(NULL
);
129 if (info
.extension_manifest
) {
130 extension
= Extension::Create(
132 info
.extension_location
,
133 *info
.extension_manifest
,
134 GetCreationFlags(&info
),
137 error
= errors::kManifestUnreadable
;
140 // Once installed, non-unpacked extensions cannot change their IDs (e.g., by
141 // updating the 'key' field in their manifest).
142 // TODO(jstritar): migrate preferences when unpacked extensions change IDs.
143 if (extension
.get() && !Manifest::IsUnpackedLocation(extension
->location()) &&
144 info
.extension_id
!= extension
->id()) {
145 error
= errors::kCannotChangeExtensionID
;
147 content::RecordAction(UserMetricsAction("Extensions.IDChangedError"));
150 // Check policy on every load in case an extension was blacklisted while
151 // Chrome was not running.
152 const ManagementPolicy
* policy
= extensions::ExtensionSystem::Get(
153 extension_service_
->profile())->management_policy();
154 if (extension
.get()) {
155 Extension::DisableReason disable_reason
= Extension::DISABLE_NONE
;
156 bool force_disabled
= false;
157 if (!policy
->UserMayLoad(extension
.get(), NULL
)) {
158 // The error message from UserMayInstall() often contains the extension ID
159 // and is therefore not well suited to this UI.
160 error
= errors::kDisabledByPolicy
;
162 } else if (!extension_prefs_
->IsExtensionDisabled(extension
->id()) &&
163 policy
->MustRemainDisabled(extension
, &disable_reason
, NULL
)) {
164 extension_prefs_
->SetExtensionState(extension
->id(), Extension::DISABLED
);
165 extension_prefs_
->AddDisableReason(extension
->id(), disable_reason
);
166 force_disabled
= true;
168 UMA_HISTOGRAM_BOOLEAN("ExtensionInstalledLoader.ForceDisabled",
172 if (!extension
.get()) {
173 extension_service_
->ReportExtensionLoadError(
174 info
.extension_path
, error
, false);
179 extension_prefs_
->UpdateManifest(extension
.get());
181 extension_service_
->AddExtension(extension
.get());
184 void InstalledLoader::LoadAllExtensions() {
185 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
187 base::TimeTicks start_time
= base::TimeTicks::Now();
189 scoped_ptr
<ExtensionPrefs::ExtensionsInfo
> extensions_info(
190 extension_prefs_
->GetInstalledExtensionsInfo());
192 std::vector
<int> reload_reason_counts(NUM_MANIFEST_RELOAD_REASONS
, 0);
193 bool should_write_prefs
= false;
195 for (size_t i
= 0; i
< extensions_info
->size(); ++i
) {
196 ExtensionInfo
* info
= extensions_info
->at(i
).get();
198 // Skip extensions that were loaded from the command-line because we don't
199 // want those to persist across browser restart.
200 if (info
->extension_location
== Manifest::COMMAND_LINE
)
203 ManifestReloadReason reload_reason
= ShouldReloadExtensionManifest(*info
);
204 ++reload_reason_counts
[reload_reason
];
205 UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestReloadEnumValue",
208 if (reload_reason
!= NOT_NEEDED
) {
209 // Reloading an extension reads files from disk. We do this on the
210 // UI thread because reloads should be very rare, and the complexity
211 // added by delaying the time when the extensions service knows about
212 // all extensions is significant. See crbug.com/37548 for details.
213 // |allow_io| disables tests that file operations run on the file
215 base::ThreadRestrictions::ScopedAllowIO allow_io
;
218 scoped_refptr
<const Extension
> extension(
219 extension_file_util::LoadExtension(
220 info
->extension_path
,
221 info
->extension_location
,
222 GetCreationFlags(info
),
225 if (!extension
.get()) {
226 extension_service_
->ReportExtensionLoadError(
227 info
->extension_path
, error
, false);
231 extensions_info
->at(i
)->extension_manifest
.reset(
232 static_cast<base::DictionaryValue
*>(
233 extension
->manifest()->value()->DeepCopy()));
234 should_write_prefs
= true;
238 for (size_t i
= 0; i
< extensions_info
->size(); ++i
) {
239 if (extensions_info
->at(i
)->extension_location
== Manifest::COMMAND_LINE
)
241 Load(*extensions_info
->at(i
), should_write_prefs
);
244 extension_service_
->OnLoadedInstalledExtensions();
246 // The histograms Extensions.ManifestReload* allow us to validate
247 // the assumption that reloading manifest is a rare event.
248 UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNotNeeded",
249 reload_reason_counts
[NOT_NEEDED
]);
250 UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadUnpackedDir",
251 reload_reason_counts
[UNPACKED_DIR
]);
252 UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNeedsRelocalization",
253 reload_reason_counts
[NEEDS_RELOCALIZATION
]);
255 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll",
256 extension_service_
->extensions()->size());
257 UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled",
258 extension_service_
->disabled_extensions()->size());
260 UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
261 base::TimeTicks::Now() - start_time
);
263 int app_user_count
= 0;
264 int app_external_count
= 0;
265 int hosted_app_count
= 0;
266 int legacy_packaged_app_count
= 0;
267 int platform_app_count
= 0;
268 int user_script_count
= 0;
269 int content_pack_count
= 0;
270 int extension_user_count
= 0;
271 int extension_external_count
= 0;
273 int page_action_count
= 0;
274 int browser_action_count
= 0;
275 int disabled_for_permissions_count
= 0;
276 int item_user_count
= 0;
277 int non_webstore_ntp_override_count
= 0;
278 const ExtensionSet
* extensions
= extension_service_
->extensions();
279 ExtensionSet::const_iterator ex
;
280 for (ex
= extensions
->begin(); ex
!= extensions
->end(); ++ex
) {
281 Manifest::Location location
= (*ex
)->location();
282 Manifest::Type type
= (*ex
)->GetType();
283 if ((*ex
)->is_app()) {
284 UMA_HISTOGRAM_ENUMERATION("Extensions.AppLocation",
286 } else if (type
== Manifest::TYPE_EXTENSION
) {
287 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionLocation",
290 if (!ManifestURL::UpdatesFromGallery(*ex
)) {
291 UMA_HISTOGRAM_ENUMERATION("Extensions.NonWebstoreLocation",
294 if (Manifest::IsExternalLocation(location
)) {
295 // See loop below for DISABLED.
296 if (ManifestURL::UpdatesFromGallery(*ex
)) {
297 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
298 EXTERNAL_ITEM_WEBSTORE_ENABLED
,
299 EXTERNAL_ITEM_MAX_ITEMS
);
301 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
302 EXTERNAL_ITEM_NONWEBSTORE_ENABLED
,
303 EXTERNAL_ITEM_MAX_ITEMS
);
306 if ((*ex
)->from_webstore()) {
307 // Check for inconsistencies if the extension was supposedly installed
308 // from the webstore.
311 // This value was a mistake. Turns out sideloaded extensions can
312 // have the from_webstore bit if they update from the webstore.
313 DEPRECATED_IS_EXTERNAL
= 1,
315 if (!ManifestURL::UpdatesFromGallery(*ex
)) {
316 UMA_HISTOGRAM_ENUMERATION("Extensions.FromWebstoreInconsistency",
321 // Don't count component extensions, since they are only extensions as an
322 // implementation detail.
323 if (location
== Manifest::COMPONENT
)
325 // Histogram for non-webstore extensions overriding new tab page should
326 // include unpacked extensions.
327 if (!(*ex
)->from_webstore()) {
328 const extensions::URLOverrides::URLOverrideMap
& override_map
=
329 extensions::URLOverrides::GetChromeURLOverrides(ex
->get());
330 if (override_map
.find("newtab") != override_map
.end()) {
331 ++non_webstore_ntp_override_count
;
335 // Don't count unpacked extensions, since they're a developer-specific
337 if (Manifest::IsUnpackedLocation(location
))
340 UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestVersion",
341 (*ex
)->manifest_version(), 10);
343 if (type
== Manifest::TYPE_EXTENSION
) {
344 BackgroundPageType background_page_type
=
345 GetBackgroundPageType(ex
->get());
346 UMA_HISTOGRAM_ENUMERATION(
347 "Extensions.BackgroundPageType", background_page_type
, 10);
350 // Using an enumeration shows us the total installed ratio across all users.
351 // Using the totals per user at each startup tells us the distribution of
352 // usage for each user (e.g. 40% of users have at least one app installed).
353 UMA_HISTOGRAM_ENUMERATION("Extensions.LoadType", type
, 100);
355 case Manifest::TYPE_THEME
:
358 case Manifest::TYPE_USER_SCRIPT
:
361 case Manifest::TYPE_HOSTED_APP
:
363 if (Manifest::IsExternalLocation(location
)) {
364 ++app_external_count
;
369 case Manifest::TYPE_LEGACY_PACKAGED_APP
:
370 ++legacy_packaged_app_count
;
371 if (Manifest::IsExternalLocation(location
)) {
372 ++app_external_count
;
377 case Manifest::TYPE_PLATFORM_APP
:
378 ++platform_app_count
;
379 if (Manifest::IsExternalLocation(location
)) {
380 ++app_external_count
;
385 case Manifest::TYPE_EXTENSION
:
387 if (Manifest::IsExternalLocation(location
)) {
388 ++extension_external_count
;
390 ++extension_user_count
;
394 if (!Manifest::IsExternalLocation((*ex
)->location()))
396 ExtensionActionManager
* extension_action_manager
=
397 ExtensionActionManager::Get(extension_service_
->profile());
398 if (extension_action_manager
->GetPageAction(*ex
->get()))
400 if (extension_action_manager
->GetBrowserAction(*ex
->get()))
401 ++browser_action_count
;
403 if (extensions::ManagedModeInfo::IsContentPack(ex
->get()))
404 ++content_pack_count
;
406 extension_service_
->RecordPermissionMessagesHistogram(
407 ex
->get(), "Extensions.Permissions_Load");
410 const ExtensionSet
* disabled_extensions
=
411 extension_service_
->disabled_extensions();
412 for (ex
= disabled_extensions
->begin();
413 ex
!= disabled_extensions
->end(); ++ex
) {
414 if (extension_service_
->extension_prefs()->
415 DidExtensionEscalatePermissions((*ex
)->id())) {
416 ++disabled_for_permissions_count
;
418 if (Manifest::IsExternalLocation((*ex
)->location())) {
419 // See loop above for ENABLED.
420 if (ManifestURL::UpdatesFromGallery(*ex
)) {
421 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
422 EXTERNAL_ITEM_WEBSTORE_DISABLED
,
423 EXTERNAL_ITEM_MAX_ITEMS
);
425 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
426 EXTERNAL_ITEM_NONWEBSTORE_DISABLED
,
427 EXTERNAL_ITEM_MAX_ITEMS
);
432 scoped_ptr
<ExtensionPrefs::ExtensionsInfo
> uninstalled_extensions_info(
433 extension_prefs_
->GetUninstalledExtensionsInfo());
434 for (size_t i
= 0; i
< uninstalled_extensions_info
->size(); ++i
) {
435 ExtensionInfo
* info
= uninstalled_extensions_info
->at(i
).get();
436 if (Manifest::IsExternalLocation(info
->extension_location
)) {
437 std::string update_url
;
438 if (info
->extension_manifest
->GetString("update_url", &update_url
) &&
439 extension_urls::IsWebstoreUpdateUrl(GURL(update_url
))) {
440 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
441 EXTERNAL_ITEM_WEBSTORE_UNINSTALLED
,
442 EXTERNAL_ITEM_MAX_ITEMS
);
444 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
445 EXTERNAL_ITEM_NONWEBSTORE_UNINSTALLED
,
446 EXTERNAL_ITEM_MAX_ITEMS
);
451 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAllUser", item_user_count
);
452 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadApp",
453 app_user_count
+ app_external_count
);
454 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppUser", app_user_count
);
455 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppExternal", app_external_count
);
456 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadHostedApp", hosted_app_count
);
457 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPackagedApp",
458 legacy_packaged_app_count
);
459 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPlatformApp", platform_app_count
);
460 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension",
461 extension_user_count
+ extension_external_count
);
462 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionUser",
463 extension_user_count
);
464 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionExternal",
465 extension_external_count
);
466 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count
);
467 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count
);
468 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count
);
469 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
470 browser_action_count
);
471 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadContentPack", content_pack_count
);
472 UMA_HISTOGRAM_COUNTS_100("Extensions.DisabledForPermissions",
473 disabled_for_permissions_count
);
474 UMA_HISTOGRAM_COUNTS_100("Extensions.NonWebStoreNewTabPageOverrides",
475 non_webstore_ntp_override_count
);
478 int InstalledLoader::GetCreationFlags(const ExtensionInfo
* info
) {
479 int flags
= extension_prefs_
->GetCreationFlags(info
->extension_id
);
480 if (!Manifest::IsUnpackedLocation(info
->extension_location
))
481 flags
|= Extension::REQUIRE_KEY
;
482 if (extension_prefs_
->AllowFileAccess(info
->extension_id
))
483 flags
|= Extension::ALLOW_FILE_ACCESS
;
487 } // namespace extensions