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.
4
5 #include "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
6
7 #include "base/metrics/histogram.h"
8 #include "base/strings/stringprintf.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/search/most_visited_iframe_source.h"
11 #include "chrome/browser/search/search.h"
12 #include "chrome/common/search_urls.h"
13 #include "chrome/common/url_constants.h"
14 #include "content/public/browser/navigation_details.h"
15 #include "content/public/browser/navigation_entry.h"
16 #include "content/public/browser/user_metrics.h"
17 #include "content/public/browser/web_contents.h"
18
19 // Macro to log UMA statistics related to the 8 tiles shown on the NTP.
20 #define UMA_HISTOGRAM_NTP_TILES(name, sample) \
21 UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 0, 8, 9)
22
23 namespace {
24
25 // Used to track if suggestions were issued by the client or the server.
26 enum SuggestionsType {
27 CLIENT_SIDE = 0,
28 SERVER_SIDE = 1,
29 SUGGESTIONS_TYPE_COUNT = 2
30 };
31
32 // Number of Most Visited elements on the NTP for logging purposes.
33 const int kNumMostVisited = 8;
34
35 // Name of the histogram keeping track of Most Visited impressions.
36 const char kMostVisitedImpressionHistogramName[] =
37 "NewTabPage.SuggestionsImpression";
38
39 // Format string to generate the name for the histogram keeping track of
40 // suggestion impressions.
41 const char kMostVisitedImpressionHistogramWithProvider[] =
42 "NewTabPage.SuggestionsImpression.%s";
43
44 // Name of the histogram keeping track of Most Visited navigations.
45 const char kMostVisitedNavigationHistogramName[] =
46 "NewTabPage.MostVisited";
47
48 // Format string to generate the name for the histogram keeping track of
49 // suggestion navigations.
50 const char kMostVisitedNavigationHistogramWithProvider[] =
51 "NewTabPage.MostVisited.%s";
52
53 } // namespace
54
55 DEFINE_WEB_CONTENTS_USER_DATA_KEY(NTPUserDataLogger);
56
~NTPUserDataLogger()57 NTPUserDataLogger::~NTPUserDataLogger() {}
58
59 // static
GetOrCreateFromWebContents(content::WebContents * content)60 NTPUserDataLogger* NTPUserDataLogger::GetOrCreateFromWebContents(
61 content::WebContents* content) {
62 // Calling CreateForWebContents when an instance is already attached has no
63 // effect, so we can do this.
64 NTPUserDataLogger::CreateForWebContents(content);
65 NTPUserDataLogger* logger = NTPUserDataLogger::FromWebContents(content);
66
67 // We record the URL of this NTP in order to identify navigations that
68 // originate from it. We use the NavigationController's URL since it might
69 // differ from the WebContents URL which is usually chrome://newtab/.
70 const content::NavigationEntry* entry =
71 content->GetController().GetVisibleEntry();
72 if (entry)
73 logger->ntp_url_ = entry->GetURL();
74
75 return logger;
76 }
77
78 // static
GetMostVisitedImpressionHistogramNameForProvider(const std::string & provider)79 std::string NTPUserDataLogger::GetMostVisitedImpressionHistogramNameForProvider(
80 const std::string& provider) {
81 return base::StringPrintf(kMostVisitedImpressionHistogramWithProvider,
82 provider.c_str());
83 }
84
85 // static
GetMostVisitedNavigationHistogramNameForProvider(const std::string & provider)86 std::string NTPUserDataLogger::GetMostVisitedNavigationHistogramNameForProvider(
87 const std::string& provider) {
88 return base::StringPrintf(kMostVisitedNavigationHistogramWithProvider,
89 provider.c_str());
90 }
91
EmitNtpStatistics()92 void NTPUserDataLogger::EmitNtpStatistics() {
93 UMA_HISTOGRAM_COUNTS("NewTabPage.NumberOfMouseOvers", number_of_mouseovers_);
94 number_of_mouseovers_ = 0;
95
96 // Only log the following statistics if at least one tile is recorded. This
97 // check is required because the statistics are emitted whenever the user
98 // changes tab away from the NTP. However, if the user comes back to that NTP
99 // later the statistics are not regenerated (i.e. they are all 0). If we log
100 // them again we get a strong bias.
101 if (number_of_tiles_ > 0) {
102 UMA_HISTOGRAM_ENUMERATION(
103 "NewTabPage.SuggestionsType",
104 has_server_side_suggestions_ ? SERVER_SIDE : CLIENT_SIDE,
105 SUGGESTIONS_TYPE_COUNT);
106 has_server_side_suggestions_ = false;
107 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfTiles", number_of_tiles_);
108 number_of_tiles_ = 0;
109 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfThumbnailTiles",
110 number_of_thumbnail_tiles_);
111 number_of_thumbnail_tiles_ = 0;
112 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfGrayTiles",
113 number_of_gray_tiles_);
114 number_of_gray_tiles_ = 0;
115 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfExternalTiles",
116 number_of_external_tiles_);
117 number_of_external_tiles_ = 0;
118 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfThumbnailErrors",
119 number_of_thumbnail_errors_);
120 number_of_thumbnail_errors_ = 0;
121 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfGrayTileFallbacks",
122 number_of_gray_tile_fallbacks_);
123 number_of_gray_tile_fallbacks_ = 0;
124 UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfExternalTileFallbacks",
125 number_of_external_tile_fallbacks_);
126 number_of_external_tile_fallbacks_ = 0;
127 }
128 }
129
LogEvent(NTPLoggingEventType event)130 void NTPUserDataLogger::LogEvent(NTPLoggingEventType event) {
131 switch (event) {
132 case NTP_SERVER_SIDE_SUGGESTION:
133 has_server_side_suggestions_ = true;
134 break;
135 case NTP_CLIENT_SIDE_SUGGESTION:
136 // We should never get a mix of server and client side suggestions,
137 // otherwise there could be a race condition depending on the order in
138 // which the iframes call this method.
139 DCHECK(!has_server_side_suggestions_);
140 break;
141 case NTP_TILE:
142 number_of_tiles_++;
143 break;
144 case NTP_THUMBNAIL_TILE:
145 number_of_thumbnail_tiles_++;
146 break;
147 case NTP_GRAY_TILE:
148 number_of_gray_tiles_++;
149 break;
150 case NTP_EXTERNAL_TILE:
151 number_of_external_tiles_++;
152 break;
153 case NTP_THUMBNAIL_ERROR:
154 number_of_thumbnail_errors_++;
155 break;
156 case NTP_GRAY_TILE_FALLBACK:
157 number_of_gray_tile_fallbacks_++;
158 break;
159 case NTP_EXTERNAL_TILE_FALLBACK:
160 number_of_external_tile_fallbacks_++;
161 break;
162 case NTP_MOUSEOVER:
163 number_of_mouseovers_++;
164 break;
165 default:
166 NOTREACHED();
167 }
168 }
169
LogMostVisitedImpression(int position,const base::string16 & provider)170 void NTPUserDataLogger::LogMostVisitedImpression(
171 int position, const base::string16& provider) {
172 // Log the Most Visited navigation for navigations that have providers and
173 // those that dont.
174 UMA_HISTOGRAM_ENUMERATION(kMostVisitedImpressionHistogramName, position,
175 kNumMostVisited);
176
177 // If a provider is specified, log the metric specific to it.
178 if (!provider.empty()) {
179 // Cannot rely on UMA histograms macro because the name of the histogram is
180 // generated dynamically.
181 base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
182 GetMostVisitedImpressionHistogramNameForProvider(
183 base::UTF16ToUTF8(provider)),
184 1,
185 kNumMostVisited,
186 kNumMostVisited + 1,
187 base::Histogram::kUmaTargetedHistogramFlag);
188 counter->Add(position);
189 }
190 }
191
LogMostVisitedNavigation(int position,const base::string16 & provider)192 void NTPUserDataLogger::LogMostVisitedNavigation(
193 int position, const base::string16& provider) {
194 // Log the Most Visited navigation for navigations that have providers and
195 // those that dont.
196 UMA_HISTOGRAM_ENUMERATION(kMostVisitedNavigationHistogramName, position,
197 kNumMostVisited);
198
199 // If a provider is specified, log the metric specific to it.
200 if (!provider.empty()) {
201 // Cannot rely on UMA histograms macro because the name of the histogram is
202 // generated dynamically.
203 base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
204 GetMostVisitedNavigationHistogramNameForProvider(
205 base::UTF16ToUTF8(provider)),
206 1,
207 kNumMostVisited,
208 kNumMostVisited + 1,
209 base::Histogram::kUmaTargetedHistogramFlag);
210 counter->Add(position);
211 }
212
213 // Records the action. This will be available as a time-stamped stream
214 // server-side and can be used to compute time-to-long-dwell.
215 content::RecordAction(base::UserMetricsAction("MostVisited_Clicked"));
216 }
217
218 // content::WebContentsObserver override
NavigationEntryCommitted(const content::LoadCommittedDetails & load_details)219 void NTPUserDataLogger::NavigationEntryCommitted(
220 const content::LoadCommittedDetails& load_details) {
221 if (!load_details.previous_url.is_valid())
222 return;
223
224 if (search::MatchesOriginAndPath(ntp_url_, load_details.previous_url)) {
225 EmitNtpStatistics();
226 }
227 }
228
NTPUserDataLogger(content::WebContents * contents)229 NTPUserDataLogger::NTPUserDataLogger(content::WebContents* contents)
230 : content::WebContentsObserver(contents),
231 has_server_side_suggestions_(false),
232 number_of_tiles_(0),
233 number_of_thumbnail_tiles_(0),
234 number_of_gray_tiles_(0),
235 number_of_external_tiles_(0),
236 number_of_thumbnail_errors_(0),
237 number_of_gray_tile_fallbacks_(0),
238 number_of_external_tile_fallbacks_(0),
239 number_of_mouseovers_(0) {
240 }
241