blob: d1e657a5672d76fdc9bfacb1ae96df1373eac143 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/extensions/installed_loader.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <vector>
#include "base/files/file_path.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/load_error_reporter.h"
#include "chrome/browser/extensions/scripting_permissions_modifier.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/chrome_manifest_url_handlers.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/common/url_constants.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/management_policy.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_l10n_util.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/extension_urls.h"
#include "extensions/common/file_util.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/common/manifest_url_handlers.h"
#include "extensions/common/permissions/api_permission.h"
#include "extensions/common/permissions/permissions_data.h"
using content::BrowserThread;
namespace extensions {
namespace {
// The following enumeration is used in histograms matching
// Extensions.ManifestReload*.
enum ManifestReloadReason {
NOT_NEEDED = 0, // Reload not needed.
UNPACKED_DIR, // Unpacked directory.
NEEDS_RELOCALIZATION, // The locale has changed since we read this extension.
CORRUPT_PREFERENCES, // The manifest in the preferences is corrupt.
// New enum values must go above here.
NUM_MANIFEST_RELOAD_REASONS
};
// Used in histogram Extension.BackgroundPageType.
enum BackgroundPageType {
NO_BACKGROUND_PAGE = 0,
BACKGROUND_PAGE_PERSISTENT,
EVENT_PAGE,
// New enum values must go above here.
NUM_BACKGROUND_PAGE_TYPES
};
// Used in histogram Extensions.ExternalItemState.
enum ExternalItemState {
DEPRECATED_EXTERNAL_ITEM_DISABLED = 0,
DEPRECATED_EXTERNAL_ITEM_ENABLED,
EXTERNAL_ITEM_WEBSTORE_DISABLED,
EXTERNAL_ITEM_WEBSTORE_ENABLED,
EXTERNAL_ITEM_NONWEBSTORE_DISABLED,
EXTERNAL_ITEM_NONWEBSTORE_ENABLED,
EXTERNAL_ITEM_WEBSTORE_UNINSTALLED,
EXTERNAL_ITEM_NONWEBSTORE_UNINSTALLED,
// New enum values must go above here.
EXTERNAL_ITEM_MAX_ITEMS
};
bool IsManifestCorrupt(const base::DictionaryValue* manifest) {
if (!manifest)
return false;
// Because of bug #272524 sometimes manifests got mangled in the preferences
// file, one particularly bad case resulting in having both a background page
// and background scripts values. In those situations we want to reload the
// manifest from the extension to fix this.
const base::Value* background_page;
const base::Value* background_scripts;
return manifest->Get(manifest_keys::kBackgroundPage, &background_page) &&
manifest->Get(manifest_keys::kBackgroundScripts, &background_scripts);
}
ManifestReloadReason ShouldReloadExtensionManifest(const ExtensionInfo& info) {
// Always reload manifests of unpacked extensions, because they can change
// on disk independent of the manifest in our prefs.
if (Manifest::IsUnpackedLocation(info.extension_location))
return UNPACKED_DIR;
// Reload the manifest if it needs to be relocalized.
if (extension_l10n_util::ShouldRelocalizeManifest(
info.extension_manifest.get()))
return NEEDS_RELOCALIZATION;
// Reload if the copy of the manifest in the preferences is corrupt.
if (IsManifestCorrupt(info.extension_manifest.get()))
return CORRUPT_PREFERENCES;
return NOT_NEEDED;
}
BackgroundPageType GetBackgroundPageType(const Extension* extension) {
if (!BackgroundInfo::HasBackgroundPage(extension))
return NO_BACKGROUND_PAGE;
if (BackgroundInfo::HasPersistentBackgroundPage(extension))
return BACKGROUND_PAGE_PERSISTENT;
return EVENT_PAGE;
}
// Records the creation flags of an extension grouped by
// Extension::InitFromValueFlags.
void RecordCreationFlags(const Extension* extension) {
for (int i = 0; i < Extension::kInitFromValueFlagBits; ++i) {
int flag = 1 << i;
if (extension->creation_flags() & flag) {
UMA_HISTOGRAM_EXACT_LINEAR("Extensions.LoadCreationFlags", i,
Extension::kInitFromValueFlagBits);
}
}
}
// Helper to record a single disable reason histogram value (see
// RecordDisableReasons below).
void RecordDisbleReasonHistogram(int reason) {
base::UmaHistogramSparse("Extensions.DisableReason", reason);
}
// Records the disable reasons for a single extension grouped by
// disable_reason::DisableReason.
void RecordDisableReasons(int reasons) {
// |reasons| is a bitmask with values from ExtensionDisabledReason
// which are increasing powers of 2.
if (reasons == disable_reason::DISABLE_NONE) {
RecordDisbleReasonHistogram(disable_reason::DISABLE_NONE);
return;
}
for (int reason = 1; reason < disable_reason::DISABLE_REASON_LAST;
reason <<= 1) {
if (reasons & reason)
RecordDisbleReasonHistogram(reason);
}
}
} // namespace
InstalledLoader::InstalledLoader(ExtensionService* extension_service)
: extension_service_(extension_service),
extension_registry_(ExtensionRegistry::Get(extension_service->profile())),
extension_prefs_(ExtensionPrefs::Get(extension_service->profile())) {}
InstalledLoader::~InstalledLoader() {
}
void InstalledLoader::Load(const ExtensionInfo& info, bool write_to_prefs) {
// TODO(asargent): add a test to confirm that we can't load extensions if
// their ID in preferences does not match the extension's actual ID.
if (invalid_extensions_.find(info.extension_path) !=
invalid_extensions_.end())
return;
std::string error;
scoped_refptr<const Extension> extension;
if (info.extension_manifest) {
extension = Extension::Create(
info.extension_path,
info.extension_location,
*info.extension_manifest,
GetCreationFlags(&info),
&error);
} else {
error = manifest_errors::kManifestUnreadable;
}
// Once installed, non-unpacked extensions cannot change their IDs (e.g., by
// updating the 'key' field in their manifest).
// TODO(jstritar): migrate preferences when unpacked extensions change IDs.
if (extension.get() && !Manifest::IsUnpackedLocation(extension->location()) &&
info.extension_id != extension->id()) {
error = manifest_errors::kCannotChangeExtensionID;
extension = nullptr;
}
if (!extension.get()) {
LoadErrorReporter::GetInstance()->ReportLoadError(
info.extension_path, error, extension_service_->profile(),
false); // Be quiet.
return;
}
const ManagementPolicy* policy = extensions::ExtensionSystem::Get(
extension_service_->profile())->management_policy();
bool force_disabled = false;
if (extension_prefs_->IsExtensionDisabled(extension->id())) {
int disable_reasons = extension_prefs_->GetDisableReasons(extension->id());
// Update the extension prefs to reflect if the extension is no longer
// blocked due to admin policy.
if ((disable_reasons & disable_reason::DISABLE_BLOCKED_BY_POLICY) &&
!policy->MustRemainDisabled(extension.get(), nullptr, nullptr)) {
disable_reasons &= (~disable_reason::DISABLE_BLOCKED_BY_POLICY);
extension_prefs_->ReplaceDisableReasons(extension->id(), disable_reasons);
if (disable_reasons == disable_reason::DISABLE_NONE)
extension_prefs_->SetExtensionEnabled(extension->id());
}
if ((disable_reasons & disable_reason::DISABLE_CORRUPTED) &&
policy->MustRemainEnabled(extension.get(), nullptr)) {
// This extension must have been disabled due to corruption on a
// previous run of chrome, and for some reason we weren't successful in
// auto-reinstalling it. So we want to notify the
// PendingExtensionManager that we'd still like to keep attempt to
// re-download and reinstall it whenever the ExtensionService checks for
// external updates.
PendingExtensionManager* pending_manager =
extension_service_->pending_extension_manager();
pending_manager->ExpectPolicyReinstallForCorruption(extension->id());
}
} else {
// Extension is enabled. Check management policy to verify if it should
// remain so.
disable_reason::DisableReason disable_reason = disable_reason::DISABLE_NONE;
if (policy->MustRemainDisabled(extension.get(), &disable_reason, nullptr)) {
extension_prefs_->SetExtensionDisabled(extension->id(), disable_reason);
force_disabled = true;
}
}
UMA_HISTOGRAM_BOOLEAN("ExtensionInstalledLoader.ForceDisabled2",
force_disabled);
if (write_to_prefs)
extension_prefs_->UpdateManifest(extension.get());
extension_service_->AddExtension(extension.get());
}
void InstalledLoader::LoadAllExtensions() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TRACE_EVENT0("browser,startup", "InstalledLoader::LoadAllExtensions");
SCOPED_UMA_HISTOGRAM_TIMER("Extensions.LoadAllTime2");
base::TimeTicks start_time = base::TimeTicks::Now();
Profile* profile = extension_service_->profile();
std::unique_ptr<ExtensionPrefs::ExtensionsInfo> extensions_info(
extension_prefs_->GetInstalledExtensionsInfo());
std::vector<int> reload_reason_counts(NUM_MANIFEST_RELOAD_REASONS, 0);
bool should_write_prefs = false;
for (size_t i = 0; i < extensions_info->size(); ++i) {
ExtensionInfo* info = extensions_info->at(i).get();
// Skip extensions that were loaded from the command-line because we don't
// want those to persist across browser restart.
if (info->extension_location == Manifest::COMMAND_LINE)
continue;
ManifestReloadReason reload_reason = ShouldReloadExtensionManifest(*info);
++reload_reason_counts[reload_reason];
if (reload_reason != NOT_NEEDED) {
// Reloading an extension reads files from disk. We do this on the
// UI thread because reloads should be very rare, and the complexity
// added by delaying the time when the extensions service knows about
// all extensions is significant. See crbug.com/37548 for details.
// |allow_io| disables tests that file operations run on the file
// thread.
base::ThreadRestrictions::ScopedAllowIO allow_io;
std::string error;
scoped_refptr<const Extension> extension(
file_util::LoadExtension(info->extension_path,
info->extension_location,
GetCreationFlags(info),
&error));
if (!extension.get() || extension->id() != info->extension_id) {
invalid_extensions_.insert(info->extension_path);
LoadErrorReporter::GetInstance()->ReportLoadError(info->extension_path,
error, profile,
false); // Be quiet.
continue;
}
extensions_info->at(i)->extension_manifest.reset(
static_cast<base::DictionaryValue*>(
extension->manifest()->value()->DeepCopy()));
should_write_prefs = true;
}
}
for (size_t i = 0; i < extensions_info->size(); ++i) {
if (extensions_info->at(i)->extension_location != Manifest::COMMAND_LINE)
Load(*extensions_info->at(i), should_write_prefs);
}
// The histograms Extensions.ManifestReload* allow us to validate
// the assumption that reloading manifest is a rare event.
UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNotNeeded",
reload_reason_counts[NOT_NEEDED]);
UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadUnpackedDir",
reload_reason_counts[UNPACKED_DIR]);
UMA_HISTOGRAM_COUNTS_100("Extensions.ManifestReloadNeedsRelocalization",
reload_reason_counts[NEEDS_RELOCALIZATION]);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAll",
extension_registry_->enabled_extensions().size());
UMA_HISTOGRAM_COUNTS_100("Extensions.Disabled",
extension_registry_->disabled_extensions().size());
// TODO(rkaplow): Obsolete this when verified similar to LoadAllTime2.
UMA_HISTOGRAM_TIMES("Extensions.LoadAllTime",
base::TimeTicks::Now() - start_time);
RecordExtensionsMetrics();
}
void InstalledLoader::RecordExtensionsMetricsForTesting() {
RecordExtensionsMetrics();
}
void InstalledLoader::RecordExtensionsMetrics() {
Profile* profile = extension_service_->profile();
int app_user_count = 0;
int app_external_count = 0;
int hosted_app_count = 0;
int legacy_packaged_app_count = 0;
int platform_app_count = 0;
int user_script_count = 0;
int extension_user_count = 0;
int extension_external_count = 0;
int theme_count = 0;
int page_action_count = 0;
int browser_action_count = 0;
int no_action_count = 0;
int disabled_for_permissions_count = 0;
int non_webstore_ntp_override_count = 0;
int incognito_allowed_count = 0;
int incognito_not_allowed_count = 0;
int file_access_allowed_count = 0;
int file_access_not_allowed_count = 0;
int eventless_event_pages_count = 0;
int off_store_item_count = 0;
int web_request_blocking_count = 0;
int web_request_count = 0;
const ExtensionSet& extensions = extension_registry_->enabled_extensions();
for (ExtensionSet::const_iterator iter = extensions.begin();
iter != extensions.end();
++iter) {
const Extension* extension = iter->get();
Manifest::Location location = extension->location();
Manifest::Type type = extension->GetType();
// For the first few metrics, include all extensions and apps (component,
// unpacked, etc). It's good to know these locations, and it doesn't
// muck up any of the stats. Later, though, we want to omit component and
// unpacked, as they are less interesting.
if (extension->is_app())
UMA_HISTOGRAM_ENUMERATION(
"Extensions.AppLocation", location, Manifest::NUM_LOCATIONS);
else if (extension->is_extension())
UMA_HISTOGRAM_ENUMERATION(
"Extensions.ExtensionLocation", location, Manifest::NUM_LOCATIONS);
if (!ManifestURL::UpdatesFromGallery(extension)) {
UMA_HISTOGRAM_ENUMERATION(
"Extensions.NonWebstoreLocation", location, Manifest::NUM_LOCATIONS);
// Check for inconsistencies if the extension was supposedly installed
// from the webstore.
enum {
BAD_UPDATE_URL = 0,
// This value was a mistake. Turns out sideloaded extensions can
// have the from_webstore bit if they update from the webstore.
DEPRECATED_IS_EXTERNAL = 1,
};
if (extension->from_webstore()) {
UMA_HISTOGRAM_ENUMERATION(
"Extensions.FromWebstoreInconsistency", BAD_UPDATE_URL, 2);
}
}
if (Manifest::IsExternalLocation(location)) {
// See loop below for DISABLED.
if (ManifestURL::UpdatesFromGallery(extension)) {
UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
EXTERNAL_ITEM_WEBSTORE_ENABLED,
EXTERNAL_ITEM_MAX_ITEMS);
} else {
UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
EXTERNAL_ITEM_NONWEBSTORE_ENABLED,
EXTERNAL_ITEM_MAX_ITEMS);
}
}
if (extension->permissions_data()->HasAPIPermission(
APIPermission::kWebRequestBlocking)) {
web_request_blocking_count++;
}
if (extension->permissions_data()->HasAPIPermission(
APIPermission::kWebRequest)) {
web_request_count++;
}
// From now on, don't count component extensions, since they are only
// extensions as an implementation detail. Continue to count unpacked
// extensions for a few metrics.
if (Manifest::IsComponentLocation(location))
continue;
// Histogram for non-webstore extensions overriding new tab page should
// include unpacked extensions.
if (!extension->from_webstore() &&
URLOverrides::GetChromeURLOverrides(extension).count("newtab")) {
++non_webstore_ntp_override_count;
}
// Don't count unpacked extensions anymore, either.
if (Manifest::IsUnpackedLocation(location))
continue;
UMA_HISTOGRAM_ENUMERATION("Extensions.ManifestVersion",
extension->manifest_version(),
10); // TODO(kalman): Why 10 manifest versions?
// We might have wanted to count legacy packaged apps here, too, since they
// are effectively extensions. Unfortunately, it's too late, as we don't
// want to mess up the existing stats.
if (type == Manifest::TYPE_EXTENSION) {
UMA_HISTOGRAM_ENUMERATION("Extensions.BackgroundPageType",
GetBackgroundPageType(extension),
NUM_BACKGROUND_PAGE_TYPES);
if (GetBackgroundPageType(extension) == EVENT_PAGE) {
// Count extension event pages with no registered events. Either the
// event page is badly designed, or there may be a bug where the event
// page failed to start after an update (crbug.com/469361).
if (!EventRouter::Get(extension_service_->profile())
->HasRegisteredEvents(extension->id())) {
++eventless_event_pages_count;
VLOG(1) << "Event page without registered event listeners: "
<< extension->id() << " " << extension->name();
}
}
}
// Using an enumeration shows us the total installed ratio across all users.
// Using the totals per user at each startup tells us the distribution of
// usage for each user (e.g. 40% of users have at least one app installed).
UMA_HISTOGRAM_ENUMERATION(
"Extensions.LoadType", type, Manifest::NUM_LOAD_TYPES);
switch (type) {
case Manifest::TYPE_THEME:
++theme_count;
break;
case Manifest::TYPE_USER_SCRIPT:
++user_script_count;
break;
case Manifest::TYPE_HOSTED_APP:
++hosted_app_count;
if (Manifest::IsExternalLocation(location)) {
++app_external_count;
} else {
++app_user_count;
}
break;
case Manifest::TYPE_LEGACY_PACKAGED_APP:
++legacy_packaged_app_count;
if (Manifest::IsExternalLocation(location)) {
++app_external_count;
} else {
++app_user_count;
}
break;
case Manifest::TYPE_PLATFORM_APP:
++platform_app_count;
if (Manifest::IsExternalLocation(location)) {
++app_external_count;
} else {
++app_user_count;
}
break;
case Manifest::TYPE_EXTENSION:
default:
if (Manifest::IsExternalLocation(location)) {
++extension_external_count;
} else {
++extension_user_count;
}
break;
}
// We check the manifest key (instead of the ExtensionActionManager) because
// we want to know how many extensions have a given type of action as part
// of their code, rather than as part of the extension action redesign
// (which gives each extension an action).
if (extension->manifest()->HasKey(manifest_keys::kPageAction))
++page_action_count;
else if (extension->manifest()->HasKey(manifest_keys::kBrowserAction))
++browser_action_count;
else
++no_action_count;
RecordCreationFlags(extension);
ExtensionService::RecordPermissionMessagesHistogram(extension, "Load");
// For incognito and file access, skip anything that doesn't appear in
// settings. Also, policy-installed (and unpacked of course, checked above)
// extensions are boring.
if (extension->ShouldDisplayInExtensionSettings() &&
!Manifest::IsPolicyLocation(extension->location())) {
if (util::CanBeIncognitoEnabled(extension)) {
if (util::IsIncognitoEnabled(extension->id(), profile))
++incognito_allowed_count;
else
++incognito_not_allowed_count;
}
if (extension->wants_file_access()) {
if (util::AllowFileAccess(extension->id(), profile))
++file_access_allowed_count;
else
++file_access_not_allowed_count;
}
}
if (!ManifestURL::UpdatesFromGallery(extension))
++off_store_item_count;
ScriptingPermissionsModifier scripting_modifier(profile, extension);
// NOTE: CanAffectExtension() returns false in all cases when the
// RuntimeHostPermissions feature is disabled.
if (scripting_modifier.CanAffectExtension()) {
bool extension_has_withheld_hosts =
scripting_modifier.HasWithheldHostPermissions();
UMA_HISTOGRAM_BOOLEAN(
"Extensions.RuntimeHostPermissions.ExtensionHasWithheldHosts",
extension_has_withheld_hosts);
if (extension_has_withheld_hosts) {
// Record the number of granted hosts if and only if the extension
// has withheld host permissions. This lets us equate "0" granted
// hosts to "on click only".
size_t num_granted_hosts = 0;
for (const auto& pattern : extension->permissions_data()
->active_permissions()
.effective_hosts()) {
// Ignore chrome:-scheme patterns (like chrome://favicon); these
// aren't withheld, and thus shouldn't be considered "granted".
if (pattern.scheme() != content::kChromeUIScheme)
++num_granted_hosts;
}
// TODO(devlin): This only takes into account the granted hosts that
// were also requested by the extension (because it looks at the active
// permissions). We could potentially also record the granted hosts that
// were explicitly not requested.
UMA_HISTOGRAM_COUNTS_100(
"Extensions.RuntimeHostPermissions.GrantedHostCount",
num_granted_hosts);
}
}
}
const ExtensionSet& disabled_extensions =
extension_registry_->disabled_extensions();
for (ExtensionSet::const_iterator ex = disabled_extensions.begin();
ex != disabled_extensions.end();
++ex) {
if (extension_prefs_->DidExtensionEscalatePermissions((*ex)->id())) {
++disabled_for_permissions_count;
}
RecordDisableReasons(extension_prefs_->GetDisableReasons((*ex)->id()));
if (Manifest::IsExternalLocation((*ex)->location())) {
// See loop above for ENABLED.
if (ManifestURL::UpdatesFromGallery(ex->get())) {
UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
EXTERNAL_ITEM_WEBSTORE_DISABLED,
EXTERNAL_ITEM_MAX_ITEMS);
} else {
UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
EXTERNAL_ITEM_NONWEBSTORE_DISABLED,
EXTERNAL_ITEM_MAX_ITEMS);
}
}
}
std::unique_ptr<ExtensionPrefs::ExtensionsInfo> uninstalled_extensions_info(
extension_prefs_->GetUninstalledExtensionsInfo());
for (size_t i = 0; i < uninstalled_extensions_info->size(); ++i) {
ExtensionInfo* info = uninstalled_extensions_info->at(i).get();
if (Manifest::IsExternalLocation(info->extension_location)) {
std::string update_url;
if (info->extension_manifest->GetString("update_url", &update_url) &&
extension_urls::IsWebstoreUpdateUrl(GURL(update_url))) {
UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
EXTERNAL_ITEM_WEBSTORE_UNINSTALLED,
EXTERNAL_ITEM_MAX_ITEMS);
} else {
UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalItemState",
EXTERNAL_ITEM_NONWEBSTORE_UNINSTALLED,
EXTERNAL_ITEM_MAX_ITEMS);
}
}
}
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadApp",
app_user_count + app_external_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppUser", app_user_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadAppExternal", app_external_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadHostedApp", hosted_app_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPackagedApp",
legacy_packaged_app_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadPlatformApp", platform_app_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtension",
extension_user_count + extension_external_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionUser",
extension_user_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadExtensionExternal",
extension_external_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadUserScript", user_script_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadTheme", theme_count);
// Histogram name different for legacy reasons.
UMA_HISTOGRAM_COUNTS_100("PageActionController.ExtensionsWithPageActions",
page_action_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadBrowserAction",
browser_action_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadNoExtensionAction",
no_action_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.DisabledForPermissions",
disabled_for_permissions_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.NonWebStoreNewTabPageOverrides",
non_webstore_ntp_override_count);
if (incognito_allowed_count + incognito_not_allowed_count > 0) {
UMA_HISTOGRAM_COUNTS_100("Extensions.IncognitoAllowed",
incognito_allowed_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.IncognitoNotAllowed",
incognito_not_allowed_count);
}
if (file_access_allowed_count + file_access_not_allowed_count > 0) {
UMA_HISTOGRAM_COUNTS_100("Extensions.FileAccessAllowed",
file_access_allowed_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.FileAccessNotAllowed",
file_access_not_allowed_count);
}
UMA_HISTOGRAM_COUNTS_100("Extensions.CorruptExtensionTotalDisables",
extension_prefs_->GetCorruptedDisableCount());
UMA_HISTOGRAM_COUNTS_100("Extensions.EventlessEventPages",
eventless_event_pages_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.LoadOffStoreItems",
off_store_item_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.WebRequestBlockingCount",
web_request_blocking_count);
UMA_HISTOGRAM_COUNTS_100("Extensions.WebRequestCount", web_request_count);
}
int InstalledLoader::GetCreationFlags(const ExtensionInfo* info) {
int flags = extension_prefs_->GetCreationFlags(info->extension_id);
if (!Manifest::IsUnpackedLocation(info->extension_location))
flags |= Extension::REQUIRE_KEY;
if (extension_prefs_->AllowFileAccess(info->extension_id))
flags |= Extension::ALLOW_FILE_ACCESS;
return flags;
}
} // namespace extensions