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
23namespace {
24
25// Used to track if suggestions were issued by the client or the server.
26enum 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.
33const int kNumMostVisited = 8;
34
35// Name of the histogram keeping track of Most Visited impressions.
36const char kMostVisitedImpressionHistogramName[] =
37    "NewTabPage.SuggestionsImpression";
38
39// Format string to generate the name for the histogram keeping track of
40// suggestion impressions.
41const char kMostVisitedImpressionHistogramWithProvider[] =
42    "NewTabPage.SuggestionsImpression.%s";
43
44// Name of the histogram keeping track of Most Visited navigations.
45const char kMostVisitedNavigationHistogramName[] =
46    "NewTabPage.MostVisited";
47
48// Format string to generate the name for the histogram keeping track of
49// suggestion navigations.
50const char kMostVisitedNavigationHistogramWithProvider[] =
51    "NewTabPage.MostVisited.%s";
52
53}  // namespace
54
55DEFINE_WEB_CONTENTS_USER_DATA_KEY(NTPUserDataLogger);
56
57NTPUserDataLogger::~NTPUserDataLogger() {}
58
59// static
60NTPUserDataLogger* 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
79std::string NTPUserDataLogger::GetMostVisitedImpressionHistogramNameForProvider(
80    const std::string& provider) {
81  return base::StringPrintf(kMostVisitedImpressionHistogramWithProvider,
82                            provider.c_str());
83}
84
85// static
86std::string NTPUserDataLogger::GetMostVisitedNavigationHistogramNameForProvider(
87    const std::string& provider) {
88  return base::StringPrintf(kMostVisitedNavigationHistogramWithProvider,
89                            provider.c_str());
90}
91
92void 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
130void 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
170void 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
192void 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
219void 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
229NTPUserDataLogger::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