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 @@
#include "chrome/common/chrome_features.h"
#include "content/public/browser/browser_context.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_observer.h"
#include "content/public/browser/web_contents_user_data.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);
......@@ -37,7 +40,8 @@ constexpr char kPerSourceLogTimeoutMsecParamName[] =
// per-WebContents data that TabActivityWatcher uses to log the tab.
class TabActivityWatcher::WebContentsData
: public content::WebContentsObserver,
public content::WebContentsUserData<WebContentsData> {
public content::WebContentsUserData<WebContentsData>,
public content::RenderWidgetHost::InputEventObserver {
public:
~WebContentsData() override = default;
......@@ -48,6 +52,10 @@ class TabActivityWatcher::WebContentsData
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 {
return last_log_time_for_source_;
}
......@@ -56,9 +64,18 @@ class TabActivityWatcher::WebContentsData
friend class content::WebContentsUserData<WebContentsData>;
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:
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(
content::NavigationHandle* navigation_handle) override {
if (!navigation_handle->HasCommitted() ||
......@@ -77,17 +94,33 @@ class TabActivityWatcher::WebContentsData
// Clear the per-SourceId last log time.
last_log_time_for_source_ = base::TimeTicks();
// Reset the per-page data.
tab_metrics_.page_metrics = {};
}
void WasHidden() override {
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.
ukm::SourceId ukm_source_id_ = 0;
// Used to throttle event logging per SourceId.
base::TimeTicks last_log_time_for_source_;
// Stores current stats for the tab.
TabMetricsLogger::TabMetrics tab_metrics_;
DISALLOW_COPY_AND_ASSIGN(WebContentsData);
};
......@@ -146,7 +179,8 @@ void TabActivityWatcher::MaybeLogTab(content::WebContents* web_contents) {
}
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);
}
......
......@@ -16,12 +16,32 @@ class WebContents;
// Must be used on the UI thread.
class TabMetricsLogger {
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;
// Logs metrics for the tab with the given main frame WebContents. Does
// nothing if |ukm_source_id| is zero.
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_
......@@ -64,9 +64,8 @@ int GetSiteEngagementScore(const content::WebContents* web_contents) {
TabMetricsLoggerImpl::TabMetricsLoggerImpl() = default;
TabMetricsLoggerImpl::~TabMetricsLoggerImpl() = default;
void TabMetricsLoggerImpl::LogBackgroundTab(
ukm::SourceId ukm_source_id,
content::WebContents* web_contents) {
void TabMetricsLoggerImpl::LogBackgroundTab(ukm::SourceId ukm_source_id,
const TabMetrics& tab_metrics) {
if (!ukm_source_id)
return;
......@@ -74,6 +73,7 @@ void TabMetricsLoggerImpl::LogBackgroundTab(
if (!ukm_recorder)
return;
content::WebContents* web_contents = tab_metrics.web_contents;
Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
if (!browser)
return;
......@@ -87,6 +87,9 @@ void TabMetricsLoggerImpl::LogBackgroundTab(
DCHECK_NE(index, TabStripModel::kNoTab);
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())
entry.SetSiteEngagementScore(GetSiteEngagementScore(web_contents));
......
......@@ -18,7 +18,7 @@ class TabMetricsLoggerImpl : public TabMetricsLogger {
// TabMetricsLogger:
void LogBackgroundTab(ukm::SourceId ukm_source_id,
content::WebContents* web_contents) override;
const TabMetrics& tab_metrics) override;
private:
DISALLOW_COPY_AND_ASSIGN(TabMetricsLoggerImpl);
......
......@@ -2072,12 +2072,27 @@ be describing additional metrics about the same event.
Boolean value indicating whether the tab is pinned in the tabstrip.
</summary>
</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">
<summary>
Site engagement score in the range [0, 100], rounded down to a multiple of
10 to limit granularity.
</summary>
</metric>
<metric name="TouchEventCount">
<summary>
Number of touch events that were sent to the page.
</summary>
</metric>
</event>
</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