1 // Copyright 2020 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 "android_webview/browser/metrics/visibility_metrics_logger.h"
6
7 #include "base/metrics/histogram_macros.h"
8 #include "base/no_destructor.h"
9 #include "base/rand_util.h"
10 #include "base/time/time.h"
11 #include "content/public/browser/browser_task_traits.h"
12 #include "content/public/browser/browser_thread.h"
13
14 using base::NoDestructor;
15 using content::BrowserThread;
16
17 namespace android_webview {
18
19 // Have bypassed the usual macros here because they do not support a
20 // means to increment counters by more than 1 per call.
21 base::HistogramBase*
CreateHistogramForDurationTracking(const char * name,int max_value)22 VisibilityMetricsLogger::CreateHistogramForDurationTracking(const char* name,
23 int max_value) {
24 return base::Histogram::FactoryGet(
25 name, 1, max_value + 1, max_value + 2,
26 base::HistogramBase::kUmaTargetedHistogramFlag);
27 }
28
GetGlobalVisibilityHistogram()29 base::HistogramBase* VisibilityMetricsLogger::GetGlobalVisibilityHistogram() {
30 static NoDestructor<base::HistogramBase*> histogram(
31 CreateHistogramForDurationTracking(
32 "Android.WebView.Visibility.Global",
33 static_cast<int>(VisibilityMetricsLogger::Visibility::kMaxValue)));
34 return *histogram;
35 }
36
37 base::HistogramBase*
GetPerWebViewVisibilityHistogram()38 VisibilityMetricsLogger::GetPerWebViewVisibilityHistogram() {
39 static NoDestructor<base::HistogramBase*> histogram(
40 CreateHistogramForDurationTracking(
41 "Android.WebView.Visibility.PerWebView",
42 static_cast<int>(VisibilityMetricsLogger::Visibility::kMaxValue)));
43 return *histogram;
44 }
45
46 base::HistogramBase*
GetGlobalOpenWebVisibilityHistogram()47 VisibilityMetricsLogger::GetGlobalOpenWebVisibilityHistogram() {
48 static NoDestructor<base::HistogramBase*> histogram(
49 CreateHistogramForDurationTracking(
50 "Android.WebView.WebViewOpenWebVisible.Global",
51 static_cast<int>(
52 VisibilityMetricsLogger::WebViewOpenWebVisibility::kMaxValue)));
53 return *histogram;
54 }
55
56 base::HistogramBase*
GetPerWebViewOpenWebVisibilityHistogram()57 VisibilityMetricsLogger::GetPerWebViewOpenWebVisibilityHistogram() {
58 static NoDestructor<base::HistogramBase*> histogram(
59 CreateHistogramForDurationTracking(
60 "Android.WebView.WebViewOpenWebVisible.PerWebView",
61 static_cast<int>(
62 VisibilityMetricsLogger::WebViewOpenWebVisibility::kMaxValue)));
63 return *histogram;
64 }
65
66 base::HistogramBase*
GetOpenWebVisibileScreenPortionHistogram()67 VisibilityMetricsLogger::GetOpenWebVisibileScreenPortionHistogram() {
68 static NoDestructor<base::HistogramBase*> histogram(
69 CreateHistogramForDurationTracking(
70 "Android.WebView.WebViewOpenWebVisible.ScreenPortion",
71 static_cast<int>(VisibilityMetricsLogger::
72 WebViewOpenWebScreenPortion::kMaxValue)));
73 return *histogram;
74 }
75
VisibilityMetricsLogger()76 VisibilityMetricsLogger::VisibilityMetricsLogger() {
77 DCHECK_CURRENTLY_ON(BrowserThread::UI);
78 last_update_time_ = base::TimeTicks::Now();
79 }
80
81 VisibilityMetricsLogger::~VisibilityMetricsLogger() = default;
82
AddClient(Client * client)83 void VisibilityMetricsLogger::AddClient(Client* client) {
84 DCHECK_CURRENTLY_ON(BrowserThread::UI);
85 DCHECK(client_visibility_.find(client) == client_visibility_.end());
86
87 UpdateDurations(base::TimeTicks::Now());
88
89 client_visibility_[client] = VisibilityInfo();
90 ProcessClientUpdate(client, client->GetVisibilityInfo());
91 }
92
RemoveClient(Client * client)93 void VisibilityMetricsLogger::RemoveClient(Client* client) {
94 DCHECK_CURRENTLY_ON(BrowserThread::UI);
95 DCHECK(client_visibility_.find(client) != client_visibility_.end());
96
97 UpdateDurations(base::TimeTicks::Now());
98
99 ProcessClientUpdate(client, VisibilityInfo());
100 client_visibility_.erase(client);
101 }
102
ClientVisibilityChanged(Client * client)103 void VisibilityMetricsLogger::ClientVisibilityChanged(Client* client) {
104 DCHECK_CURRENTLY_ON(BrowserThread::UI);
105 DCHECK(client_visibility_.find(client) != client_visibility_.end());
106
107 UpdateDurations(base::TimeTicks::Now());
108
109 ProcessClientUpdate(client, client->GetVisibilityInfo());
110 }
111
UpdateOpenWebScreenArea(int pixels,int percentage)112 void VisibilityMetricsLogger::UpdateOpenWebScreenArea(int pixels,
113 int percentage) {
114 DCHECK_CURRENTLY_ON(BrowserThread::UI);
115
116 UpdateDurations(base::TimeTicks::Now());
117
118 open_web_screen_area_pixels_ = pixels;
119 open_web_screen_area_percentage_ = percentage;
120
121 DCHECK(percentage >= 0);
122 DCHECK(percentage <= 100);
123 current_open_web_screen_portion_ =
124 static_cast<VisibilityMetricsLogger::WebViewOpenWebScreenPortion>(
125 percentage / 10);
126 }
127
UpdateDurations(base::TimeTicks update_time)128 void VisibilityMetricsLogger::UpdateDurations(base::TimeTicks update_time) {
129 base::TimeDelta delta = update_time - last_update_time_;
130 if (visible_client_count_ > 0) {
131 visible_duration_tracker_.any_webview_tracked_duration_ += delta;
132 } else {
133 visible_duration_tracker_.no_webview_tracked_duration_ += delta;
134 }
135
136 if (visible_webcontent_client_count_ > 0) {
137 webcontent_visible_tracker_.any_webview_tracked_duration_ += delta;
138 } else {
139 webcontent_visible_tracker_.no_webview_tracked_duration_ += delta;
140 }
141
142 visible_duration_tracker_.per_webview_duration_ +=
143 delta * visible_client_count_;
144 visible_duration_tracker_.per_webview_untracked_duration_ +=
145 delta * (client_visibility_.size() - visible_client_count_);
146
147 webcontent_visible_tracker_.per_webview_duration_ +=
148 delta * visible_webcontent_client_count_;
149 webcontent_visible_tracker_.per_webview_untracked_duration_ +=
150 delta * (client_visibility_.size() - visible_webcontent_client_count_);
151
152 open_web_screen_portion_tracked_duration_[static_cast<int>(
153 current_open_web_screen_portion_)] += delta;
154
155 last_update_time_ = update_time;
156 }
157
IsVisible() const158 bool VisibilityMetricsLogger::VisibilityInfo::IsVisible() const {
159 return view_attached && view_visible && window_visible;
160 }
161
ContainsOpenWebContent() const162 bool VisibilityMetricsLogger::VisibilityInfo::ContainsOpenWebContent() const {
163 return scheme_http_or_https;
164 }
165
IsDisplayingOpenWebContent() const166 bool VisibilityMetricsLogger::VisibilityInfo::IsDisplayingOpenWebContent()
167 const {
168 return IsVisible() && ContainsOpenWebContent();
169 }
170
ProcessClientUpdate(Client * client,const VisibilityInfo & info)171 void VisibilityMetricsLogger::ProcessClientUpdate(Client* client,
172 const VisibilityInfo& info) {
173 VisibilityInfo curr_info = client_visibility_[client];
174 bool was_visible = curr_info.IsVisible();
175 bool is_visible = info.IsVisible();
176 bool was_visible_web = curr_info.IsDisplayingOpenWebContent();
177 bool is_visible_web = info.IsDisplayingOpenWebContent();
178 client_visibility_[client] = info;
179 DCHECK(!was_visible || visible_client_count_ > 0);
180
181 bool any_client_was_visible = visible_client_count_ > 0;
182
183 if (!was_visible && is_visible) {
184 ++visible_client_count_;
185 } else if (was_visible && !is_visible) {
186 --visible_client_count_;
187 }
188
189 if (!was_visible_web && is_visible_web) {
190 ++visible_webcontent_client_count_;
191 } else if (was_visible_web && !is_visible_web) {
192 --visible_webcontent_client_count_;
193 }
194
195 bool any_client_is_visible = visible_client_count_ > 0;
196 if (on_visibility_changed_callback_ &&
197 any_client_was_visible != any_client_is_visible) {
198 on_visibility_changed_callback_.Run(any_client_is_visible);
199 }
200 }
201
SetOnVisibilityChangedCallback(OnVisibilityChangedCallback callback)202 void VisibilityMetricsLogger::SetOnVisibilityChangedCallback(
203 OnVisibilityChangedCallback callback) {
204 DCHECK_CURRENTLY_ON(BrowserThread::UI);
205 on_visibility_changed_callback_ = std::move(callback);
206 }
207
RecordMetrics()208 void VisibilityMetricsLogger::RecordMetrics() {
209 DCHECK_CURRENTLY_ON(BrowserThread::UI);
210 UpdateDurations(base::TimeTicks::Now());
211 RecordVisibilityMetrics();
212 RecordOpenWebDisplayMetrics();
213 RecordScreenPortionMetrics();
214 }
215
RecordVisibilityMetrics()216 void VisibilityMetricsLogger::RecordVisibilityMetrics() {
217 int32_t any_webview_visible_seconds;
218 int32_t no_webview_visible_seconds;
219 int32_t total_webview_visible_seconds;
220 int32_t total_no_webview_visible_seconds;
221
222 any_webview_visible_seconds =
223 visible_duration_tracker_.any_webview_tracked_duration_.InSeconds();
224 visible_duration_tracker_.any_webview_tracked_duration_ -=
225 base::TimeDelta::FromSeconds(any_webview_visible_seconds);
226 no_webview_visible_seconds =
227 visible_duration_tracker_.no_webview_tracked_duration_.InSeconds();
228 visible_duration_tracker_.no_webview_tracked_duration_ -=
229 base::TimeDelta::FromSeconds(no_webview_visible_seconds);
230
231 total_webview_visible_seconds =
232 visible_duration_tracker_.per_webview_duration_.InSeconds();
233 visible_duration_tracker_.per_webview_duration_ -=
234 base::TimeDelta::FromSeconds(total_webview_visible_seconds);
235 total_no_webview_visible_seconds =
236 visible_duration_tracker_.per_webview_untracked_duration_.InSeconds();
237 visible_duration_tracker_.per_webview_untracked_duration_ -=
238 base::TimeDelta::FromSeconds(total_no_webview_visible_seconds);
239
240 if (any_webview_visible_seconds) {
241 GetGlobalVisibilityHistogram()->AddCount(
242 static_cast<int>(Visibility::kVisible), any_webview_visible_seconds);
243 }
244 if (no_webview_visible_seconds) {
245 GetGlobalVisibilityHistogram()->AddCount(
246 static_cast<int>(Visibility::kNotVisible), no_webview_visible_seconds);
247 }
248
249 if (total_webview_visible_seconds) {
250 GetPerWebViewVisibilityHistogram()->AddCount(
251 static_cast<int>(Visibility::kVisible), total_webview_visible_seconds);
252 }
253 if (total_no_webview_visible_seconds) {
254 GetPerWebViewVisibilityHistogram()->AddCount(
255 static_cast<int>(Visibility::kNotVisible),
256 total_no_webview_visible_seconds);
257 }
258 }
259
RecordOpenWebDisplayMetrics()260 void VisibilityMetricsLogger::RecordOpenWebDisplayMetrics() {
261 int32_t any_webcontent_visible_seconds;
262 int32_t no_webcontent_visible_seconds;
263 int32_t total_webcontent_isible_seconds;
264 int32_t total_not_webcontent_or_not_visible_seconds;
265
266 any_webcontent_visible_seconds =
267 webcontent_visible_tracker_.any_webview_tracked_duration_.InSeconds();
268 webcontent_visible_tracker_.any_webview_tracked_duration_ -=
269 base::TimeDelta::FromSeconds(any_webcontent_visible_seconds);
270 no_webcontent_visible_seconds =
271 webcontent_visible_tracker_.no_webview_tracked_duration_.InSeconds();
272 webcontent_visible_tracker_.no_webview_tracked_duration_ -=
273 base::TimeDelta::FromSeconds(no_webcontent_visible_seconds);
274
275 total_webcontent_isible_seconds =
276 webcontent_visible_tracker_.per_webview_duration_.InSeconds();
277 webcontent_visible_tracker_.per_webview_duration_ -=
278 base::TimeDelta::FromSeconds(total_webcontent_isible_seconds);
279 total_not_webcontent_or_not_visible_seconds =
280 webcontent_visible_tracker_.per_webview_untracked_duration_.InSeconds();
281 webcontent_visible_tracker_.per_webview_untracked_duration_ -=
282 base::TimeDelta::FromSeconds(total_not_webcontent_or_not_visible_seconds);
283
284 if (any_webcontent_visible_seconds) {
285 GetGlobalOpenWebVisibilityHistogram()->AddCount(
286 static_cast<int>(WebViewOpenWebVisibility::kDisplayOpenWebContent),
287 any_webcontent_visible_seconds);
288 }
289 if (no_webcontent_visible_seconds) {
290 GetGlobalOpenWebVisibilityHistogram()->AddCount(
291 static_cast<int>(WebViewOpenWebVisibility::kNotDisplayOpenWebContent),
292 no_webcontent_visible_seconds);
293 }
294
295 if (total_webcontent_isible_seconds) {
296 GetPerWebViewOpenWebVisibilityHistogram()->AddCount(
297 static_cast<int>(WebViewOpenWebVisibility::kDisplayOpenWebContent),
298 total_webcontent_isible_seconds);
299 }
300 if (total_not_webcontent_or_not_visible_seconds) {
301 GetPerWebViewOpenWebVisibilityHistogram()->AddCount(
302 static_cast<int>(WebViewOpenWebVisibility::kNotDisplayOpenWebContent),
303 total_not_webcontent_or_not_visible_seconds);
304 }
305 }
306
RecordScreenPortionMetrics()307 void VisibilityMetricsLogger::RecordScreenPortionMetrics() {
308 for (int i = 0; i < static_cast<int>(WebViewOpenWebScreenPortion::kMaxValue);
309 i++) {
310 int32_t elapsed_seconds =
311 open_web_screen_portion_tracked_duration_[i].InSeconds();
312 if (elapsed_seconds == 0)
313 continue;
314
315 open_web_screen_portion_tracked_duration_[i] -=
316 base::TimeDelta::FromSeconds(elapsed_seconds);
317 GetOpenWebVisibileScreenPortionHistogram()->AddCount(i, elapsed_seconds);
318 }
319 }
320
321 } // namespace android_webview