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