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