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