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