blob: 8784c5abdb92badfa77a56b539f61c91b653a95d [file] [log] [blame]
// Copyright 2020 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 "android_webview/browser/metrics/visibility_metrics_logger.h"
#include "android_webview/common/aw_features.h"
#include "base/cxx17_backports.h"
#include "base/feature_list.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_base.h"
#include "base/time/time.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/url_constants.h"
using content::BrowserThread;
namespace android_webview {
namespace {
// Have bypassed the usual macros here because they do not support a
// means to increment counters by more than 1 per call.
base::HistogramBase* CreateHistogramForDurationTracking(const char* name,
int max_value) {
return base::Histogram::FactoryGet(
name, 1, max_value + 1, max_value + 2,
base::HistogramBase::kUmaTargetedHistogramFlag);
}
base::HistogramBase* GetGlobalVisibilityHistogram() {
static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
"Android.WebView.Visibility.Global",
static_cast<int>(VisibilityMetricsLogger::Visibility::kMaxValue)));
return histogram;
}
base::HistogramBase* GetPerWebViewVisibilityHistogram() {
static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
"Android.WebView.Visibility.PerWebView",
static_cast<int>(VisibilityMetricsLogger::Visibility::kMaxValue)));
return histogram;
}
void LogGlobalVisibleScheme(VisibilityMetricsLogger::Scheme scheme,
int32_t seconds) {
static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
"Android.WebView.VisibleScheme.Global",
static_cast<int>(VisibilityMetricsLogger::Scheme::kMaxValue)));
histogram->AddCount(static_cast<int32_t>(scheme), seconds);
}
void LogPerWebViewVisibleScheme(VisibilityMetricsLogger::Scheme scheme,
int32_t seconds) {
static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
"Android.WebView.VisibleScheme.PerWebView",
static_cast<int>(VisibilityMetricsLogger::Scheme::kMaxValue)));
histogram->AddCount(static_cast<int32_t>(scheme), seconds);
}
base::HistogramBase* GetOpenWebVisibileScreenPortionHistogram() {
static base::HistogramBase* histogram(CreateHistogramForDurationTracking(
"Android.WebView.WebViewOpenWebVisible.ScreenPortion2",
static_cast<int>(
VisibilityMetricsLogger::WebViewOpenWebScreenPortion::kMaxValue)));
return histogram;
}
} // anonymous namespace
// static
VisibilityMetricsLogger::Scheme VisibilityMetricsLogger::SchemeStringToEnum(
const std::string& scheme) {
if (scheme.empty())
return Scheme::kEmpty;
if (scheme == url::kHttpScheme)
return Scheme::kHttp;
if (scheme == url::kHttpsScheme)
return Scheme::kHttps;
if (scheme == url::kFileScheme)
return Scheme::kFile;
if (scheme == url::kFtpScheme)
return Scheme::kFtp;
if (scheme == url::kDataScheme)
return Scheme::kData;
if (scheme == url::kJavaScriptScheme)
return Scheme::kJavaScript;
if (scheme == url::kAboutScheme)
return Scheme::kAbout;
if (scheme == content::kChromeUIScheme)
return Scheme::kChrome;
if (scheme == url::kBlobScheme)
return Scheme::kBlob;
if (scheme == url::kContentScheme)
return Scheme::kContent;
if (scheme == "intent")
return Scheme::kIntent;
return Scheme::kUnknown;
}
VisibilityMetricsLogger::VisibilityMetricsLogger() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
last_update_time_ = base::TimeTicks::Now();
}
VisibilityMetricsLogger::~VisibilityMetricsLogger() = default;
void VisibilityMetricsLogger::AddClient(Client* client) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(client_visibility_.find(client) == client_visibility_.end());
UpdateDurations();
client_visibility_[client] = VisibilityInfo();
ProcessClientUpdate(client, client->GetVisibilityInfo());
}
void VisibilityMetricsLogger::RemoveClient(Client* client) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(client_visibility_.find(client) != client_visibility_.end());
UpdateDurations();
ProcessClientUpdate(client, VisibilityInfo());
client_visibility_.erase(client);
}
void VisibilityMetricsLogger::ClientVisibilityChanged(Client* client) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(client_visibility_.find(client) != client_visibility_.end());
UpdateDurations();
ProcessClientUpdate(client, client->GetVisibilityInfo());
}
void VisibilityMetricsLogger::UpdateOpenWebScreenArea(int pixels,
int percentage) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
UpdateDurations();
DCHECK(percentage >= 0);
DCHECK(percentage <= 100);
if (pixels == 0) {
current_open_web_screen_portion_ = VisibilityMetricsLogger::
WebViewOpenWebScreenPortion::kExactlyZeroPercent;
} else {
current_open_web_screen_portion_ =
static_cast<VisibilityMetricsLogger::WebViewOpenWebScreenPortion>(
percentage / 10);
}
}
void VisibilityMetricsLogger::UpdateDurations() {
base::TimeTicks update_time = base::TimeTicks::Now();
base::TimeDelta delta = update_time - last_update_time_;
if (all_clients_visible_count_ > 0) {
all_clients_tracker_.any_webview_tracked_duration_ += delta;
} else {
all_clients_tracker_.no_webview_tracked_duration_ += delta;
}
all_clients_tracker_.per_webview_duration_ +=
delta * all_clients_visible_count_;
all_clients_tracker_.per_webview_untracked_duration_ +=
delta * (client_visibility_.size() - all_clients_visible_count_);
for (size_t i = 0; i < base::size(per_scheme_visible_counts_); i++) {
if (!per_scheme_visible_counts_[i])
continue;
per_scheme_trackers_[i].any_webview_tracked_duration_ += delta;
per_scheme_trackers_[i].per_webview_duration_ +=
delta * per_scheme_visible_counts_[i];
}
if (per_scheme_visible_counts_[static_cast<size_t>(Scheme::kHttp)] > 0 ||
per_scheme_visible_counts_[static_cast<size_t>(Scheme::kHttps)] > 0) {
open_web_screen_portion_tracked_duration_[static_cast<int>(
current_open_web_screen_portion_)] += delta;
}
last_update_time_ = update_time;
}
bool VisibilityMetricsLogger::VisibilityInfo::IsVisible() const {
return view_attached && view_visible && window_visible;
}
bool VisibilityMetricsLogger::VisibilityInfo::IsDisplayingOpenWebContent()
const {
return IsVisible() && (scheme == Scheme::kHttp || scheme == Scheme::kHttps);
}
void VisibilityMetricsLogger::ProcessClientUpdate(Client* client,
const VisibilityInfo& info) {
VisibilityInfo curr_info = client_visibility_[client];
bool was_visible = curr_info.IsVisible();
bool is_visible = info.IsVisible();
Scheme old_scheme = curr_info.scheme;
Scheme new_scheme = info.scheme;
client_visibility_[client] = info;
DCHECK(!was_visible || all_clients_visible_count_ > 0);
bool any_client_was_visible = all_clients_visible_count_ > 0;
if (!was_visible && is_visible) {
++all_clients_visible_count_;
} else if (was_visible && !is_visible) {
--all_clients_visible_count_;
}
if (was_visible)
per_scheme_visible_counts_[static_cast<size_t>(old_scheme)]--;
if (is_visible)
per_scheme_visible_counts_[static_cast<size_t>(new_scheme)]++;
bool any_client_is_visible = all_clients_visible_count_ > 0;
if (on_visibility_changed_callback_ &&
any_client_was_visible != any_client_is_visible) {
on_visibility_changed_callback_.Run(any_client_is_visible);
}
}
void VisibilityMetricsLogger::SetOnVisibilityChangedCallback(
OnVisibilityChangedCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
on_visibility_changed_callback_ = std::move(callback);
}
void VisibilityMetricsLogger::RecordMetrics() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
UpdateDurations();
RecordVisibilityMetrics();
RecordVisibleSchemeMetrics();
RecordScreenPortionMetrics();
}
void VisibilityMetricsLogger::RecordVisibilityMetrics() {
int32_t any_webview_visible_seconds;
int32_t no_webview_visible_seconds;
int32_t total_webview_visible_seconds;
int32_t total_no_webview_visible_seconds;
any_webview_visible_seconds =
all_clients_tracker_.any_webview_tracked_duration_.InSeconds();
all_clients_tracker_.any_webview_tracked_duration_ -=
base::Seconds(any_webview_visible_seconds);
no_webview_visible_seconds =
all_clients_tracker_.no_webview_tracked_duration_.InSeconds();
all_clients_tracker_.no_webview_tracked_duration_ -=
base::Seconds(no_webview_visible_seconds);
total_webview_visible_seconds =
all_clients_tracker_.per_webview_duration_.InSeconds();
all_clients_tracker_.per_webview_duration_ -=
base::Seconds(total_webview_visible_seconds);
total_no_webview_visible_seconds =
all_clients_tracker_.per_webview_untracked_duration_.InSeconds();
all_clients_tracker_.per_webview_untracked_duration_ -=
base::Seconds(total_no_webview_visible_seconds);
if (any_webview_visible_seconds) {
GetGlobalVisibilityHistogram()->AddCount(
static_cast<int>(Visibility::kVisible), any_webview_visible_seconds);
}
if (no_webview_visible_seconds) {
GetGlobalVisibilityHistogram()->AddCount(
static_cast<int>(Visibility::kNotVisible), no_webview_visible_seconds);
}
if (total_webview_visible_seconds) {
GetPerWebViewVisibilityHistogram()->AddCount(
static_cast<int>(Visibility::kVisible), total_webview_visible_seconds);
}
if (total_no_webview_visible_seconds) {
GetPerWebViewVisibilityHistogram()->AddCount(
static_cast<int>(Visibility::kNotVisible),
total_no_webview_visible_seconds);
}
}
void VisibilityMetricsLogger::RecordVisibleSchemeMetrics() {
for (size_t i = 0; i < base::size(per_scheme_trackers_); i++) {
Scheme scheme = static_cast<Scheme>(i);
auto& tracker = per_scheme_trackers_[i];
int32_t any_webview_seconds =
tracker.any_webview_tracked_duration_.InSeconds();
if (any_webview_seconds) {
tracker.any_webview_tracked_duration_ -=
base::Seconds(any_webview_seconds);
LogGlobalVisibleScheme(scheme, any_webview_seconds);
}
int32_t per_webview_seconds = tracker.per_webview_duration_.InSeconds();
if (per_webview_seconds) {
tracker.per_webview_duration_ -= base::Seconds(per_webview_seconds);
LogPerWebViewVisibleScheme(scheme, per_webview_seconds);
}
}
}
void VisibilityMetricsLogger::RecordScreenPortionMetrics() {
if (!base::FeatureList::IsEnabled(features::kWebViewMeasureScreenCoverage))
return;
for (size_t i = 0; i < base::size(open_web_screen_portion_tracked_duration_);
i++) {
int32_t elapsed_seconds =
open_web_screen_portion_tracked_duration_[i].InSeconds();
if (elapsed_seconds == 0)
continue;
open_web_screen_portion_tracked_duration_[i] -=
base::Seconds(elapsed_seconds);
GetOpenWebVisibileScreenPortionHistogram()->AddCount(i, elapsed_seconds);
}
}
} // namespace android_webview