blob: efbdf5f04b6c65ff92efbb88a5fa6d95f2c44fb1 [file] [log] [blame]
// Copyright 2014 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/permissions/permission_uma_util.h"
#include <utility>
#include "base/metrics/histogram_macros.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/permissions/permission_manager.h"
#include "chrome/browser/permissions/permission_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/website_settings/permission_bubble_request.h"
#include "components/rappor/rappor_service.h"
#include "components/rappor/rappor_utils.h"
#include "content/public/browser/permission_type.h"
#include "content/public/common/origin_util.h"
#include "url/gurl.h"
// UMA keys need to be statically initialized so plain function would not
// work. Use macros instead.
#define PERMISSION_ACTION_UMA(secure_origin, permission, permission_secure, \
permission_insecure, action) \
UMA_HISTOGRAM_ENUMERATION(permission, action, PERMISSION_ACTION_NUM); \
if (secure_origin) { \
UMA_HISTOGRAM_ENUMERATION(permission_secure, action, \
PERMISSION_ACTION_NUM); \
} else { \
UMA_HISTOGRAM_ENUMERATION(permission_insecure, action, \
PERMISSION_ACTION_NUM); \
}
#define PERMISSION_BUBBLE_TYPE_UMA(metric_name, permission_bubble_type) \
UMA_HISTOGRAM_ENUMERATION( \
metric_name, \
static_cast<base::HistogramBase::Sample>(permission_bubble_type), \
static_cast<base::HistogramBase::Sample>(PermissionBubbleType::NUM))
using content::PermissionType;
namespace {
const std::string GetRapporMetric(PermissionType permission,
PermissionAction action) {
std::string action_str;
switch (action) {
case GRANTED:
action_str = "Granted";
break;
case DENIED:
action_str = "Denied";
break;
case DISMISSED:
action_str = "Dismissed";
break;
case IGNORED:
action_str = "Ignored";
break;
case REVOKED:
action_str = "Revoked";
break;
default:
NOTREACHED();
break;
}
std::string permission_str =
PermissionUtil::GetPermissionString(permission);
if (permission_str.empty())
return "";
return base::StringPrintf("ContentSettings.PermissionActions_%s.%s.Url",
permission_str.c_str(), action_str.c_str());
}
void RecordPermissionAction(PermissionType permission,
PermissionAction action,
const GURL& requesting_origin) {
bool secure_origin = content::IsOriginSecure(requesting_origin);
switch (permission) {
case PermissionType::GEOLOCATION:
PERMISSION_ACTION_UMA(
secure_origin,
"Permissions.Action.Geolocation",
"Permissions.Action.SecureOrigin.Geolocation",
"Permissions.Action.InsecureOrigin.Geolocation",
action);
break;
case PermissionType::NOTIFICATIONS:
PERMISSION_ACTION_UMA(
secure_origin,
"Permissions.Action.Notifications",
"Permissions.Action.SecureOrigin.Notifications",
"Permissions.Action.InsecureOrigin.Notifications",
action);
break;
case PermissionType::MIDI_SYSEX:
PERMISSION_ACTION_UMA(
secure_origin,
"Permissions.Action.MidiSysEx",
"Permissions.Action.SecureOrigin.MidiSysEx",
"Permissions.Action.InsecureOrigin.MidiSysEx",
action);
break;
case PermissionType::PUSH_MESSAGING:
PERMISSION_ACTION_UMA(
secure_origin,
"Permissions.Action.PushMessaging",
"Permissions.Action.SecureOrigin.PushMessaging",
"Permissions.Action.InsecureOrigin.PushMessaging",
action);
break;
case PermissionType::PROTECTED_MEDIA_IDENTIFIER:
PERMISSION_ACTION_UMA(
secure_origin,
"Permissions.Action.ProtectedMedia",
"Permissions.Action.SecureOrigin.ProtectedMedia",
"Permissions.Action.InsecureOrigin.ProtectedMedia",
action);
break;
case PermissionType::DURABLE_STORAGE:
PERMISSION_ACTION_UMA(
secure_origin,
"Permissions.Action.DurableStorage",
"Permissions.Action.SecureOrigin.DurableStorage",
"Permissions.Action.InsecureOrigin.DurableStorage",
action);
break;
case PermissionType::AUDIO_CAPTURE:
// Media permissions are disabled on insecure origins, so there's no
// need to record metrics for secure/insecue.
UMA_HISTOGRAM_ENUMERATION("Permissions.Action.AudioCapture", action,
PERMISSION_ACTION_NUM);
break;
case PermissionType::VIDEO_CAPTURE:
UMA_HISTOGRAM_ENUMERATION("Permissions.Action.VideoCapture", action,
PERMISSION_ACTION_NUM);
break;
// The user is not prompted for these permissions, thus there is no
// permission action recorded for them.
case PermissionType::MIDI:
case PermissionType::BACKGROUND_SYNC:
case PermissionType::NUM:
NOTREACHED() << "PERMISSION "
<< PermissionUtil::GetPermissionString(permission)
<< " not accounted for";
}
// Retrieve the name of the RAPPOR metric. Currently, the new metric name is
// the deprecated name with "2" on the end, e.g.
// ContentSettings.PermissionActions_Geolocation.Granted.Url2. For simplicity,
// we retrieve the deprecated name and append the "2" for the new name.
// TODO(dominickn): remove the deprecated metric and replace it solely with
// the new one in GetRapporMetric - crbug.com/605836.
const std::string deprecated_metric = GetRapporMetric(permission, action);
rappor::RapporService* rappor_service = g_browser_process->rappor_service();
if (!deprecated_metric.empty() && rappor_service) {
rappor::SampleDomainAndRegistryFromGURL(rappor_service, deprecated_metric,
requesting_origin);
std::string rappor_metric = deprecated_metric + "2";
rappor_service->RecordSample(
rappor_metric, rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
}
}
void RecordPermissionRequest(PermissionType permission,
const GURL& requesting_origin,
const GURL& embedding_origin,
Profile* profile) {
rappor::RapporService* rappor_service = g_browser_process->rappor_service();
if (rappor_service) {
if (permission == PermissionType::GEOLOCATION) {
// TODO(dominickn): remove this deprecated metric - crbug.com/605836.
rappor::SampleDomainAndRegistryFromGURL(
rappor_service,
"ContentSettings.PermissionRequested.Geolocation.Url",
requesting_origin);
rappor_service->RecordSample(
"ContentSettings.PermissionRequested.Geolocation.Url2",
rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
} else if (permission == PermissionType::NOTIFICATIONS) {
// TODO(dominickn): remove this deprecated metric - crbug.com/605836.
rappor::SampleDomainAndRegistryFromGURL(
rappor_service,
"ContentSettings.PermissionRequested.Notifications.Url",
requesting_origin);
rappor_service->RecordSample(
"ContentSettings.PermissionRequested.Notifications.Url2",
rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
} else if (permission == PermissionType::MIDI ||
permission == PermissionType::MIDI_SYSEX) {
// TODO(dominickn): remove this deprecated metric - crbug.com/605836.
rappor::SampleDomainAndRegistryFromGURL(
rappor_service,
"ContentSettings.PermissionRequested.Midi.Url",
requesting_origin);
rappor_service->RecordSample(
"ContentSettings.PermissionRequested.Midi.Url2",
rappor::LOW_FREQUENCY_ETLD_PLUS_ONE_RAPPOR_TYPE,
rappor::GetDomainAndRegistrySampleFromGURL(requesting_origin));
}
}
bool secure_origin = content::IsOriginSecure(requesting_origin);
UMA_HISTOGRAM_ENUMERATION(
"ContentSettings.PermissionRequested",
static_cast<base::HistogramBase::Sample>(permission),
static_cast<base::HistogramBase::Sample>(PermissionType::NUM));
if (secure_origin) {
UMA_HISTOGRAM_ENUMERATION(
"ContentSettings.PermissionRequested_SecureOrigin",
static_cast<base::HistogramBase::Sample>(permission),
static_cast<base::HistogramBase::Sample>(PermissionType::NUM));
} else {
UMA_HISTOGRAM_ENUMERATION(
"ContentSettings.PermissionRequested_InsecureOrigin",
static_cast<base::HistogramBase::Sample>(permission),
static_cast<base::HistogramBase::Sample>(PermissionType::NUM));
}
// In order to gauge the compatibility risk of implementing an improved
// iframe permissions security model, we would like to know the ratio of
// same-origin to cross-origin permission requests. Our estimate of this
// ratio could be somewhat biased by repeated requests coming from a
// single frame, but we expect this to be insignificant.
if (requesting_origin.GetOrigin() != embedding_origin.GetOrigin()) {
content::PermissionManager* manager = profile->GetPermissionManager();
if (!manager)
return;
blink::mojom::PermissionStatus embedding_permission_status =
manager->GetPermissionStatus(permission, embedding_origin,
embedding_origin);
base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
"Permissions.Requested.CrossOrigin_" +
PermissionUtil::GetPermissionString(permission),
1, static_cast<int>(blink::mojom::PermissionStatus::LAST),
static_cast<int>(blink::mojom::PermissionStatus::LAST) + 1,
base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add(static_cast<int>(embedding_permission_status));
} else {
UMA_HISTOGRAM_ENUMERATION(
"Permissions.Requested.SameOrigin",
static_cast<base::HistogramBase::Sample>(permission),
static_cast<base::HistogramBase::Sample>(PermissionType::NUM));
}
}
} // anonymous namespace
const char PermissionUmaUtil::kPermissionsPromptShown[] =
"Permissions.Prompt.Shown";
const char PermissionUmaUtil::kPermissionsPromptAccepted[] =
"Permissions.Prompt.Accepted";
const char PermissionUmaUtil::kPermissionsPromptDenied[] =
"Permissions.Prompt.Denied";
const char PermissionUmaUtil::kPermissionsPromptRequestsPerPrompt[] =
"Permissions.Prompt.RequestsPerPrompt";
const char PermissionUmaUtil::kPermissionsPromptMergedBubbleTypes[] =
"Permissions.Prompt.MergedBubbleTypes";
const char PermissionUmaUtil::kPermissionsPromptMergedBubbleAccepted[] =
"Permissions.Prompt.MergedBubbleAccepted";
const char PermissionUmaUtil::kPermissionsPromptMergedBubbleDenied[] =
"Permissions.Prompt.MergedBubbleDenied";
// Make sure you update histograms.xml permission histogram_suffix if you
// add new permission
void PermissionUmaUtil::PermissionRequested(PermissionType permission,
const GURL& requesting_origin,
const GURL& embedding_origin,
Profile* profile) {
RecordPermissionRequest(permission, requesting_origin, embedding_origin,
profile);
}
void PermissionUmaUtil::PermissionGranted(PermissionType permission,
const GURL& requesting_origin) {
RecordPermissionAction(permission, GRANTED, requesting_origin);
}
void PermissionUmaUtil::PermissionDenied(PermissionType permission,
const GURL& requesting_origin) {
RecordPermissionAction(permission, DENIED, requesting_origin);
}
void PermissionUmaUtil::PermissionDismissed(PermissionType permission,
const GURL& requesting_origin) {
RecordPermissionAction(permission, DISMISSED, requesting_origin);
}
void PermissionUmaUtil::PermissionIgnored(PermissionType permission,
const GURL& requesting_origin) {
RecordPermissionAction(permission, IGNORED, requesting_origin);
}
void PermissionUmaUtil::PermissionRevoked(PermissionType permission,
const GURL& revoked_origin) {
// TODO(tsergeant): Expand metrics definitions for revocation to include all
// permissions.
if (permission == PermissionType::NOTIFICATIONS ||
permission == PermissionType::GEOLOCATION ||
permission == PermissionType::AUDIO_CAPTURE ||
permission == PermissionType::VIDEO_CAPTURE) {
RecordPermissionAction(permission, REVOKED, revoked_origin);
}
}
void PermissionUmaUtil::PermissionPromptShown(
const std::vector<PermissionBubbleRequest*>& requests) {
DCHECK(!requests.empty());
PermissionBubbleType permission_prompt_type = PermissionBubbleType::MULTIPLE;
if (requests.size() == 1)
permission_prompt_type = requests[0]->GetPermissionBubbleType();
PERMISSION_BUBBLE_TYPE_UMA(kPermissionsPromptShown, permission_prompt_type);
UMA_HISTOGRAM_ENUMERATION(
kPermissionsPromptRequestsPerPrompt,
static_cast<base::HistogramBase::Sample>(requests.size()),
static_cast<base::HistogramBase::Sample>(10));
if (requests.size() > 1) {
for (const auto* request : requests) {
PERMISSION_BUBBLE_TYPE_UMA(kPermissionsPromptMergedBubbleTypes,
request->GetPermissionBubbleType());
}
}
}
void PermissionUmaUtil::PermissionPromptAccepted(
const std::vector<PermissionBubbleRequest*>& requests,
const std::vector<bool>& accept_states) {
DCHECK(!requests.empty());
DCHECK(requests.size() == accept_states.size());
bool all_accepted = accept_states[0];
PermissionBubbleType permission_prompt_type =
requests[0]->GetPermissionBubbleType();
if (requests.size() > 1) {
permission_prompt_type = PermissionBubbleType::MULTIPLE;
for (size_t i = 0; i < requests.size(); ++i) {
const auto* request = requests[i];
if (accept_states[i]) {
PERMISSION_BUBBLE_TYPE_UMA(kPermissionsPromptMergedBubbleAccepted,
request->GetPermissionBubbleType());
} else {
all_accepted = false;
PERMISSION_BUBBLE_TYPE_UMA(kPermissionsPromptMergedBubbleDenied,
request->GetPermissionBubbleType());
}
}
}
if (all_accepted) {
PERMISSION_BUBBLE_TYPE_UMA(kPermissionsPromptAccepted,
permission_prompt_type);
} else {
PERMISSION_BUBBLE_TYPE_UMA(kPermissionsPromptDenied,
permission_prompt_type);
}
}
void PermissionUmaUtil::PermissionPromptDenied(
const std::vector<PermissionBubbleRequest*>& requests) {
DCHECK(!requests.empty());
DCHECK(requests.size() == 1);
PERMISSION_BUBBLE_TYPE_UMA(kPermissionsPromptDenied,
requests[0]->GetPermissionBubbleType());
}