Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / extensions / installed_loader.cc
blob5470083162d4047e6c08175135cd948292f1b101
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_error_reporter.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/profiles/profile_manager.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/extensions/api/managed_mode_private/managed_mode_handler.h"
21 #include "chrome/common/extensions/manifest_url_handler.h"
22 #include "chrome/common/pref_names.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/user_metrics.h"
25 #include "extensions/browser/extension_prefs.h"
26 #include "extensions/browser/extension_registry.h"
27 #include "extensions/browser/extension_system.h"
28 #include "extensions/browser/management_policy.h"
29 #include "extensions/common/extension.h"
30 #include "extensions/common/extension_l10n_util.h"
31 #include "extensions/common/file_util.h"
32 #include "extensions/common/manifest.h"
33 #include "extensions/common/manifest_constants.h"
34 #include "extensions/common/manifest_handlers/background_info.h"
36 using base::UserMetricsAction;
37 using content::BrowserThread;
39 namespace extensions {
41 namespace errors = manifest_errors;
43 namespace {
45 // The following enumeration is used in histograms matching
46 // Extensions.ManifestReload* . Values may be added, as long as existing
47 // values are not changed.
48 enum ManifestReloadReason {
49 NOT_NEEDED = 0, // Reload not needed.
50 UNPACKED_DIR, // Unpacked directory.
51 NEEDS_RELOCALIZATION, // The locale has changed since we read this extension.
52 CORRUPT_PREFERENCES, // The manifest in the preferences is corrupt.
53 NUM_MANIFEST_RELOAD_REASONS
56 // Used in histogram Extension.BackgroundPageType. Values may be added, as
57 // long as existing values are not changed.
58 enum BackgroundPageType {
59 NO_BACKGROUND_PAGE = 0,
60 BACKGROUND_PAGE_PERSISTENT = 1,
61 EVENT_PAGE = 2,
64 // Used in histogram Extensions.ExternalItemState. Values may be added, as
65 // long as existing values are not changed.
66 enum ExternalItemState {
67 DEPRECATED_EXTERNAL_ITEM_DISABLED = 0,
68 DEPRECATED_EXTERNAL_ITEM_ENABLED = 1,
69 EXTERNAL_ITEM_WEBSTORE_DISABLED = 2,
70 EXTERNAL_ITEM_WEBSTORE_ENABLED = 3,
71 EXTERNAL_ITEM_NONWEBSTORE_DISABLED = 4,
72 EXTERNAL_ITEM_NONWEBSTORE_ENABLED = 5,
73 EXTERNAL_ITEM_WEBSTORE_UNINSTALLED = 6,
74 EXTERNAL_ITEM_NONWEBSTORE_UNINSTALLED = 7,
75 EXTERNAL_ITEM_MAX_ITEMS = 8
78 bool IsManifestCorrupt(const base::DictionaryValue* manifest) {
79 if (!manifest)
80 return false;
82 // Because of bug #272524 sometimes manifests got mangled in the preferences
83 // file, one particularly bad case resulting in having both a background page
84 // and background scripts values. In those situations we want to reload the
85 // manifest from the extension to fix this.
86 const base::Value* background_page;
87 const base::Value* background_scripts;
88 return manifest->Get(manifest_keys::kBackgroundPage, &background_page) &&
89 manifest->Get(manifest_keys::kBackgroundScripts, &background_scripts);
92 ManifestReloadReason ShouldReloadExtensionManifest(const ExtensionInfo& info) {
93 // Always reload manifests of unpacked extensions, because they can change
94 // on disk independent of the manifest in our prefs.
95 if (Manifest::IsUnpackedLocation(info.extension_location))
96 return UNPACKED_DIR;
98 // Reload the manifest if it needs to be relocalized.
99 if (extension_l10n_util::ShouldRelocalizeManifest(
100 info.extension_manifest.get()))
101 return NEEDS_RELOCALIZATION;
103 // Reload if the copy of the manifest in the preferences is corrupt.
104 if (IsManifestCorrupt(info.extension_manifest.get()))
105 return CORRUPT_PREFERENCES;
107 return NOT_NEEDED;
110 BackgroundPageType GetBackgroundPageType(const Extension* extension) {
111 if (!BackgroundInfo::HasBackgroundPage(extension))
112 return NO_BACKGROUND_PAGE;
113 if (BackgroundInfo::HasPersistentBackgroundPage(extension))
114 return BACKGROUND_PAGE_PERSISTENT;
115 return EVENT_PAGE;
118 } // namespace
120 InstalledLoader::InstalledLoader(ExtensionService* extension_service)
121 : extension_service_(extension_service),
122 extension_registry_(ExtensionRegistry::Get(extension_service->profile())),
123 extension_prefs_(ExtensionPrefs::Get(extension_service->profile())) {}
125 InstalledLoader::~InstalledLoader() {
128 void InstalledLoader::Load(const ExtensionInfo& info, bool write_to_prefs) {
129 std::string error;
130 scoped_refptr<const Extension> extension(NULL);
131 if (info.extension_manifest) {
132 extension = Extension::Create(
133 info.extension_path,
134 info.extension_location,
135 *info.extension_manifest,
136 GetCreationFlags(&info),
137 &error);
138 } else {
139 error = errors::kManifestUnreadable;
142 // Once installed, non-unpacked extensions cannot change their IDs (e.g., by
143 // updating the 'key' field in their manifest).
144 // TODO(jstritar): migrate preferences when unpacked extensions change IDs.
145 if (extension.get() && !Manifest::IsUnpackedLocation(extension->location()) &&
146 info.extension_id != extension->id()) {
147 error = errors::kCannotChangeExtensionID;
148 extension = NULL;
149 content::RecordAction(UserMetricsAction("Extensions.IDChangedError"));
152 // Check policy on every load in case an extension was blacklisted while
153 // Chrome was not running.
154 const ManagementPolicy* policy = extensions::ExtensionSystem::Get(
155 extension_service_->profile())->management_policy();
156 if (extension.get()) {
157 Extension::DisableReason disable_reason = Extension::DISABLE_NONE;
158 bool force_disabled = false;
159 if (!policy->UserMayLoad(extension.get(), NULL)) {
160 // The error message from UserMayInstall() often contains the extension ID
161 // and is therefore not well suited to this UI.
162 error = errors::kDisabledByPolicy;
163 extension = NULL;
164 } else if (!extension_prefs_->IsExtensionDisabled(extension->id()) &&
165 policy->MustRemainDisabled(extension, &disable_reason, NULL)) {
166 extension_prefs_->SetExtensionState(extension->id(), Extension::DISABLED);
167 extension_prefs_->AddDisableReason(extension->id(), disable_reason);
168 force_disabled = true;
170 UMA_HISTOGRAM_BOOLEAN("ExtensionInstalledLoader.ForceDisabled",
171 force_disabled);
174 if (!extension.get()) {
175 ExtensionErrorReporter::GetInstance()->ReportLoadError(
176 info.extension_path,
177 error,
178 extension_service_->profile(),
179 false); // Be quiet.
180 return;
183 if (write_to_prefs)
184 extension_prefs_->UpdateManifest(extension.get());
186 extension_service_->AddExtension(extension.get());
189 void InstalledLoader::LoadAllExtensions() {
190 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
192 base::TimeTicks start_time = base::TimeTicks::Now();
194 scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
195 extension_prefs_->GetInstalledExtensionsInfo());
197 std::vector<int> reload_reason_counts(NUM_MANIFEST_RELOAD_REASONS, 0);
198 bool should_write_prefs = false;
200 for (size_t i = 0; i < extensions_info->size(); ++i) {
201 ExtensionInfo* info = extensions_info->at(i).get();
203 // Skip extensions that were loaded from the command-line because we don't
204 // want those to persist across browser restart.
205 if (info->extension_location == Manifest::COMMAND_LINE)
206 continue;
208 ManifestReloadReason reload_reason = ShouldReloadExtensionManifest(*info);
209 ++reload_reason_counts[reload_reason];
210 UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestReloadEnumValue",
211 reload_reason, 100);
213 if (reload_reason != NOT_NEEDED) {
214 // Reloading an extension reads files from disk. We do this on the
215 // UI thread because reloads should be very rare, and the complexity
216 // added by delaying the time when the extensions service knows about
217 // all extensions is significant. See crbug.com/37548 for details.
218 // |allow_io| disables tests that file operations run on the file
219 // thread.
220 base::ThreadRestrictions::ScopedAllowIO allow_io;
222 std::string error;
223 scoped_refptr<const Extension> extension(
224 file_util::LoadExtension(info->extension_path,
225 info->extension_location,
226 GetCreationFlags(info),
227 &error));
229 if (!extension.get()) {
230 ExtensionErrorReporter::GetInstance()->ReportLoadError(
231 info->extension_path,
232 error,
233 extension_service_->profile(),
234 false); // Be quiet.
235 continue;
238 extensions_info->at(i)->extension_manifest.reset(
239 static_cast<base::DictionaryValue*>(
240 extension->manifest()->value()->DeepCopy()));
241 should_write_prefs = true;
245 for (size_t i = 0; i < extensions_info->size(); ++i) {
246 if (extensions_info->at(i)->extension_location == Manifest::COMMAND_LINE)
247 continue;
248 Load(*extensions_info->at(i), should_write_prefs);
251 extension_service_->OnLoadedInstalledExtensions();
253 // The histograms Extensions.ManifestReload* allow us to validate
254 // the assumption that reloading manifest is a rare event.
255 UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNotNeeded",
256 reload_reason_counts[NOT_NEEDED]);
257 UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadUnpackedDir",
258 reload_reason_counts[UNPACKED_DIR]);
259 UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNeedsRelocalization",
260 reload_reason_counts[NEEDS_RELOCALIZATION]);
262 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll",
263 extension_registry_->enabled_extensions().size());
264 UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled",
265 extension_registry_->disabled_extensions().size());
267 UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
268 base::TimeTicks::Now() - start_time);
270 int app_user_count = 0;
271 int app_external_count = 0;
272 int hosted_app_count = 0;
273 int legacy_packaged_app_count = 0;
274 int platform_app_count = 0;
275 int user_script_count = 0;
276 int content_pack_count = 0;
277 int extension_user_count = 0;
278 int extension_external_count = 0;
279 int theme_count = 0;
280 int page_action_count = 0;
281 int browser_action_count = 0;
282 int disabled_for_permissions_count = 0;
283 int item_user_count = 0;
284 int non_webstore_ntp_override_count = 0;
285 const ExtensionSet& extensions = extension_registry_->enabled_extensions();
286 ExtensionSet::const_iterator ex;
287 for (ex = extensions.begin(); ex != extensions.end(); ++ex) {
288 Manifest::Location location = (*ex)->location();
289 Manifest::Type type = (*ex)->GetType();
290 if ((*ex)->is_app()) {
291 UMA_HISTOGRAM_ENUMERATION("Extensions.AppLocation",
292 location, 100);
293 } else if (type == Manifest::TYPE_EXTENSION) {
294 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionLocation",
295 location, 100);
297 if (!ManifestURL::UpdatesFromGallery(*ex)) {
298 UMA_HISTOGRAM_ENUMERATION("Extensions.NonWebstoreLocation",
299 location, 100);
301 if (Manifest::IsExternalLocation(location)) {
302 // See loop below for DISABLED.
303 if (ManifestURL::UpdatesFromGallery(*ex)) {
304 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
305 EXTERNAL_ITEM_WEBSTORE_ENABLED,
306 EXTERNAL_ITEM_MAX_ITEMS);
307 } else {
308 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
309 EXTERNAL_ITEM_NONWEBSTORE_ENABLED,
310 EXTERNAL_ITEM_MAX_ITEMS);
313 if ((*ex)->from_webstore()) {
314 // Check for inconsistencies if the extension was supposedly installed
315 // from the webstore.
316 enum {
317 BAD_UPDATE_URL = 0,
318 // This value was a mistake. Turns out sideloaded extensions can
319 // have the from_webstore bit if they update from the webstore.
320 DEPRECATED_IS_EXTERNAL = 1,
322 if (!ManifestURL::UpdatesFromGallery(*ex)) {
323 UMA_HISTOGRAM_ENUMERATION("Extensions.FromWebstoreInconsistency",
324 BAD_UPDATE_URL, 2);
328 // Don't count component extensions, since they are only extensions as an
329 // implementation detail.
330 if (location == Manifest::COMPONENT)
331 continue;
332 // Histogram for non-webstore extensions overriding new tab page should
333 // include unpacked extensions.
334 if (!(*ex)->from_webstore()) {
335 const extensions::URLOverrides::URLOverrideMap& override_map =
336 extensions::URLOverrides::GetChromeURLOverrides(ex->get());
337 if (override_map.find("newtab") != override_map.end()) {
338 ++non_webstore_ntp_override_count;
342 // Don't count unpacked extensions, since they're a developer-specific
343 // feature.
344 if (Manifest::IsUnpackedLocation(location))
345 continue;
347 UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestVersion",
348 (*ex)->manifest_version(), 10);
350 if (type == Manifest::TYPE_EXTENSION) {
351 BackgroundPageType background_page_type =
352 GetBackgroundPageType(ex->get());
353 UMA_HISTOGRAM_ENUMERATION(
354 "Extensions.BackgroundPageType", background_page_type, 10);
357 // Using an enumeration shows us the total installed ratio across all users.
358 // Using the totals per user at each startup tells us the distribution of
359 // usage for each user (e.g. 40% of users have at least one app installed).
360 UMA_HISTOGRAM_ENUMERATION("Extensions.LoadType", type, 100);
361 switch (type) {
362 case Manifest::TYPE_THEME:
363 ++theme_count;
364 break;
365 case Manifest::TYPE_USER_SCRIPT:
366 ++user_script_count;
367 break;
368 case Manifest::TYPE_HOSTED_APP:
369 ++hosted_app_count;
370 if (Manifest::IsExternalLocation(location)) {
371 ++app_external_count;
372 } else {
373 ++app_user_count;
375 break;
376 case Manifest::TYPE_LEGACY_PACKAGED_APP:
377 ++legacy_packaged_app_count;
378 if (Manifest::IsExternalLocation(location)) {
379 ++app_external_count;
380 } else {
381 ++app_user_count;
383 break;
384 case Manifest::TYPE_PLATFORM_APP:
385 ++platform_app_count;
386 if (Manifest::IsExternalLocation(location)) {
387 ++app_external_count;
388 } else {
389 ++app_user_count;
391 break;
392 case Manifest::TYPE_EXTENSION:
393 default:
394 if (Manifest::IsExternalLocation(location)) {
395 ++extension_external_count;
396 } else {
397 ++extension_user_count;
399 break;
401 if (!Manifest::IsExternalLocation((*ex)->location()))
402 ++item_user_count;
403 ExtensionActionManager* extension_action_manager =
404 ExtensionActionManager::Get(extension_service_->profile());
405 if (extension_action_manager->GetPageAction(*ex->get()))
406 ++page_action_count;
407 if (extension_action_manager->GetBrowserAction(*ex->get()))
408 ++browser_action_count;
410 if (extensions::ManagedModeInfo::IsContentPack(ex->get()))
411 ++content_pack_count;
413 extension_service_->RecordPermissionMessagesHistogram(
414 ex->get(), "Extensions.Permissions_Load");
417 const ExtensionSet& disabled_extensions =
418 extension_registry_->disabled_extensions();
419 for (ex = disabled_extensions.begin(); ex != disabled_extensions.end();
420 ++ex) {
421 if (extension_prefs_->DidExtensionEscalatePermissions((*ex)->id())) {
422 ++disabled_for_permissions_count;
424 if (Manifest::IsExternalLocation((*ex)->location())) {
425 // See loop above for ENABLED.
426 if (ManifestURL::UpdatesFromGallery(*ex)) {
427 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
428 EXTERNAL_ITEM_WEBSTORE_DISABLED,
429 EXTERNAL_ITEM_MAX_ITEMS);
430 } else {
431 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
432 EXTERNAL_ITEM_NONWEBSTORE_DISABLED,
433 EXTERNAL_ITEM_MAX_ITEMS);
438 scoped_ptr<ExtensionPrefs::ExtensionsInfo> uninstalled_extensions_info(
439 extension_prefs_->GetUninstalledExtensionsInfo());
440 for (size_t i = 0; i < uninstalled_extensions_info->size(); ++i) {
441 ExtensionInfo* info = uninstalled_extensions_info->at(i).get();
442 if (Manifest::IsExternalLocation(info->extension_location)) {
443 std::string update_url;
444 if (info->extension_manifest->GetString("update_url", &update_url) &&
445 extension_urls::IsWebstoreUpdateUrl(GURL(update_url))) {
446 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
447 EXTERNAL_ITEM_WEBSTORE_UNINSTALLED,
448 EXTERNAL_ITEM_MAX_ITEMS);
449 } else {
450 UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
451 EXTERNAL_ITEM_NONWEBSTORE_UNINSTALLED,
452 EXTERNAL_ITEM_MAX_ITEMS);
457 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAllUser", item_user_count);
458 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadApp",
459 app_user_count + app_external_count);
460 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppUser", app_user_count);
461 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppExternal", app_external_count);
462 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadHostedApp", hosted_app_count);
463 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPackagedApp",
464 legacy_packaged_app_count);
465 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPlatformApp", platform_app_count);
466 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension",
467 extension_user_count + extension_external_count);
468 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionUser",
469 extension_user_count);
470 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionExternal",
471 extension_external_count);
472 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
473 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
474 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPageAction", page_action_count);
475 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
476 browser_action_count);
477 UMA_HISTOGRAM_COUNTS_100("Extensions.LoadContentPack", content_pack_count);
478 UMA_HISTOGRAM_COUNTS_100("Extensions.DisabledForPermissions",
479 disabled_for_permissions_count);
480 UMA_HISTOGRAM_COUNTS_100("Extensions.NonWebStoreNewTabPageOverrides",
481 non_webstore_ntp_override_count);
484 int InstalledLoader::GetCreationFlags(const ExtensionInfo* info) {
485 int flags = extension_prefs_->GetCreationFlags(info->extension_id);
486 if (!Manifest::IsUnpackedLocation(info->extension_location))
487 flags |= Extension::REQUIRE_KEY;
488 if (extension_prefs_->AllowFileAccess(info->extension_id))
489 flags |= Extension::ALLOW_FILE_ACCESS;
490 return flags;
493 } // namespace extensions