Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / installed_loader.cc
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/extension_action_manager.h"
15 #include "chrome/browser/extensions/extension_error_reporter.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/manifest_url_handler.h"
21 #include "chrome/common/pref_names.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/user_metrics.h"
24 #include "extensions/browser/api/runtime/runtime_api.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"
35
36 using base::UserMetricsAction;
37 using content::BrowserThread;
38
39 namespace extensions {
40
41 namespace errors = manifest_errors;
42
43 namespace {
44
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
54 };
55
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,
62 };
63
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
76 };
77
78 bool IsManifestCorrupt(const base::DictionaryValue* manifest) {
79   if (!manifest)
80     return false;
81
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);
90 }
91
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;
97
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;
102
103   // Reload if the copy of the manifest in the preferences is corrupt.
104   if (IsManifestCorrupt(info.extension_manifest.get()))
105     return CORRUPT_PREFERENCES;
106
107   return NOT_NEEDED;
108 }
109
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;
116 }
117
118 }  // namespace
119
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())) {}
124
125 InstalledLoader::~InstalledLoader() {
126 }
127
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;
140   }
141
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"));
150   }
151
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;
169     }
170     UMA_HISTOGRAM_BOOLEAN("ExtensionInstalledLoader.ForceDisabled",
171                           force_disabled);
172   }
173
174   if (!extension.get()) {
175     ExtensionErrorReporter::GetInstance()->ReportLoadError(
176         info.extension_path,
177         error,
178         extension_service_->profile(),
179         false);  // Be quiet.
180     return;
181   }
182
183   if (write_to_prefs)
184     extension_prefs_->UpdateManifest(extension.get());
185
186   extension_service_->AddExtension(extension.get());
187 }
188
189 void InstalledLoader::LoadAllExtensions() {
190   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
191
192   base::TimeTicks start_time = base::TimeTicks::Now();
193
194   scoped_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
195       extension_prefs_->GetInstalledExtensionsInfo());
196
197   std::vector<int> reload_reason_counts(NUM_MANIFEST_RELOAD_REASONS, 0);
198   bool should_write_prefs = false;
199
200   for (size_t i = 0; i < extensions_info->size(); ++i) {
201     ExtensionInfo* info = extensions_info->at(i).get();
202
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;
207
208     ManifestReloadReason reload_reason = ShouldReloadExtensionManifest(*info);
209     ++reload_reason_counts[reload_reason];
210     UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestReloadEnumValue",
211                               reload_reason, 100);
212
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;
221
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));
228
229       if (!extension.get()) {
230         ExtensionErrorReporter::GetInstance()->ReportLoadError(
231             info->extension_path,
232             error,
233             extension_service_->profile(),
234             false);  // Be quiet.
235         continue;
236       }
237
238       extensions_info->at(i)->extension_manifest.reset(
239           static_cast<base::DictionaryValue*>(
240               extension->manifest()->value()->DeepCopy()));
241       should_write_prefs = true;
242     }
243   }
244
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);
249   }
250
251   extension_service_->OnLoadedInstalledExtensions();
252
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]);
261
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());
266
267   UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
268                       base::TimeTicks::Now() - start_time);
269
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);
296     }
297     if (!ManifestURL::UpdatesFromGallery(*ex)) {
298       UMA_HISTOGRAM_ENUMERATION("Extensions.NonWebstoreLocation",
299                                 location, 100);
300     }
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);
311       }
312     }
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,
321       };
322       if (!ManifestURL::UpdatesFromGallery(*ex)) {
323         UMA_HISTOGRAM_ENUMERATION("Extensions.FromWebstoreInconsistency",
324                                   BAD_UPDATE_URL, 2);
325       }
326     }
327
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;
339       }
340     }
341
342     // Don't count unpacked extensions, since they're a developer-specific
343     // feature.
344     if (Manifest::IsUnpackedLocation(location))
345       continue;
346
347     UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestVersion",
348                               (*ex)->manifest_version(), 10);
349
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);
355     }
356
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;
374         }
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;
382         }
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;
390         }
391         break;
392       case Manifest::TYPE_EXTENSION:
393       default:
394         if (Manifest::IsExternalLocation(location)) {
395           ++extension_external_count;
396         } else {
397           ++extension_user_count;
398         }
399         break;
400     }
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;
409
410     if (extensions::ManagedModeInfo::IsContentPack(ex->get()))
411       ++content_pack_count;
412
413     extension_service_->RecordPermissionMessagesHistogram(
414         ex->get(), "Extensions.Permissions_Load");
415   }
416
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;
423     }
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);
434       }
435     }
436   }
437
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);
453       }
454     }
455   }
456
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);
482 }
483
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;
491 }
492
493 }  // namespace extensions