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