installed_loader.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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.
4
5#include "chrome/browser/extensions/installed_loader.h"
6
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"
34
35using base::UserMetricsAction;
36using content::BrowserThread;
37
38namespace extensions {
39
40namespace errors = manifest_errors;
41
42namespace {
43
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.
47enum 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
53};
54
55// Used in histogram Extension.BackgroundPageType. Values may be added, as
56// long as existing values are not changed.
57enum BackgroundPageType {
58  NO_BACKGROUND_PAGE = 0,
59  BACKGROUND_PAGE_PERSISTENT = 1,
60  EVENT_PAGE = 2,
61};
62
63// Used in histogram Extensions.ExternalItemState. Values may be added, as
64// long as existing values are not changed.
65enum 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
75};
76
77bool IsManifestCorrupt(const base::DictionaryValue* manifest) {
78  if (!manifest)
79    return false;
80
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);
89}
90
91ManifestReloadReason 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;
96
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;
101
102  // Reload if the copy of the manifest in the preferences is corrupt.
103  if (IsManifestCorrupt(info.extension_manifest.get()))
104    return CORRUPT_PREFERENCES;
105
106  return NOT_NEEDED;
107}
108
109BackgroundPageType 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;
115}
116
117}  // namespace
118
119InstalledLoader::InstalledLoader(ExtensionService* extension_service)
120    : extension_service_(extension_service),
121      extension_registry_(ExtensionRegistry::Get(extension_service->profile())),
122      extension_prefs_(extension_service->extension_prefs()) {}
123
124InstalledLoader::~InstalledLoader() {
125}
126
127void 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;
139  }
140
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"));
149  }
150
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;
168    }
169    UMA_HISTOGRAM_BOOLEAN("ExtensionInstalledLoader.ForceDisabled",
170                          force_disabled);
171  }
172
173  if (!extension.get()) {
174    extension_service_->ReportExtensionLoadError(
175        info.extension_path, error, false);
176    return;
177  }
178
179  if (write_to_prefs)
180    extension_prefs_->UpdateManifest(extension.get());
181
182  extension_service_->AddExtension(extension.get());
183}
184
185void InstalledLoader::LoadAllExtensions() {
186  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
187
188  base::TimeTicks start_time = base::TimeTicks::Now();
189
190  scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
191      extension_prefs_->GetInstalledExtensionsInfo());
192
193  std::vector<int> reload_reason_counts(NUM_MANIFEST_RELOAD_REASONS, 0);
194  bool should_write_prefs = false;
195
196  for (size_t i = 0; i < extensions_info->size(); ++i) {
197    ExtensionInfo* info = extensions_info->at(i).get();
198
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;
203
204    ManifestReloadReason reload_reason = ShouldReloadExtensionManifest(*info);
205    ++reload_reason_counts[reload_reason];
206    UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestReloadEnumValue",
207                              reload_reason, 100);
208
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;
217
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));
225
226      if (!extension.get()) {
227        extension_service_->ReportExtensionLoadError(
228            info->extension_path, error, false);
229        continue;
230      }
231
232      extensions_info->at(i)->extension_manifest.reset(
233          static_cast<base::DictionaryValue*>(
234              extension->manifest()->value()->DeepCopy()));
235      should_write_prefs = true;
236    }
237  }
238
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);
243  }
244
245  extension_service_->OnLoadedInstalledExtensions();
246
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]);
255
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());
260
261  UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
262                      base::TimeTicks::Now() - start_time);
263
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);
290    }
291    if (!ManifestURL::UpdatesFromGallery(*ex)) {
292      UMA_HISTOGRAM_ENUMERATION("Extensions.NonWebstoreLocation",
293                                location, 100);
294    }
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);
305      }
306    }
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,
315      };
316      if (!ManifestURL::UpdatesFromGallery(*ex)) {
317        UMA_HISTOGRAM_ENUMERATION("Extensions.FromWebstoreInconsistency",
318                                  BAD_UPDATE_URL, 2);
319      }
320    }
321
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;
333      }
334    }
335
336    // Don't count unpacked extensions, since they're a developer-specific
337    // feature.
338    if (Manifest::IsUnpackedLocation(location))
339      continue;
340
341    UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestVersion",
342                              (*ex)->manifest_version(), 10);
343
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);
349    }
350
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;
368        }
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;
376        }
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;
384        }
385        break;
386      case Manifest::TYPE_EXTENSION:
387      default:
388        if (Manifest::IsExternalLocation(location)) {
389          ++extension_external_count;
390        } else {
391          ++extension_user_count;
392        }
393        break;
394    }
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;
403
404    if (extensions::ManagedModeInfo::IsContentPack(ex->get()))
405      ++content_pack_count;
406
407    extension_service_->RecordPermissionMessagesHistogram(
408        ex->get(), "Extensions.Permissions_Load");
409  }
410
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;
418    }
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);
429      }
430    }
431  }
432
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);
448      }
449    }
450  }
451
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);
477}
478
479int 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;
486}
487
488}  // namespace extensions
489