visibility_metrics_logger.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  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. #include "android_webview/browser/metrics/visibility_metrics_logger.h"
  5. #include "android_webview/common/aw_features.h"
  6. #include "base/feature_list.h"
  7. #include "base/metrics/histogram.h"
  8. #include "base/metrics/histogram_base.h"
  9. #include "base/time/time.h"
  10. #include "content/public/browser/browser_thread.h"
  11. #include "content/public/common/url_constants.h"
  12. using content::BrowserThread;
  13. namespace android_webview {
  14. namespace {
  15. const char* SchemeEnumToString(VisibilityMetricsLogger::Scheme scheme) {
  16. switch (scheme) {
  17. case VisibilityMetricsLogger::Scheme::kEmpty:
  18. return "empty";
  19. case VisibilityMetricsLogger::Scheme::kUnknown:
  20. return "unknown";
  21. case VisibilityMetricsLogger::Scheme::kHttp:
  22. return url::kHttpScheme;
  23. case VisibilityMetricsLogger::Scheme::kHttps:
  24. return url::kHttpsScheme;
  25. case VisibilityMetricsLogger::Scheme::kFile:
  26. return url::kFileScheme;
  27. case VisibilityMetricsLogger::Scheme::kFtp:
  28. return url::kFtpScheme;
  29. case VisibilityMetricsLogger::Scheme::kData:
  30. return url::kDataScheme;
  31. case VisibilityMetricsLogger::Scheme::kJavaScript:
  32. return url::kJavaScriptScheme;
  33. case VisibilityMetricsLogger::Scheme::kAbout:
  34. return url::kAboutScheme;
  35. case VisibilityMetricsLogger::Scheme::kChrome:
  36. return content::kChromeUIScheme;
  37. case VisibilityMetricsLogger::Scheme::kBlob:
  38. return url::kBlobScheme;
  39. case VisibilityMetricsLogger::Scheme::kContent:
  40. return url::kContentScheme;
  41. case VisibilityMetricsLogger::Scheme::kIntent:
  42. return "intent";
  43. default:
  44. NOTREACHED();
  45. }
  46. return "";
  47. }
  48. // Have bypassed the usual macros here because they do not support a
  49. // means to increment counters by more than 1 per call.
  50. base::HistogramBase* GetOrCreateHistogramForDurationTracking(
  51. const std::string& name,
  52. int max_value) {
  53. return base::Histogram::FactoryGet(
  54. name, 1, max_value + 1, max_value + 2,
  55. base::HistogramBase::kUmaTargetedHistogramFlag);
  56. }
  57. // base::Histogram::FactoryGet would internally convert to std::string anyway,
  58. // this overload is for convenience.
  59. base::HistogramBase* GetOrCreateHistogramForDurationTracking(const char* name,
  60. int max_value) {
  61. return GetOrCreateHistogramForDurationTracking(std::string(name), max_value);
  62. }
  63. base::HistogramBase* GetGlobalVisibilityHistogram() {
  64. static base::HistogramBase* histogram(GetOrCreateHistogramForDurationTracking(
  65. "Android.WebView.Visibility.Global",
  66. static_cast<int>(VisibilityMetricsLogger::Visibility::kMaxValue)));
  67. return histogram;
  68. }
  69. base::HistogramBase* GetPerWebViewVisibilityHistogram() {
  70. static base::HistogramBase* histogram(GetOrCreateHistogramForDurationTracking(
  71. "Android.WebView.Visibility.PerWebView",
  72. static_cast<int>(VisibilityMetricsLogger::Visibility::kMaxValue)));
  73. return histogram;
  74. }
  75. void LogGlobalVisibleScheme(VisibilityMetricsLogger::Scheme scheme,
  76. int32_t seconds) {
  77. static base::HistogramBase* histogram(GetOrCreateHistogramForDurationTracking(
  78. "Android.WebView.VisibleScheme.Global",
  79. static_cast<int>(VisibilityMetricsLogger::Scheme::kMaxValue)));
  80. histogram->AddCount(static_cast<int32_t>(scheme), seconds);
  81. }
  82. void LogPerWebViewVisibleScheme(VisibilityMetricsLogger::Scheme scheme,
  83. int32_t seconds) {
  84. static base::HistogramBase* histogram(GetOrCreateHistogramForDurationTracking(
  85. "Android.WebView.VisibleScheme.PerWebView",
  86. static_cast<int>(VisibilityMetricsLogger::Scheme::kMaxValue)));
  87. histogram->AddCount(static_cast<int32_t>(scheme), seconds);
  88. }
  89. void LogGlobalVisibleScreenCoverage(int percentage, int32_t seconds) {
  90. static base::HistogramBase* histogram(GetOrCreateHistogramForDurationTracking(
  91. "Android.WebView.VisibleScreenCoverage.Global", 100));
  92. histogram->AddCount(percentage, seconds);
  93. }
  94. void LogPerWebViewVisibleScreenCoverage(int percentage, int32_t seconds) {
  95. static base::HistogramBase* histogram(GetOrCreateHistogramForDurationTracking(
  96. "Android.WebView.VisibleScreenCoverage.PerWebView", 100));
  97. histogram->AddCount(percentage, seconds);
  98. }
  99. void LogPerSchemeVisibleScreenCoverage(VisibilityMetricsLogger::Scheme scheme,
  100. int percentage,
  101. int32_t seconds) {
  102. GetOrCreateHistogramForDurationTracking(
  103. std::string("Android.WebView.VisibleScreenCoverage.PerWebView.") +
  104. SchemeEnumToString(scheme),
  105. 100)
  106. ->AddCount(percentage, seconds);
  107. }
  108. } // anonymous namespace
  109. // static
  110. VisibilityMetricsLogger::Scheme VisibilityMetricsLogger::SchemeStringToEnum(
  111. const std::string& scheme) {
  112. if (scheme.empty())
  113. return Scheme::kEmpty;
  114. if (scheme == url::kHttpScheme)
  115. return Scheme::kHttp;
  116. if (scheme == url::kHttpsScheme)
  117. return Scheme::kHttps;
  118. if (scheme == url::kFileScheme)
  119. return Scheme::kFile;
  120. if (scheme == url::kFtpScheme)
  121. return Scheme::kFtp;
  122. if (scheme == url::kDataScheme)
  123. return Scheme::kData;
  124. if (scheme == url::kJavaScriptScheme)
  125. return Scheme::kJavaScript;
  126. if (scheme == url::kAboutScheme)
  127. return Scheme::kAbout;
  128. if (scheme == content::kChromeUIScheme)
  129. return Scheme::kChrome;
  130. if (scheme == url::kBlobScheme)
  131. return Scheme::kBlob;
  132. if (scheme == url::kContentScheme)
  133. return Scheme::kContent;
  134. if (scheme == "intent")
  135. return Scheme::kIntent;
  136. return Scheme::kUnknown;
  137. }
  138. VisibilityMetricsLogger::VisibilityMetricsLogger() {
  139. DCHECK_CURRENTLY_ON(BrowserThread::UI);
  140. last_update_time_ = base::TimeTicks::Now();
  141. }
  142. VisibilityMetricsLogger::~VisibilityMetricsLogger() = default;
  143. void VisibilityMetricsLogger::AddClient(Client* client) {
  144. DCHECK_CURRENTLY_ON(BrowserThread::UI);
  145. DCHECK(client_visibility_.find(client) == client_visibility_.end());
  146. UpdateDurations();
  147. client_visibility_[client] = VisibilityInfo();
  148. ProcessClientUpdate(client, client->GetVisibilityInfo());
  149. }
  150. void VisibilityMetricsLogger::RemoveClient(Client* client) {
  151. DCHECK_CURRENTLY_ON(BrowserThread::UI);
  152. DCHECK(client_visibility_.find(client) != client_visibility_.end());
  153. UpdateDurations();
  154. ProcessClientUpdate(client, VisibilityInfo());
  155. client_visibility_.erase(client);
  156. }
  157. void VisibilityMetricsLogger::ClientVisibilityChanged(Client* client) {
  158. DCHECK_CURRENTLY_ON(BrowserThread::UI);
  159. DCHECK(client_visibility_.find(client) != client_visibility_.end());
  160. UpdateDurations();
  161. ProcessClientUpdate(client, client->GetVisibilityInfo());
  162. }
  163. void VisibilityMetricsLogger::UpdateScreenCoverage(
  164. int global_percentage,
  165. const std::vector<Scheme>& schemes,
  166. const std::vector<int>& scheme_percentages) {
  167. DCHECK_CURRENTLY_ON(BrowserThread::UI);
  168. DCHECK(schemes.size() == scheme_percentages.size());
  169. UpdateDurations();
  170. DCHECK(global_percentage >= 0);
  171. DCHECK(global_percentage <= 100);
  172. global_coverage_percentage_ = global_percentage;
  173. schemes_to_coverage_percentages_.clear();
  174. for (size_t i = 0; i < schemes.size(); i++) {
  175. DCHECK(scheme_percentages[i] >= 0);
  176. DCHECK(scheme_percentages[i] <= 100);
  177. schemes_to_coverage_percentages_.emplace(schemes[i], scheme_percentages[i]);
  178. }
  179. }
  180. void VisibilityMetricsLogger::UpdateDurations() {
  181. base::TimeTicks update_time = base::TimeTicks::Now();
  182. base::TimeDelta delta = update_time - last_update_time_;
  183. if (all_clients_visible_count_ > 0) {
  184. all_clients_tracker_.any_webview_tracked_duration_ += delta;
  185. } else {
  186. all_clients_tracker_.no_webview_tracked_duration_ += delta;
  187. }
  188. all_clients_tracker_.per_webview_duration_ +=
  189. delta * all_clients_visible_count_;
  190. all_clients_tracker_.per_webview_untracked_duration_ +=
  191. delta * (client_visibility_.size() - all_clients_visible_count_);
  192. for (size_t i = 0; i < std::size(per_scheme_visible_counts_); i++) {
  193. if (!per_scheme_visible_counts_[i])
  194. continue;
  195. per_scheme_trackers_[i].any_webview_tracked_duration_ += delta;
  196. per_scheme_trackers_[i].per_webview_duration_ +=
  197. delta * per_scheme_visible_counts_[i];
  198. }
  199. if (all_clients_visible_count_ > 0) {
  200. global_coverage_percentage_durations_[global_coverage_percentage_] += delta;
  201. for (auto& scheme_and_percentage : schemes_to_coverage_percentages_) {
  202. schemes_to_percentages_to_durations_[scheme_and_percentage.first]
  203. [scheme_and_percentage.second] +=
  204. delta;
  205. }
  206. }
  207. last_update_time_ = update_time;
  208. }
  209. bool VisibilityMetricsLogger::VisibilityInfo::IsVisible() const {
  210. return view_attached && view_visible && window_visible;
  211. }
  212. void VisibilityMetricsLogger::ProcessClientUpdate(Client* client,
  213. const VisibilityInfo& info) {
  214. VisibilityInfo curr_info = client_visibility_[client];
  215. bool was_visible = curr_info.IsVisible();
  216. bool is_visible = info.IsVisible();
  217. Scheme old_scheme = curr_info.scheme;
  218. Scheme new_scheme = info.scheme;
  219. client_visibility_[client] = info;
  220. DCHECK(!was_visible || all_clients_visible_count_ > 0);
  221. bool any_client_was_visible = all_clients_visible_count_ > 0;
  222. if (!was_visible && is_visible) {
  223. ++all_clients_visible_count_;
  224. } else if (was_visible && !is_visible) {
  225. --all_clients_visible_count_;
  226. }
  227. if (was_visible)
  228. per_scheme_visible_counts_[static_cast<size_t>(old_scheme)]--;
  229. if (is_visible)
  230. per_scheme_visible_counts_[static_cast<size_t>(new_scheme)]++;
  231. bool any_client_is_visible = all_clients_visible_count_ > 0;
  232. if (on_visibility_changed_callback_ &&
  233. any_client_was_visible != any_client_is_visible) {
  234. on_visibility_changed_callback_.Run(any_client_is_visible);
  235. }
  236. }
  237. void VisibilityMetricsLogger::SetOnVisibilityChangedCallback(
  238. OnVisibilityChangedCallback callback) {
  239. DCHECK_CURRENTLY_ON(BrowserThread::UI);
  240. on_visibility_changed_callback_ = std::move(callback);
  241. }
  242. void VisibilityMetricsLogger::RecordMetrics() {
  243. DCHECK_CURRENTLY_ON(BrowserThread::UI);
  244. UpdateDurations();
  245. RecordVisibilityMetrics();
  246. RecordVisibleSchemeMetrics();
  247. RecordScreenCoverageMetrics();
  248. }
  249. void VisibilityMetricsLogger::RecordVisibilityMetrics() {
  250. int32_t any_webview_visible_seconds;
  251. int32_t no_webview_visible_seconds;
  252. int32_t total_webview_visible_seconds;
  253. int32_t total_no_webview_visible_seconds;
  254. any_webview_visible_seconds =
  255. all_clients_tracker_.any_webview_tracked_duration_.InSeconds();
  256. all_clients_tracker_.any_webview_tracked_duration_ -=
  257. base::Seconds(any_webview_visible_seconds);
  258. no_webview_visible_seconds =
  259. all_clients_tracker_.no_webview_tracked_duration_.InSeconds();
  260. all_clients_tracker_.no_webview_tracked_duration_ -=
  261. base::Seconds(no_webview_visible_seconds);
  262. total_webview_visible_seconds =
  263. all_clients_tracker_.per_webview_duration_.InSeconds();
  264. all_clients_tracker_.per_webview_duration_ -=
  265. base::Seconds(total_webview_visible_seconds);
  266. total_no_webview_visible_seconds =
  267. all_clients_tracker_.per_webview_untracked_duration_.InSeconds();
  268. all_clients_tracker_.per_webview_untracked_duration_ -=
  269. base::Seconds(total_no_webview_visible_seconds);
  270. if (any_webview_visible_seconds) {
  271. GetGlobalVisibilityHistogram()->AddCount(
  272. static_cast<int>(Visibility::kVisible), any_webview_visible_seconds);
  273. }
  274. if (no_webview_visible_seconds) {
  275. GetGlobalVisibilityHistogram()->AddCount(
  276. static_cast<int>(Visibility::kNotVisible), no_webview_visible_seconds);
  277. }
  278. if (total_webview_visible_seconds) {
  279. GetPerWebViewVisibilityHistogram()->AddCount(
  280. static_cast<int>(Visibility::kVisible), total_webview_visible_seconds);
  281. }
  282. if (total_no_webview_visible_seconds) {
  283. GetPerWebViewVisibilityHistogram()->AddCount(
  284. static_cast<int>(Visibility::kNotVisible),
  285. total_no_webview_visible_seconds);
  286. }
  287. }
  288. void VisibilityMetricsLogger::RecordVisibleSchemeMetrics() {
  289. for (size_t i = 0; i < std::size(per_scheme_trackers_); i++) {
  290. Scheme scheme = static_cast<Scheme>(i);
  291. auto& tracker = per_scheme_trackers_[i];
  292. int32_t any_webview_seconds =
  293. tracker.any_webview_tracked_duration_.InSeconds();
  294. if (any_webview_seconds) {
  295. tracker.any_webview_tracked_duration_ -=
  296. base::Seconds(any_webview_seconds);
  297. LogGlobalVisibleScheme(scheme, any_webview_seconds);
  298. }
  299. int32_t per_webview_seconds = tracker.per_webview_duration_.InSeconds();
  300. if (per_webview_seconds) {
  301. tracker.per_webview_duration_ -= base::Seconds(per_webview_seconds);
  302. LogPerWebViewVisibleScheme(scheme, per_webview_seconds);
  303. }
  304. }
  305. }
  306. void VisibilityMetricsLogger::RecordScreenCoverageMetrics() {
  307. if (!base::FeatureList::IsEnabled(features::kWebViewMeasureScreenCoverage))
  308. return;
  309. for (size_t i = 0; i < std::size(global_coverage_percentage_durations_);
  310. i++) {
  311. int32_t seconds = global_coverage_percentage_durations_[i].InSeconds();
  312. if (seconds == 0)
  313. continue;
  314. global_coverage_percentage_durations_[i] -= base::Seconds(seconds);
  315. LogGlobalVisibleScreenCoverage(i, seconds);
  316. }
  317. for (auto& scheme_and_map : schemes_to_percentages_to_durations_) {
  318. for (auto& percentage_and_duration : scheme_and_map.second) {
  319. int32_t seconds = percentage_and_duration.second.InSeconds();
  320. if (seconds == 0)
  321. continue;
  322. percentage_and_duration.second -= base::Seconds(seconds);
  323. LogPerWebViewVisibleScreenCoverage(percentage_and_duration.first,
  324. seconds);
  325. LogPerSchemeVisibleScreenCoverage(scheme_and_map.first,
  326. percentage_and_duration.first, seconds);
  327. }
  328. }
  329. }
  330. } // namespace android_webview