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/metrics/sparse_histogram.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "base/values.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/extensions/extension_action_manager.h"
16 #include "chrome/browser/extensions/extension_error_reporter.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_util.h"
19 #include "chrome/browser/profiles/profile_manager.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/extensions/api/supervised_user_private/supervised_user_handler.h"
22 #include "chrome/common/extensions/chrome_manifest_url_handlers.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/notification_service.h"
25 #include "content/public/browser/user_metrics.h"
26 #include "extensions/browser/extension_prefs.h"
27 #include "extensions/browser/extension_registry.h"
28 #include "extensions/browser/extension_system.h"
29 #include "extensions/browser/management_policy.h"
30 #include "extensions/common/extension.h"
31 #include "extensions/common/extension_l10n_util.h"
32 #include "extensions/common/extension_set.h"
33 #include "extensions/common/extension_urls.h"
34 #include "extensions/common/file_util.h"
35 #include "extensions/common/manifest.h"
36 #include "extensions/common/manifest_constants.h"
37 #include "extensions/common/manifest_handlers/background_info.h"
38 #include "extensions/common/manifest_url_handlers.h"
40 using base::UserMetricsAction;
41 using content::BrowserThread;
43 namespace extensions {
45 namespace errors = manifest_errors;
49 // The following enumeration is used in histograms matching
50 // Extensions.ManifestReload*.
51 enum ManifestReloadReason {
52 NOT_NEEDED = 0, // Reload not needed.
53 UNPACKED_DIR, // Unpacked directory.
54 NEEDS_RELOCALIZATION, // The locale has changed since we read this extension.
55 CORRUPT_PREFERENCES, // The manifest in the preferences is corrupt.
57 // New enum values must go above here.
58 NUM_MANIFEST_RELOAD_REASONS
61 // Used in histogram Extension.BackgroundPageType.
62 enum BackgroundPageType {
63 NO_BACKGROUND_PAGE = 0,
64 BACKGROUND_PAGE_PERSISTENT,
67 // New enum values must go above here.
68 NUM_BACKGROUND_PAGE_TYPES
71 // Used in histogram Extensions.ExternalItemState.
72 enum ExternalItemState {
73 DEPRECATED_EXTERNAL_ITEM_DISABLED = 0,
74 DEPRECATED_EXTERNAL_ITEM_ENABLED,
75 EXTERNAL_ITEM_WEBSTORE_DISABLED,
76 EXTERNAL_ITEM_WEBSTORE_ENABLED,
77 EXTERNAL_ITEM_NONWEBSTORE_DISABLED,
78 EXTERNAL_ITEM_NONWEBSTORE_ENABLED,
79 EXTERNAL_ITEM_WEBSTORE_UNINSTALLED,
80 EXTERNAL_ITEM_NONWEBSTORE_UNINSTALLED,
82 // New enum values must go above here.
83 EXTERNAL_ITEM_MAX_ITEMS
86 bool IsManifestCorrupt(const base::DictionaryValue* manifest) {
90 // Because of bug #272524 sometimes manifests got mangled in the preferences
91 // file, one particularly bad case resulting in having both a background page
92 // and background scripts values. In those situations we want to reload the
93 // manifest from the extension to fix this.
94 const base::Value* background_page;
95 const base::Value* background_scripts;
96 return manifest->Get(manifest_keys::kBackgroundPage, &background_page) &&
97 manifest->Get(manifest_keys::kBackgroundScripts, &background_scripts);
100 ManifestReloadReason ShouldReloadExtensionManifest(const ExtensionInfo& info) {
101 // Always reload manifests of unpacked extensions, because they can change
102 // on disk independent of the manifest in our prefs.
103 if (Manifest::IsUnpackedLocation(info.extension_location))
106 // Reload the manifest if it needs to be relocalized.
107 if (extension_l10n_util::ShouldRelocalizeManifest(
108 info.extension_manifest.get()))
109 return NEEDS_RELOCALIZATION;
111 // Reload if the copy of the manifest in the preferences is corrupt.
112 if (IsManifestCorrupt(info.extension_manifest.get()))
113 return CORRUPT_PREFERENCES;
118 BackgroundPageType GetBackgroundPageType(const Extension* extension) {
119 if (!BackgroundInfo::HasBackgroundPage(extension))
120 return NO_BACKGROUND_PAGE;
121 if (BackgroundInfo::HasPersistentBackgroundPage(extension))
122 return BACKGROUND_PAGE_PERSISTENT;
126 // Records the creation flags of an extension grouped by
127 // Extension::InitFromValueFlags.
128 void RecordCreationFlags(const Extension* extension) {
129 for (int i = 0; i < Extension::kInitFromValueFlagBits; ++i) {
131 if (extension->creation_flags() & flag) {
132 UMA_HISTOGRAM_ENUMERATION(
133 "Extensions.LoadCreationFlags", i, Extension::kInitFromValueFlagBits);
138 // Helper to record a single disable reason histogram value (see
139 // RecordDisableReasons below).
140 void RecordDisbleReasonHistogram(int reason) {
141 UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.DisableReason", reason);
144 // Records the disable reasons for a single extension grouped by
145 // Extension::DisableReason.
146 void RecordDisableReasons(int reasons) {
147 // |reasons| is a bitmask with values from Extension::DisabledReason
148 // which are increasing powers of 2.
149 if (reasons == Extension::DISABLE_NONE) {
150 RecordDisbleReasonHistogram(Extension::DISABLE_NONE);
153 for (int reason = 1; reason < Extension::DISABLE_REASON_LAST; reason <<= 1) {
154 if (reasons & reason)
155 RecordDisbleReasonHistogram(reason);
161 InstalledLoader::InstalledLoader(ExtensionService* extension_service)
162 : extension_service_(extension_service),
163 extension_registry_(ExtensionRegistry::Get(extension_service->profile())),
164 extension_prefs_(ExtensionPrefs::Get(extension_service->profile())) {}
166 InstalledLoader::~InstalledLoader() {
169 void InstalledLoader::Load(const ExtensionInfo& info, bool write_to_prefs) {
171 scoped_refptr<const Extension> extension(NULL);
172 if (info.extension_manifest) {
173 extension = Extension::Create(
175 info.extension_location,
176 *info.extension_manifest,
177 GetCreationFlags(&info),
180 error = errors::kManifestUnreadable;
183 // Once installed, non-unpacked extensions cannot change their IDs (e.g., by
184 // updating the 'key' field in their manifest).
185 // TODO(jstritar): migrate preferences when unpacked extensions change IDs.
186 if (extension.get() && !Manifest::IsUnpackedLocation(extension->location()) &&
187 info.extension_id != extension->id()) {
188 error = errors::kCannotChangeExtensionID;
192 // Check policy on every load in case an extension was blacklisted while
193 // Chrome was not running.
194 const ManagementPolicy* policy = extensions::ExtensionSystem::Get(
195 extension_service_->profile())->management_policy();
196 if (extension.get()) {
197 Extension::DisableReason disable_reason = Extension::DISABLE_NONE;
198 bool force_disabled = false;
199 if (!policy->UserMayLoad(extension.get(), NULL)) {
200 // The error message from UserMayInstall() often contains the extension ID
201 // and is therefore not well suited to this UI.
202 error = errors::kDisabledByPolicy;
204 } else if (!extension_prefs_->IsExtensionDisabled(extension->id()) &&
205 policy->MustRemainDisabled(
206 extension.get(), &disable_reason, NULL)) {
207 extension_prefs_->SetExtensionState(extension->id(), Extension::DISABLED);
208 extension_prefs_->AddDisableReason(extension->id(), disable_reason);
209 force_disabled = true;
211 UMA_HISTOGRAM_BOOLEAN("ExtensionInstalledLoader.ForceDisabled",
215 if (!extension.get()) {
216 ExtensionErrorReporter::GetInstance()->ReportLoadError(
219 extension_service_->profile(),
225 extension_prefs_->UpdateManifest(extension.get());
227 extension_service_->AddExtension(extension.get());
230 void InstalledLoader::LoadAllExtensions() {
231 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
233 base::TimeTicks start_time = base::TimeTicks::Now();
235 Profile* profile = extension_service_->profile();
236 scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
237 extension_prefs_->GetInstalledExtensionsInfo());
239 std::vector<int> reload_reason_counts(NUM_MANIFEST_RELOAD_REASONS, 0);
240 bool should_write_prefs = false;
242 for (size_t i = 0; i < extensions_info->size(); ++i) {
243 ExtensionInfo* info = extensions_info->at(i).get();
245 // Skip extensions that were loaded from the command-line because we don't
246 // want those to persist across browser restart.
247 if (info->extension_location == Manifest::COMMAND_LINE)
250 ManifestReloadReason reload_reason = ShouldReloadExtensionManifest(*info);
251 ++reload_reason_counts[reload_reason];
253 if (reload_reason != NOT_NEEDED) {
254 // Reloading an extension reads files from disk. We do this on the
255 // UI thread because reloads should be very rare, and the complexity
256 // added by delaying the time when the extensions service knows about
257 // all extensions is significant. See crbug.com/37548 for details.
258 // |allow_io| disables tests that file operations run on the file
260 base::ThreadRestrictions::ScopedAllowIO allow_io;
263 scoped_refptr<const Extension> extension(
264 file_util::LoadExtension(info->extension_path,
265 info->extension_location,
266 GetCreationFlags(info),
269 if (!extension.get()) {
270 ExtensionErrorReporter::GetInstance()->ReportLoadError(
271 info->extension_path,
278 extensions_info->at(i)->extension_manifest.reset(
279 static_cast<base::DictionaryValue*>(
280 extension->manifest()->value()->DeepCopy()));
281 should_write_prefs = true;
285 for (size_t i = 0; i < extensions_info->size(); ++i) {
286 if (extensions_info->at(i)->extension_location != Manifest::COMMAND_LINE)
287 Load(*extensions_info->at(i), should_write_prefs);
290 extension_service_->OnLoadedInstalledExtensions();
292 // The histograms Extensions.ManifestReload* allow us to validate
293 // the assumption that reloading manifest is a rare event.
294 UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNotNeeded",
295 reload_reason_counts[NOT_NEEDED]);
296 UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadUnpackedDir",
297 reload_reason_counts[UNPACKED_DIR]);
298 UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNeedsRelocalization",
299 reload_reason_counts[NEEDS_RELOCALIZATION]);
301 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll",
302 extension_registry_->enabled_extensions().size());
303 UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled",
304 extension_registry_->disabled_extensions().size());
306 UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
307 base::TimeTicks::Now() - start_time);
309 int app_user_count = 0;
310 int app_external_count = 0;
311 int hosted_app_count = 0;
312 int legacy_packaged_app_count = 0;
313 int platform_app_count = 0;
314 int user_script_count = 0;
315 int content_pack_count = 0;
316 int extension_user_count = 0;
317 int extension_external_count = 0;
319 int page_action_count = 0;
320 int browser_action_count = 0;
321 int disabled_for_permissions_count = 0;
322 int non_webstore_ntp_override_count = 0;
323 int incognito_allowed_count = 0;
324 int incognito_not_allowed_count = 0;
325 int file_access_allowed_count = 0;
326 int file_access_not_allowed_count = 0;
328 const ExtensionSet& extensions = extension_registry_->enabled_extensions();
329 ExtensionActionManager* extension_action_manager =
330 ExtensionActionManager::Get(profile);
331 for (ExtensionSet::const_iterator iter = extensions.begin();
332 iter != extensions.end();
334 const Extension* extension = iter->get();
335 Manifest::Location location = extension->location();
336 Manifest::Type type = extension->GetType();
338 // For the first few metrics, include all extensions and apps (component,
339 // unpacked, etc). It's good to know these locations, and it doesn't
340 // muck up any of the stats. Later, though, we want to omit component and
341 // unpacked, as they are less interesting.
342 if (extension->is_app())
343 UMA_HISTOGRAM_ENUMERATION(
344 "Extensions.AppLocation", location, Manifest::NUM_LOCATIONS);
345 else if (extension->is_extension())
346 UMA_HISTOGRAM_ENUMERATION(
347 "Extensions.ExtensionLocation", location, Manifest::NUM_LOCATIONS);
349 if (!ManifestURL::UpdatesFromGallery(extension)) {
350 UMA_HISTOGRAM_ENUMERATION(
351 "Extensions.NonWebstoreLocation", location, Manifest::NUM_LOCATIONS);
353 // Check for inconsistencies if the extension was supposedly installed
354 // from the webstore.
357 // This value was a mistake. Turns out sideloaded extensions can
358 // have the from_webstore bit if they update from the webstore.
359 DEPRECATED_IS_EXTERNAL = 1,
361 if (extension->from_webstore()) {
362 UMA_HISTOGRAM_ENUMERATION(
363 "Extensions.FromWebstoreInconsistency", BAD_UPDATE_URL, 2);
367 if (Manifest::IsExternalLocation(location)) {
368 // See loop below for DISABLED.
369 if (ManifestURL::UpdatesFromGallery(extension)) {
370 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
371 EXTERNAL_ITEM_WEBSTORE_ENABLED,
372 EXTERNAL_ITEM_MAX_ITEMS);
374 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
375 EXTERNAL_ITEM_NONWEBSTORE_ENABLED,
376 EXTERNAL_ITEM_MAX_ITEMS);
380 // From now on, don't count component extensions, since they are only
381 // extensions as an implementation detail. Continue to count unpacked
382 // extensions for a few metrics.
383 if (location == Manifest::COMPONENT)
386 // Histogram for non-webstore extensions overriding new tab page should
387 // include unpacked extensions.
388 if (!extension->from_webstore() &&
389 URLOverrides::GetChromeURLOverrides(extension).count("newtab")) {
390 ++non_webstore_ntp_override_count;
393 // Don't count unpacked extensions anymore, either.
394 if (Manifest::IsUnpackedLocation(location))
397 UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestVersion",
398 extension->manifest_version(),
399 10); // TODO(kalman): Why 10 manifest versions?
401 // We might have wanted to count legacy packaged apps here, too, since they
402 // are effectively extensions. Unfortunately, it's too late, as we don't
403 // want to mess up the existing stats.
404 if (type == Manifest::TYPE_EXTENSION) {
405 UMA_HISTOGRAM_ENUMERATION("Extensions.BackgroundPageType",
406 GetBackgroundPageType(extension),
407 NUM_BACKGROUND_PAGE_TYPES);
410 // Using an enumeration shows us the total installed ratio across all users.
411 // Using the totals per user at each startup tells us the distribution of
412 // usage for each user (e.g. 40% of users have at least one app installed).
413 UMA_HISTOGRAM_ENUMERATION(
414 "Extensions.LoadType", type, Manifest::NUM_LOAD_TYPES);
416 case Manifest::TYPE_THEME:
419 case Manifest::TYPE_USER_SCRIPT:
422 case Manifest::TYPE_HOSTED_APP:
424 if (Manifest::IsExternalLocation(location)) {
425 ++app_external_count;
430 case Manifest::TYPE_LEGACY_PACKAGED_APP:
431 ++legacy_packaged_app_count;
432 if (Manifest::IsExternalLocation(location)) {
433 ++app_external_count;
438 case Manifest::TYPE_PLATFORM_APP:
439 ++platform_app_count;
440 if (Manifest::IsExternalLocation(location)) {
441 ++app_external_count;
446 case Manifest::TYPE_EXTENSION:
448 if (Manifest::IsExternalLocation(location)) {
449 ++extension_external_count;
451 ++extension_user_count;
456 if (extension_action_manager->GetPageAction(*extension))
459 if (extension_action_manager->GetBrowserAction(*extension))
460 ++browser_action_count;
462 if (SupervisedUserInfo::IsContentPack(extension))
463 ++content_pack_count;
465 RecordCreationFlags(extension);
467 ExtensionService::RecordPermissionMessagesHistogram(
468 extension, "Extensions.Permissions_Load2");
470 // For incognito and file access, skip anything that doesn't appear in
471 // settings. Also, policy-installed (and unpacked of course, checked above)
472 // extensions are boring.
473 if (extension->ShouldDisplayInExtensionSettings() &&
474 !Manifest::IsPolicyLocation(extension->location())) {
475 if (extension->can_be_incognito_enabled()) {
476 if (util::IsIncognitoEnabled(extension->id(), profile))
477 ++incognito_allowed_count;
479 ++incognito_not_allowed_count;
481 if (extension->wants_file_access()) {
482 if (util::AllowFileAccess(extension->id(), profile))
483 ++file_access_allowed_count;
485 ++file_access_not_allowed_count;
490 const ExtensionSet& disabled_extensions =
491 extension_registry_->disabled_extensions();
493 for (ExtensionSet::const_iterator ex = disabled_extensions.begin();
494 ex != disabled_extensions.end();
496 if (extension_prefs_->DidExtensionEscalatePermissions((*ex)->id())) {
497 ++disabled_for_permissions_count;
499 RecordDisableReasons(extension_prefs_->GetDisableReasons((*ex)->id()));
500 if (Manifest::IsExternalLocation((*ex)->location())) {
501 // See loop above for ENABLED.
502 if (ManifestURL::UpdatesFromGallery(ex->get())) {
503 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
504 EXTERNAL_ITEM_WEBSTORE_DISABLED,
505 EXTERNAL_ITEM_MAX_ITEMS);
507 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
508 EXTERNAL_ITEM_NONWEBSTORE_DISABLED,
509 EXTERNAL_ITEM_MAX_ITEMS);
514 scoped_ptr<ExtensionPrefs::ExtensionsInfo> uninstalled_extensions_info(
515 extension_prefs_->GetUninstalledExtensionsInfo());
516 for (size_t i = 0; i < uninstalled_extensions_info->size(); ++i) {
517 ExtensionInfo* info = uninstalled_extensions_info->at(i).get();
518 if (Manifest::IsExternalLocation(info->extension_location)) {
519 std::string update_url;
520 if (info->extension_manifest->GetString("update_url", &update_url) &&
521 extension_urls::IsWebstoreUpdateUrl(GURL(update_url))) {
522 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
523 EXTERNAL_ITEM_WEBSTORE_UNINSTALLED,
524 EXTERNAL_ITEM_MAX_ITEMS);
526 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
527 EXTERNAL_ITEM_NONWEBSTORE_UNINSTALLED,
528 EXTERNAL_ITEM_MAX_ITEMS);
533 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadApp",
534 app_user_count + app_external_count);
535 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppUser", app_user_count);
536 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppExternal", app_external_count);
537 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadHostedApp", hosted_app_count);
538 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPackagedApp",
539 legacy_packaged_app_count);
540 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPlatformApp", platform_app_count);
541 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension",
542 extension_user_count + extension_external_count);
543 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionUser",
544 extension_user_count);
545 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionExternal",
546 extension_external_count);
547 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
548 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
549 // Histogram name different for legacy reasons.
550 UMA_HISTOGRAM_COUNTS_100("PageActionController.ExtensionsWithPageActions",
552 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
553 browser_action_count);
554 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadContentPack", content_pack_count);
555 UMA_HISTOGRAM_COUNTS_100("Extensions.DisabledForPermissions",
556 disabled_for_permissions_count);
557 UMA_HISTOGRAM_COUNTS_100("Extensions.NonWebStoreNewTabPageOverrides",
558 non_webstore_ntp_override_count);
559 if (incognito_allowed_count + incognito_not_allowed_count > 0) {
560 UMA_HISTOGRAM_COUNTS_100("Extensions.IncognitoAllowed",
561 incognito_allowed_count);
562 UMA_HISTOGRAM_COUNTS_100("Extensions.IncognitoNotAllowed",
563 incognito_not_allowed_count);
565 if (file_access_allowed_count + file_access_not_allowed_count > 0) {
566 UMA_HISTOGRAM_COUNTS_100("Extensions.FileAccessAllowed",
567 file_access_allowed_count);
568 UMA_HISTOGRAM_COUNTS_100("Extensions.FileAccessNotAllowed",
569 file_access_not_allowed_count);
571 UMA_HISTOGRAM_COUNTS_100("Extensions.CorruptExtensionTotalDisables",
572 extension_prefs_->GetCorruptedDisableCount());
575 int InstalledLoader::GetCreationFlags(const ExtensionInfo* info) {
576 int flags = extension_prefs_->GetCreationFlags(info->extension_id);
577 if (!Manifest::IsUnpackedLocation(info->extension_location))
578 flags |= Extension::REQUIRE_KEY;
579 if (extension_prefs_->AllowFileAccess(info->extension_id))
580 flags |= Extension::ALLOW_FILE_ACCESS;
584 } // namespace extensions