1 // Copyright 2013 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.
5 #include "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
7 #include "base/metrics/histogram.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/after_startup_task_utils.h"
11 #include "chrome/browser/search/most_visited_iframe_source.h"
12 #include "chrome/browser/search/search.h"
13 #include "chrome/common/search_urls.h"
14 #include "chrome/common/url_constants.h"
15 #include "content/public/browser/navigation_details.h"
16 #include "content/public/browser/navigation_entry.h"
17 #include "content/public/browser/user_metrics.h"
18 #include "content/public/browser/web_contents.h"
20 // Macro to log UMA statistics related to the 8 tiles shown on the NTP.
21 #define UMA_HISTOGRAM_NTP_TILES(name, sample) \
22 UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 0, 8, 9)
26 // Used to track if suggestions were issued by the client or the server.
27 enum SuggestionsType
{
30 SUGGESTIONS_TYPE_COUNT
= 2
33 // Number of Most Visited elements on the NTP for logging purposes.
34 const int kNumMostVisited
= 8;
36 // Name of the histogram keeping track of Most Visited impressions.
37 const char kMostVisitedImpressionHistogramName
[] =
38 "NewTabPage.SuggestionsImpression";
40 // Format string to generate the name for the histogram keeping track of
41 // suggestion impressions.
42 const char kMostVisitedImpressionHistogramWithProvider
[] =
43 "NewTabPage.SuggestionsImpression.%s";
45 // Name of the histogram keeping track of Most Visited navigations.
46 const char kMostVisitedNavigationHistogramName
[] =
47 "NewTabPage.MostVisited";
49 // Format string to generate the name for the histogram keeping track of
50 // suggestion navigations.
51 const char kMostVisitedNavigationHistogramWithProvider
[] =
52 "NewTabPage.MostVisited.%s";
56 DEFINE_WEB_CONTENTS_USER_DATA_KEY(NTPUserDataLogger
);
59 // Log a time event for a given |histogram| at a given |value|. This
60 // routine exists because regular histogram macros are cached thus can't be used
61 // if the name of the histogram will change at a given call site.
62 void logLoadTimeHistogram(const std::string
& histogram
, base::TimeDelta value
) {
63 base::HistogramBase
* counter
= base::Histogram::FactoryTimeGet(
65 base::TimeDelta::FromMilliseconds(1),
66 base::TimeDelta::FromSeconds(60), 100,
67 base::Histogram::kUmaTargetedHistogramFlag
);
69 counter
->AddTime(value
);
73 NTPUserDataLogger::~NTPUserDataLogger() {}
76 NTPUserDataLogger
* NTPUserDataLogger::GetOrCreateFromWebContents(
77 content::WebContents
* content
) {
78 // Calling CreateForWebContents when an instance is already attached has no
79 // effect, so we can do this.
80 NTPUserDataLogger::CreateForWebContents(content
);
81 NTPUserDataLogger
* logger
= NTPUserDataLogger::FromWebContents(content
);
83 // We record the URL of this NTP in order to identify navigations that
84 // originate from it. We use the NavigationController's URL since it might
85 // differ from the WebContents URL which is usually chrome://newtab/.
86 const content::NavigationEntry
* entry
=
87 content
->GetController().GetVisibleEntry();
89 logger
->ntp_url_
= entry
->GetURL();
95 std::string
NTPUserDataLogger::GetMostVisitedImpressionHistogramNameForProvider(
96 const std::string
& provider
) {
97 return base::StringPrintf(kMostVisitedImpressionHistogramWithProvider
,
102 std::string
NTPUserDataLogger::GetMostVisitedNavigationHistogramNameForProvider(
103 const std::string
& provider
) {
104 return base::StringPrintf(kMostVisitedNavigationHistogramWithProvider
,
108 void NTPUserDataLogger::EmitNtpStatistics() {
109 UMA_HISTOGRAM_COUNTS("NewTabPage.NumberOfMouseOvers", number_of_mouseovers_
);
110 number_of_mouseovers_
= 0;
112 // We only send statistics once per page.
113 // And we don't send if there are no tiles recorded.
114 if (has_emitted_
|| !number_of_tiles_
)
117 // LoadTime only gets update once per page, so we don't have it on reloads.
118 if (load_time_
> base::TimeDelta::FromMilliseconds(0)) {
119 logLoadTimeHistogram("NewTabPage.LoadTime", load_time_
);
121 // Split between ML and MV.
122 std::string type
= has_server_side_suggestions_
?
123 "MostLikely" : "MostVisited";
124 logLoadTimeHistogram("NewTabPage.LoadTime." + type
, load_time_
);
125 // Split between Web and Local.
126 std::string source
= ntp_url_
.SchemeIsHTTPOrHTTPS() ? "Web" : "LocalNTP";
127 logLoadTimeHistogram("NewTabPage.LoadTime." + source
, load_time_
);
129 // Split between Startup and non-startup.
130 std::string status
= during_startup_
? "Startup" : "NewTab";
131 logLoadTimeHistogram("NewTabPage.LoadTime." + status
, load_time_
);
133 load_time_
= base::TimeDelta::FromMilliseconds(0);
135 UMA_HISTOGRAM_ENUMERATION(
136 "NewTabPage.SuggestionsType",
137 has_server_side_suggestions_
? SERVER_SIDE
: CLIENT_SIDE
,
138 SUGGESTIONS_TYPE_COUNT
);
139 has_server_side_suggestions_
= false;
140 has_client_side_suggestions_
= false;
141 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfTiles", number_of_tiles_
);
142 number_of_tiles_
= 0;
143 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfThumbnailTiles",
144 number_of_thumbnail_tiles_
);
145 number_of_thumbnail_tiles_
= 0;
146 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfGrayTiles",
147 number_of_gray_tiles_
);
148 number_of_gray_tiles_
= 0;
149 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfExternalTiles",
150 number_of_external_tiles_
);
151 number_of_external_tiles_
= 0;
152 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfThumbnailErrors",
153 number_of_thumbnail_errors_
);
154 number_of_thumbnail_errors_
= 0;
155 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfGrayTileFallbacks",
156 number_of_gray_tile_fallbacks_
);
157 number_of_gray_tile_fallbacks_
= 0;
158 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfExternalTileFallbacks",
159 number_of_external_tile_fallbacks_
);
160 number_of_external_tile_fallbacks_
= 0;
162 during_startup_
= false;
165 void NTPUserDataLogger::LogEvent(NTPLoggingEventType event
,
166 base::TimeDelta time
) {
168 // It is possible that our page gets update with a different set of
169 // suggestions if the NTP is left open enough time.
170 // In either case, we want to flush our stats before recounting again.
171 case NTP_SERVER_SIDE_SUGGESTION
:
172 if (has_client_side_suggestions_
)
174 has_server_side_suggestions_
= true;
176 case NTP_CLIENT_SIDE_SUGGESTION
:
177 if (has_server_side_suggestions_
)
179 has_client_side_suggestions_
= true;
184 case NTP_THUMBNAIL_TILE
:
185 number_of_thumbnail_tiles_
++;
188 number_of_gray_tiles_
++;
190 case NTP_EXTERNAL_TILE
:
191 number_of_external_tiles_
++;
193 case NTP_THUMBNAIL_ERROR
:
194 number_of_thumbnail_errors_
++;
196 case NTP_GRAY_TILE_FALLBACK
:
197 number_of_gray_tile_fallbacks_
++;
199 case NTP_EXTERNAL_TILE_FALLBACK
:
200 number_of_external_tile_fallbacks_
++;
203 number_of_mouseovers_
++;
205 case NTP_TILE_LOADED
:
206 // The time at which the last tile has loaded (title, thumbnail or single)
207 // is a good proxy for the total load time of the NTP, therefore we keep
208 // the max as the load time.
209 load_time_
= std::max(load_time_
, time
);
216 void NTPUserDataLogger::LogMostVisitedImpression(
217 int position
, const base::string16
& provider
) {
218 // Log the Most Visited navigation for navigations that have providers and
220 UMA_HISTOGRAM_ENUMERATION(kMostVisitedImpressionHistogramName
, position
,
223 // If a provider is specified, log the metric specific to it.
224 if (!provider
.empty()) {
225 // Cannot rely on UMA histograms macro because the name of the histogram is
226 // generated dynamically.
227 base::HistogramBase
* counter
= base::LinearHistogram::FactoryGet(
228 GetMostVisitedImpressionHistogramNameForProvider(
229 base::UTF16ToUTF8(provider
)),
233 base::Histogram::kUmaTargetedHistogramFlag
);
234 counter
->Add(position
);
238 void NTPUserDataLogger::LogMostVisitedNavigation(
239 int position
, const base::string16
& provider
) {
240 // Log the Most Visited navigation for navigations that have providers and
242 UMA_HISTOGRAM_ENUMERATION(kMostVisitedNavigationHistogramName
, position
,
245 // If a provider is specified, log the metric specific to it.
246 if (!provider
.empty()) {
247 // Cannot rely on UMA histograms macro because the name of the histogram is
248 // generated dynamically.
249 base::HistogramBase
* counter
= base::LinearHistogram::FactoryGet(
250 GetMostVisitedNavigationHistogramNameForProvider(
251 base::UTF16ToUTF8(provider
)),
255 base::Histogram::kUmaTargetedHistogramFlag
);
256 counter
->Add(position
);
259 // Records the action. This will be available as a time-stamped stream
260 // server-side and can be used to compute time-to-long-dwell.
261 content::RecordAction(base::UserMetricsAction("MostVisited_Clicked"));
264 // content::WebContentsObserver override
265 void NTPUserDataLogger::NavigationEntryCommitted(
266 const content::LoadCommittedDetails
& load_details
) {
267 if (!load_details
.previous_url
.is_valid())
270 if (search::MatchesOriginAndPath(ntp_url_
, load_details
.previous_url
)) {
275 NTPUserDataLogger::NTPUserDataLogger(content::WebContents
* contents
)
276 : content::WebContentsObserver(contents
),
277 has_server_side_suggestions_(false),
278 has_client_side_suggestions_(false),
280 number_of_thumbnail_tiles_(0),
281 number_of_gray_tiles_(0),
282 number_of_external_tiles_(0),
283 number_of_thumbnail_errors_(0),
284 number_of_gray_tile_fallbacks_(0),
285 number_of_external_tile_fallbacks_(0),
286 number_of_mouseovers_(0),
288 during_startup_(false) {
289 during_startup_
= !AfterStartupTaskUtils::IsBrowserStartupComplete();