Commit 480aed6a authored by Michael Giuffrida's avatar Michael Giuffrida Committed by Commit Bot

Add input event counts to tab activity UKMs

This adds counters for key, mouse, and touch events to
TabActivityWatcher::WebContentsData. When a tab is logged, these
counters are reported in the UKM. The counters are reset on each
navigation.

These counters are passed to TabSnapshotLogger in a new hierarchical
structure, which will be built upon to add browser-, tab- and page-
specific metrics. Separating these event counts into a separate struct
makes it easier to reset them when a navigation occurs.

This CL counts the absolute number of events -- as an improvement, we
would prefer to count user actions rather than, say, the exact
number of mouse-move events fired.

A unit test simulates mouse activity, but key and touch are harder to
pipe in from a unit test.

Bug: 784639
Change-Id: I693c34f0845c3fdeca6c1fb172e7347f9c2a504f
Reviewed-on: https://chromium-review.googlesource.com/794592
Commit-Queue: Michael Giuffrida <michaelpg@chromium.org>
Reviewed-by: default avatarSteven Holte <holte@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#520674}
parent 9d24ee15
...@@ -13,10 +13,13 @@ ...@@ -13,10 +13,13 @@
#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_features.h"
#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_context.h"
#include "content/public/browser/navigation_handle.h" #include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h" #include "content/public/browser/web_contents_user_data.h"
#include "services/metrics/public/cpp/ukm_source_id.h" #include "services/metrics/public/cpp/ukm_source_id.h"
#include "third_party/WebKit/public/platform/WebInputEvent.h"
DEFINE_WEB_CONTENTS_USER_DATA_KEY(TabActivityWatcher::WebContentsData); DEFINE_WEB_CONTENTS_USER_DATA_KEY(TabActivityWatcher::WebContentsData);
...@@ -37,7 +40,8 @@ constexpr char kPerSourceLogTimeoutMsecParamName[] = ...@@ -37,7 +40,8 @@ constexpr char kPerSourceLogTimeoutMsecParamName[] =
// per-WebContents data that TabActivityWatcher uses to log the tab. // per-WebContents data that TabActivityWatcher uses to log the tab.
class TabActivityWatcher::WebContentsData class TabActivityWatcher::WebContentsData
: public content::WebContentsObserver, : public content::WebContentsObserver,
public content::WebContentsUserData<WebContentsData> { public content::WebContentsUserData<WebContentsData>,
public content::RenderWidgetHost::InputEventObserver {
public: public:
~WebContentsData() override = default; ~WebContentsData() override = default;
...@@ -48,6 +52,10 @@ class TabActivityWatcher::WebContentsData ...@@ -48,6 +52,10 @@ class TabActivityWatcher::WebContentsData
ukm::SourceId ukm_source_id() const { return ukm_source_id_; } ukm::SourceId ukm_source_id() const { return ukm_source_id_; }
const TabMetricsLogger::TabMetrics& tab_metrics() const {
return tab_metrics_;
}
base::TimeTicks last_log_time_for_source() const { base::TimeTicks last_log_time_for_source() const {
return last_log_time_for_source_; return last_log_time_for_source_;
} }
...@@ -56,9 +64,18 @@ class TabActivityWatcher::WebContentsData ...@@ -56,9 +64,18 @@ class TabActivityWatcher::WebContentsData
friend class content::WebContentsUserData<WebContentsData>; friend class content::WebContentsUserData<WebContentsData>;
explicit WebContentsData(content::WebContents* web_contents) explicit WebContentsData(content::WebContents* web_contents)
: WebContentsObserver(web_contents) {} : WebContentsObserver(web_contents) {
tab_metrics_.web_contents = web_contents;
web_contents->GetRenderViewHost()->GetWidget()->AddInputEventObserver(this);
}
// content::WebContentsObserver: // content::WebContentsObserver:
void RenderViewHostChanged(content::RenderViewHost* old_host,
content::RenderViewHost* new_host) override {
if (old_host != nullptr)
old_host->GetWidget()->RemoveInputEventObserver(this);
new_host->GetWidget()->AddInputEventObserver(this);
}
void DidFinishNavigation( void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override { content::NavigationHandle* navigation_handle) override {
if (!navigation_handle->HasCommitted() || if (!navigation_handle->HasCommitted() ||
...@@ -77,17 +94,33 @@ class TabActivityWatcher::WebContentsData ...@@ -77,17 +94,33 @@ class TabActivityWatcher::WebContentsData
// Clear the per-SourceId last log time. // Clear the per-SourceId last log time.
last_log_time_for_source_ = base::TimeTicks(); last_log_time_for_source_ = base::TimeTicks();
// Reset the per-page data.
tab_metrics_.page_metrics = {};
} }
void WasHidden() override { void WasHidden() override {
TabActivityWatcher::GetInstance()->OnWasHidden(web_contents()); TabActivityWatcher::GetInstance()->OnWasHidden(web_contents());
} }
// content::RenderWidgetHost::InputEventObserver:
void OnInputEvent(const blink::WebInputEvent& event) override {
if (blink::WebInputEvent::IsMouseEventType(event.GetType()))
tab_metrics_.page_metrics.mouse_event_count++;
else if (blink::WebInputEvent::IsKeyboardEventType(event.GetType()))
tab_metrics_.page_metrics.key_event_count++;
else if (blink::WebInputEvent::IsTouchEventType(event.GetType()))
tab_metrics_.page_metrics.touch_event_count++;
}
// Updated when a navigation is finished. // Updated when a navigation is finished.
ukm::SourceId ukm_source_id_ = 0; ukm::SourceId ukm_source_id_ = 0;
// Used to throttle event logging per SourceId. // Used to throttle event logging per SourceId.
base::TimeTicks last_log_time_for_source_; base::TimeTicks last_log_time_for_source_;
// Stores current stats for the tab.
TabMetricsLogger::TabMetrics tab_metrics_;
DISALLOW_COPY_AND_ASSIGN(WebContentsData); DISALLOW_COPY_AND_ASSIGN(WebContentsData);
}; };
...@@ -146,7 +179,8 @@ void TabActivityWatcher::MaybeLogTab(content::WebContents* web_contents) { ...@@ -146,7 +179,8 @@ void TabActivityWatcher::MaybeLogTab(content::WebContents* web_contents) {
} }
ukm::SourceId ukm_source_id = web_contents_data->ukm_source_id(); ukm::SourceId ukm_source_id = web_contents_data->ukm_source_id();
tab_metrics_logger_->LogBackgroundTab(ukm_source_id, web_contents); tab_metrics_logger_->LogBackgroundTab(ukm_source_id,
web_contents_data->tab_metrics());
web_contents_data->DidLog(now); web_contents_data->DidLog(now);
} }
......
...@@ -16,12 +16,32 @@ class WebContents; ...@@ -16,12 +16,32 @@ class WebContents;
// Must be used on the UI thread. // Must be used on the UI thread.
class TabMetricsLogger { class TabMetricsLogger {
public: public:
// The state of the page loaded in a tab's main frame, starting since the last
// navigation.
struct PageMetrics {
// Number of key events.
int key_event_count = 0;
// Number of mouse events.
int mouse_event_count = 0;
// Number of touch events.
int touch_event_count = 0;
};
// The state of a tab.
struct TabMetrics {
content::WebContents* web_contents = nullptr;
// Per-page metrics of the state of the WebContents. Tracked since the
// tab's last top-level navigation.
PageMetrics page_metrics = {};
};
virtual ~TabMetricsLogger() = default; virtual ~TabMetricsLogger() = default;
// Logs metrics for the tab with the given main frame WebContents. Does // Logs metrics for the tab with the given main frame WebContents. Does
// nothing if |ukm_source_id| is zero. // nothing if |ukm_source_id| is zero.
virtual void LogBackgroundTab(ukm::SourceId ukm_source_id, virtual void LogBackgroundTab(ukm::SourceId ukm_source_id,
content::WebContents* web_contents) = 0; const TabMetrics& tab_metrics) = 0;
}; };
#endif // CHROME_BROWSER_UI_TABS_TAB_METRICS_LOGGER_H_ #endif // CHROME_BROWSER_UI_TABS_TAB_METRICS_LOGGER_H_
...@@ -64,9 +64,8 @@ int GetSiteEngagementScore(const content::WebContents* web_contents) { ...@@ -64,9 +64,8 @@ int GetSiteEngagementScore(const content::WebContents* web_contents) {
TabMetricsLoggerImpl::TabMetricsLoggerImpl() = default; TabMetricsLoggerImpl::TabMetricsLoggerImpl() = default;
TabMetricsLoggerImpl::~TabMetricsLoggerImpl() = default; TabMetricsLoggerImpl::~TabMetricsLoggerImpl() = default;
void TabMetricsLoggerImpl::LogBackgroundTab( void TabMetricsLoggerImpl::LogBackgroundTab(ukm::SourceId ukm_source_id,
ukm::SourceId ukm_source_id, const TabMetrics& tab_metrics) {
content::WebContents* web_contents) {
if (!ukm_source_id) if (!ukm_source_id)
return; return;
...@@ -74,6 +73,7 @@ void TabMetricsLoggerImpl::LogBackgroundTab( ...@@ -74,6 +73,7 @@ void TabMetricsLoggerImpl::LogBackgroundTab(
if (!ukm_recorder) if (!ukm_recorder)
return; return;
content::WebContents* web_contents = tab_metrics.web_contents;
Browser* browser = chrome::FindBrowserWithWebContents(web_contents); Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
if (!browser) if (!browser)
return; return;
...@@ -87,6 +87,9 @@ void TabMetricsLoggerImpl::LogBackgroundTab( ...@@ -87,6 +87,9 @@ void TabMetricsLoggerImpl::LogBackgroundTab(
DCHECK_NE(index, TabStripModel::kNoTab); DCHECK_NE(index, TabStripModel::kNoTab);
ukm::builders::TabManager_TabMetrics entry(ukm_source_id); ukm::builders::TabManager_TabMetrics entry(ukm_source_id);
entry.SetKeyEventCount(tab_metrics.page_metrics.key_event_count)
.SetMouseEventCount(tab_metrics.page_metrics.mouse_event_count)
.SetTouchEventCount(tab_metrics.page_metrics.touch_event_count);
if (SiteEngagementService::IsEnabled()) if (SiteEngagementService::IsEnabled())
entry.SetSiteEngagementScore(GetSiteEngagementScore(web_contents)); entry.SetSiteEngagementScore(GetSiteEngagementScore(web_contents));
......
...@@ -18,7 +18,7 @@ class TabMetricsLoggerImpl : public TabMetricsLogger { ...@@ -18,7 +18,7 @@ class TabMetricsLoggerImpl : public TabMetricsLogger {
// TabMetricsLogger: // TabMetricsLogger:
void LogBackgroundTab(ukm::SourceId ukm_source_id, void LogBackgroundTab(ukm::SourceId ukm_source_id,
content::WebContents* web_contents) override; const TabMetrics& tab_metrics) override;
private: private:
DISALLOW_COPY_AND_ASSIGN(TabMetricsLoggerImpl); DISALLOW_COPY_AND_ASSIGN(TabMetricsLoggerImpl);
......
...@@ -2072,12 +2072,27 @@ be describing additional metrics about the same event. ...@@ -2072,12 +2072,27 @@ be describing additional metrics about the same event.
Boolean value indicating whether the tab is pinned in the tabstrip. Boolean value indicating whether the tab is pinned in the tabstrip.
</summary> </summary>
</metric> </metric>
<metric name="KeyEventCount">
<summary>
Number of key events that were sent to the page.
</summary>
</metric>
<metric name="MouseEventCount">
<summary>
Number of mouse events that were sent to the page.
</summary>
</metric>
<metric name="SiteEngagementScore"> <metric name="SiteEngagementScore">
<summary> <summary>
Site engagement score in the range [0, 100], rounded down to a multiple of Site engagement score in the range [0, 100], rounded down to a multiple of
10 to limit granularity. 10 to limit granularity.
</summary> </summary>
</metric> </metric>
<metric name="TouchEventCount">
<summary>
Number of touch events that were sent to the page.
</summary>
</metric>
</event> </event>
</ukm-configuration> </ukm-configuration>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment