blob: 7b2e3cd1960f78a9b25dfc2bdec2ca1867e52710 [file] [log] [blame]
// Copyright 2015 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 "components/security_interstitials/core/metrics_helper.h"
#include <utility>
#include "base/metrics/histogram.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "components/history/core/browser/history_service.h"
#include "components/rappor/rappor_service.h"
#include "components/rappor/rappor_utils.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
using base::RecordAction;
using base::UserMetricsAction;
namespace security_interstitials {
namespace {
// Used for setting bits in Rappor's "interstitial.*.flags"
enum InterstitialFlagBits {
DID_PROCEED = 0,
IS_REPEAT_VISIT = 1,
HIGHEST_USED_BIT = 1
};
// Directly adds to the UMA histograms, using the same properties as
// UMA_HISTOGRAM_ENUMERATION, because the macro doesn't allow non-constant
// histogram names.
void RecordSingleDecisionToMetrics(MetricsHelper::Decision decision,
const std::string& histogram_name) {
base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
histogram_name, 1, MetricsHelper::MAX_DECISION,
MetricsHelper::MAX_DECISION + 1,
base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add(decision);
}
void RecordSingleInteractionToMetrics(MetricsHelper::Interaction interaction,
const std::string& histogram_name) {
base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
histogram_name, 1, MetricsHelper::MAX_INTERACTION,
MetricsHelper::MAX_INTERACTION + 1,
base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->Add(interaction);
}
void MaybeRecordDecisionAsAction(MetricsHelper::Decision decision,
const std::string& metric_name) {
if (decision == MetricsHelper::PROCEED) {
if (metric_name == "malware")
RecordAction(UserMetricsAction("MalwareInterstitial.Proceed"));
else if (metric_name == "harmful")
RecordAction(UserMetricsAction("HarmfulInterstitial.Proceed"));
else if (metric_name == "ssl_overridable")
RecordAction(UserMetricsAction("SSLOverridableInterstitial.Proceed"));
} else if (decision == MetricsHelper::DONT_PROCEED) {
if (metric_name == "malware")
RecordAction(UserMetricsAction("MalwareInterstitial.Back"));
else if (metric_name == "harmful")
RecordAction(UserMetricsAction("HarmfulInterstitial.Back"));
else if (metric_name == "ssl_overridable")
RecordAction(UserMetricsAction("SSLOverridableInterstitial.Back"));
else if (metric_name == "ssl_nonoverridable")
RecordAction(UserMetricsAction("SSLNonOverridableInsterstitial.Back"));
else if (metric_name == "bad_clock")
RecordAction(UserMetricsAction("BadClockInterstitial.Back"));
}
}
void MaybeRecordInteractionAsAction(MetricsHelper::Interaction interaction,
const std::string& metric_name) {
if (interaction == MetricsHelper::TOTAL_VISITS) {
if (metric_name == "malware")
RecordAction(UserMetricsAction("MalwareInterstitial.Show"));
else if (metric_name == "harmful")
RecordAction(UserMetricsAction("HarmfulInterstitial.Show"));
else if (metric_name == "ssl_overridable")
RecordAction(UserMetricsAction("SSLOverridableInterstitial.Show"));
else if (metric_name == "ssl_nonoverridable")
RecordAction(UserMetricsAction("SSLNonOverridableInterstitial.Show"));
else if (metric_name == "bad_clock")
RecordAction(UserMetricsAction("BadClockInterstitial.Show"));
} else if (interaction == MetricsHelper::SHOW_ADVANCED) {
if (metric_name == "malware") {
RecordAction(UserMetricsAction("MalwareInterstitial.Advanced"));
} else if (metric_name == "harmful") {
RecordAction(UserMetricsAction("HarmfulInterstitial.Advanced"));
} else if (metric_name == "ssl_overridable" ||
metric_name == "ssl_nonoverridable") {
RecordAction(UserMetricsAction("SSLInterstitial.Advanced"));
}
} else if (interaction == MetricsHelper::RELOAD) {
if (metric_name == "ssl_nonoverridable")
RecordAction(UserMetricsAction("SSLInterstitial.Reload"));
} else if (interaction == MetricsHelper::OPEN_TIME_SETTINGS) {
if (metric_name == "bad_clock")
RecordAction(UserMetricsAction("BadClockInterstitial.Settings"));
}
}
} // namespace
MetricsHelper::ReportDetails::ReportDetails()
: rappor_report_type(rappor::NUM_RAPPOR_TYPES) {}
MetricsHelper::MetricsHelper(const GURL& request_url,
const ReportDetails settings,
history::HistoryService* history_service,
rappor::RapporService* rappor_service)
: request_url_(request_url),
settings_(settings),
rappor_service_(rappor_service),
num_visits_(-1) {
DCHECK(!settings_.metric_prefix.empty());
if (settings_.rappor_report_type == rappor::NUM_RAPPOR_TYPES) // Default.
rappor_service_ = nullptr;
DCHECK(!rappor_service_ || !settings_.rappor_prefix.empty());
if (history_service) {
history_service->GetVisibleVisitCountToHost(
request_url_,
base::Bind(&MetricsHelper::OnGotHistoryCount, base::Unretained(this)),
&request_tracker_);
}
}
void MetricsHelper::RecordUserDecision(Decision decision) {
const std::string histogram_name(
"interstitial." + settings_.metric_prefix + ".decision");
RecordUserDecisionToMetrics(decision, histogram_name);
// Record additional information about sites that users have visited before.
// Report |decision| and SHOW together, filtered by the same history state
// so they they are paired regardless of when if num_visits_ is populated.
if (num_visits_ > 0 && (decision == PROCEED || decision == DONT_PROCEED)) {
RecordUserDecisionToMetrics(SHOW, histogram_name + ".repeat_visit");
RecordUserDecisionToMetrics(decision, histogram_name + ".repeat_visit");
}
MaybeRecordDecisionAsAction(decision, settings_.metric_prefix);
RecordUserDecisionToRappor(decision);
RecordExtraUserDecisionMetrics(decision);
}
void MetricsHelper::RecordUserDecisionToMetrics(
Decision decision,
const std::string& histogram_name) {
// Record the decision, and additionally |with extra_suffix|.
RecordSingleDecisionToMetrics(decision, histogram_name);
if (!settings_.extra_suffix.empty()) {
RecordSingleDecisionToMetrics(
decision, histogram_name + "." + settings_.extra_suffix);
}
}
void MetricsHelper::RecordUserDecisionToRappor(Decision decision) {
if (!rappor_service_ || (decision != PROCEED && decision != DONT_PROCEED))
return;
scoped_ptr<rappor::Sample> sample =
rappor_service_->CreateSample(settings_.rappor_report_type);
// This will populate, for example, "intersitial.malware.domain" or
// "interstitial.ssl2.domain". |domain| will be empty for hosts w/o TLDs.
const std::string domain =
rappor::GetDomainAndRegistrySampleFromGURL(request_url_);
sample->SetStringField("domain", domain);
// Only report history and decision if we have history data.
if (num_visits_ >= 0) {
int flags = 0;
if (decision == PROCEED)
flags |= 1 << InterstitialFlagBits::DID_PROCEED;
if (num_visits_ > 0)
flags |= 1 << InterstitialFlagBits::IS_REPEAT_VISIT;
// e.g. "interstitial.malware.flags"
sample->SetFlagsField("flags", flags,
InterstitialFlagBits::HIGHEST_USED_BIT + 1);
}
rappor_service_->RecordSampleObj("interstitial." + settings_.rappor_prefix,
std::move(sample));
}
void MetricsHelper::RecordUserInteraction(Interaction interaction) {
const std::string histogram_name(
"interstitial." + settings_.metric_prefix + ".interaction");
RecordSingleInteractionToMetrics(interaction, histogram_name);
if (!settings_.extra_suffix.empty()) {
RecordSingleInteractionToMetrics(
interaction, histogram_name + "." + settings_.extra_suffix);
}
MaybeRecordInteractionAsAction(interaction, settings_.metric_prefix);
RecordExtraUserInteractionMetrics(interaction);
}
void MetricsHelper::RecordShutdownMetrics() {
RecordExtraShutdownMetrics();
}
int MetricsHelper::NumVisits() {
return num_visits_;
}
void MetricsHelper::OnGotHistoryCount(bool success,
int num_visits,
base::Time /*first_visit*/) {
if (success)
num_visits_ = num_visits;
}
} // namespace security_interstitials