Commit f80a66f6 authored by Esmael El-Moslimany's avatar Esmael El-Moslimany Committed by Commit Bot

WebUI NTP Modules: add UMA histograms for impression and usage

Bug: 1110069, 1130863
Change-Id: I74f43f32819ca55766dec170a017c2340d6a8226
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2437932
Commit-Queue: Esmael Elmoslimany <aee@chromium.org>
Reviewed-by: default avatarAlex Gough <ajgo@chromium.org>
Reviewed-by: default avatarTibor Goldschwendt <tiborg@chromium.org>
Reviewed-by: default avatarBrian White <bcwhite@chromium.org>
Cr-Commit-Position: refs/heads/master@{#812080}
parent 2bf79e4d
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import {html, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; import {assert} from 'chrome://resources/js/assert.m.js';
import {html, microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {BrowserProxy} from '../browser_proxy.js';
import {ModuleDescriptor} from './module_descriptor.js'; import {ModuleDescriptor} from './module_descriptor.js';
/** @fileoverview Element that implements the common module UI. */ /** @fileoverview Element that implements the common module UI. */
...@@ -26,11 +29,34 @@ class ModuleWrapperElement extends PolymerElement { ...@@ -26,11 +29,34 @@ class ModuleWrapperElement extends PolymerElement {
}; };
} }
/** @private */ /**
onDescriptorChange_() { * @param {ModuleDescriptor} newValue
this.$.moduleElement.innerHTML = ''; * @param {ModuleDescriptor} oldValue
* @private
*/
onDescriptorChange_(newValue, oldValue) {
assert(!oldValue);
this.$.moduleElement.appendChild(this.descriptor.element); this.$.moduleElement.appendChild(this.descriptor.element);
this.$.moduleElement.style.height = `${this.descriptor.heightPx}px`; this.$.moduleElement.style.height = `${this.descriptor.heightPx}px`;
const observer = new IntersectionObserver(([{intersectionRatio}]) => {
if (intersectionRatio >= .5) {
observer.disconnect();
BrowserProxy.getInstance().handler.onModuleImpression(
this.descriptor.id, BrowserProxy.getInstance().now());
}
}, {threshold: .5});
// Calling observe will immediately invoke the callback. If the header is
// fully shown when the page loads, the first callback invocation will
// happen before the header has dimensions. For this reason, we start
// observing after the element has had a chance to be rendered.
microTask.run(() => {
observer.observe(this.$.header);
});
// Log at most one usage per module per NTP page load. This is possible,
// if a user opens a link in a new tab.
this.descriptor.element.addEventListener('usage', () => {
BrowserProxy.getInstance().handler.onModuleUsage(this.descriptor.id);
}, {once: true});
} }
} }
......
...@@ -121,7 +121,8 @@ ...@@ -121,7 +121,8 @@
<div id="products"> <div id="products">
<template is="dom-repeat" id="productsRepeat" <template is="dom-repeat" id="productsRepeat"
items="[[shoppingTask.products]]"> items="[[shoppingTask.products]]">
<a class="product" href="[[item.targetUrl.url]]"> <a class="product" href="[[item.targetUrl.url]]" on-click="onClick_"
on-auxclick="onClick_">
<div class="image"> <div class="image">
<img is="ntp-img" auto-src="[[item.imageUrl.url]]"></img> <img is="ntp-img" auto-src="[[item.imageUrl.url]]"></img>
</div> </div>
...@@ -134,7 +135,8 @@ ...@@ -134,7 +135,8 @@
<div id="relatedSearches"> <div id="relatedSearches">
<template is="dom-repeat" id="relatedSearchesRepeat" <template is="dom-repeat" id="relatedSearchesRepeat"
items="[[shoppingTask.relatedSearches]]"> items="[[shoppingTask.relatedSearches]]">
<a class="pill" href="[[item.targetUrl.url]]"> <a class="pill" href="[[item.targetUrl.url]]" on-click="onClick_"
on-auxclick="onClick_">
<div class="loupe"></div> <div class="loupe"></div>
<div class="search-text">[[item.text]]</div> <div class="search-text">[[item.text]]</div>
</a> </a>
......
...@@ -29,6 +29,11 @@ class ShoppingTasksModuleElement extends PolymerElement { ...@@ -29,6 +29,11 @@ class ShoppingTasksModuleElement extends PolymerElement {
shoppingTask: Object, shoppingTask: Object,
}; };
} }
/** @private */
onClick_() {
this.dispatchEvent(new Event('usage', {bubbles: true, composed: true}));
}
} }
customElements.define( customElements.define(
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h" #include "base/metrics/user_metrics.h"
#include "chrome/browser/after_startup_task_utils.h" #include "chrome/browser/after_startup_task_utils.h"
...@@ -592,6 +593,19 @@ void NTPUserDataLogger::LogMostVisitedNavigation( ...@@ -592,6 +593,19 @@ void NTPUserDataLogger::LogMostVisitedNavigation(
base::RecordAction(base::UserMetricsAction("MostVisited_Clicked")); base::RecordAction(base::UserMetricsAction("MostVisited_Clicked"));
} }
void NTPUserDataLogger::LogModuleImpression(const std::string& id,
base::TimeDelta time) {
UMA_HISTOGRAM_LOAD_TIME("NewTabPage.Modules.Impression", time);
base::UmaHistogramCustomTimes("NewTabPage.Modules.Impression." + id, time,
base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromSeconds(60), 100);
}
void NTPUserDataLogger::LogModuleUsage(const std::string& id) {
UMA_HISTOGRAM_EXACT_LINEAR("NewTabPage.Modules.Usage", 1, 1);
base::UmaHistogramExactLinear("NewTabPage.Modules.Usage." + id, 1, 1);
}
NTPUserDataLogger::NTPUserDataLogger(content::WebContents* contents) NTPUserDataLogger::NTPUserDataLogger(content::WebContents* contents)
: content::WebContentsObserver(contents), : content::WebContentsObserver(contents),
has_emitted_(false), has_emitted_(false),
......
...@@ -53,6 +53,13 @@ class NTPUserDataLogger ...@@ -53,6 +53,13 @@ class NTPUserDataLogger
// all others require Google as the default search provider. // all others require Google as the default search provider.
void LogEvent(NTPLoggingEventType event, base::TimeDelta time); void LogEvent(NTPLoggingEventType event, base::TimeDelta time);
// Logs a module impression. Called when a module is loaded and can be seen by
// the user (scrolled into view).
void LogModuleImpression(const std::string& id, base::TimeDelta time);
// Logs when a user interacts with a module which will result in a navigation.
void LogModuleUsage(const std::string& id);
// Called when a search suggestion event occurs on the NTP that has an integer // Called when a search suggestion event occurs on the NTP that has an integer
// value associated with it; N suggestions were shown on this NTP load, the // value associated with it; N suggestions were shown on this NTP load, the
// Nth suggestion was clicked, etc. |time| is the delta time from navigation // Nth suggestion was clicked, etc. |time| is the delta time from navigation
......
...@@ -377,6 +377,11 @@ interface PageHandler { ...@@ -377,6 +377,11 @@ interface PageHandler {
OnVoiceSearchAction(VoiceSearchAction action); OnVoiceSearchAction(VoiceSearchAction action);
// Logs an error occurred while using voice search. // Logs an error occurred while using voice search.
OnVoiceSearchError(VoiceSearchError error); OnVoiceSearchError(VoiceSearchError error);
// Logs a module impression. Called when a module is loaded and can be seen by
// the user (scrolled into view).
OnModuleImpression(string module_id, double time);
// Logs when a user interacts with a module which will result in a navigation.
OnModuleUsage(string module_id);
// Logs that the modules have been shown at |time|. // Logs that the modules have been shown at |time|.
OnModulesRendered(double time); OnModulesRendered(double time);
......
...@@ -922,6 +922,16 @@ void NewTabPageHandler::OnVoiceSearchError( ...@@ -922,6 +922,16 @@ void NewTabPageHandler::OnVoiceSearchError(
LogEvent(event); LogEvent(event);
} }
void NewTabPageHandler::OnModuleImpression(const std::string& module_id,
double time) {
logger_->LogModuleImpression(
module_id, base::Time::FromJsTime(time) - ntp_navigation_start_time_);
}
void NewTabPageHandler::OnModuleUsage(const std::string& module_id) {
logger_->LogModuleUsage(module_id);
}
void NewTabPageHandler::OnModulesRendered(double time) { void NewTabPageHandler::OnModulesRendered(double time) {
logger_->LogEvent(NTP_MODULES_SHOWN, logger_->LogEvent(NTP_MODULES_SHOWN,
base::Time::FromJsTime(time) - ntp_navigation_start_time_); base::Time::FromJsTime(time) - ntp_navigation_start_time_);
......
...@@ -122,6 +122,8 @@ class NewTabPageHandler : public new_tab_page::mojom::PageHandler, ...@@ -122,6 +122,8 @@ class NewTabPageHandler : public new_tab_page::mojom::PageHandler,
void OnVoiceSearchAction( void OnVoiceSearchAction(
new_tab_page::mojom::VoiceSearchAction action) override; new_tab_page::mojom::VoiceSearchAction action) override;
void OnVoiceSearchError(new_tab_page::mojom::VoiceSearchError error) override; void OnVoiceSearchError(new_tab_page::mojom::VoiceSearchError error) override;
void OnModuleImpression(const std::string& module_id, double time) override;
void OnModuleUsage(const std::string& module_id) override;
void OnModulesRendered(double time) override; void OnModulesRendered(double time) override;
void QueryAutocomplete(const base::string16& input, void QueryAutocomplete(const base::string16& input,
bool prevent_inline_autocomplete) override; bool prevent_inline_autocomplete) override;
......
...@@ -32,4 +32,22 @@ suite('NewTabPageModulesModuleWrapperTest', () => { ...@@ -32,4 +32,22 @@ suite('NewTabPageModulesModuleWrapperTest', () => {
assertDeepEquals( assertDeepEquals(
moduleElement, $$(moduleWrapper, '#moduleElement').children[0]); moduleElement, $$(moduleWrapper, '#moduleElement').children[0]);
}); });
test('descriptor can only be set once', () => {
const moduleElement = document.createElement('div');
moduleWrapper.descriptor = {
id: 'foo',
heightPx: 100,
title: 'Foo Title',
element: moduleElement,
};
assertThrows(() => {
moduleWrapper.descriptor = {
id: 'foo',
heightPx: 100,
title: 'Foo Title',
element: moduleElement,
};
});
});
}); });
...@@ -11118,6 +11118,15 @@ reviews. Googlers can read more about this at go/gwsq-gerrit. ...@@ -11118,6 +11118,15 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
<affected-histogram name="NewTabPage.LogoShown"/> <affected-histogram name="NewTabPage.LogoShown"/>
</histogram_suffixes> </histogram_suffixes>
<histogram_suffixes name="NewTabPageModules" separator=".">
<suffix name="dummy" label="Module ID for a dummy module"/>
<suffix name="dummy2" label="Module ID for another dummy module"/>
<suffix name="kaleidoscope" label="Module ID for Kaleidoscope"/>
<suffix name="shopping_tasks" label="Module ID for Shopping Tasks"/>
<affected-histogram name="NewTabPage.Modules.Impression"/>
<affected-histogram name="NewTabPage.Modules.Usage"/>
</histogram_suffixes>
<histogram_suffixes name="NewTabPageProviders" separator="."> <histogram_suffixes name="NewTabPageProviders" separator=".">
<suffix name="client" label="Suggestions coming from the client."/> <suffix name="client" label="Suggestions coming from the client."/>
<suffix name="client0" label=""> <suffix name="client0" label="">
......
...@@ -747,6 +747,20 @@ reviews. Googlers can read more about this at go/gwsq-gerrit. ...@@ -747,6 +747,20 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
</summary> </summary>
</histogram> </histogram>
<histogram name="NewTabPage.Modules.Impression" units="ms"
expires_after="2021-01-01">
<owner>tiborg@chromium.org</owner>
<owner>yyushkina@chromium.org</owner>
<owner>chrome-desktop-ntp@google.com</owner>
<summary>
Histogram of the time, in milliseconds since navigation start, it took until
an NTP module header is mostly visible in the window's content area
(viewport). If the module header is mostly below the page fold, the time
delta will include the time it takes for the user to scroll the module into
view.
</summary>
</histogram>
<histogram name="NewTabPage.Modules.ShownTime" units="ms" <histogram name="NewTabPage.Modules.ShownTime" units="ms"
expires_after="2021-01-01"> expires_after="2021-01-01">
<owner>tiborg@chromium.org</owner> <owner>tiborg@chromium.org</owner>
...@@ -758,6 +772,18 @@ reviews. Googlers can read more about this at go/gwsq-gerrit. ...@@ -758,6 +772,18 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
</summary> </summary>
</histogram> </histogram>
<histogram name="NewTabPage.Modules.Usage" units="count"
expires_after="2021-01-01">
<owner>tiborg@chromium.org</owner>
<owner>yyushkina@chromium.org</owner>
<owner>chrome-desktop-ntp@google.com</owner>
<summary>
Emitted each time a user action within an NTP module launches a module's
feature. Every usage emits a 1 which has no inherent meaning aside from a
usage occurred.
</summary>
</histogram>
<histogram name="NewTabPage.MostVisited" enum="MostVisitedTileIndex" <histogram name="NewTabPage.MostVisited" enum="MostVisitedTileIndex"
expires_after="never"> expires_after="never">
<!-- expires-never: part of top-line metric (internal: go/chrome-browser-nsm) --> <!-- expires-never: part of top-line metric (internal: go/chrome-browser-nsm) -->
......
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