blob: 23a7fda4a0aa2711adbe71bd8e1d34e151d53a6b [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/webui/ntp/ntp_user_data_logger.h"
#include <algorithm>
#include "base/metrics/histogram.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/after_startup_task_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/most_visited_iframe_source.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/common/search_urls.h"
#include "chrome/common/url_constants.h"
#include "components/browser_sync/browser/profile_sync_service.h"
#include "components/sync_sessions/sessions_sync_manager.h"
#include "components/sync_sessions/sync_sessions_metrics.h"
#include "content/public/browser/navigation_details.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/browser/web_contents.h"
// Macro to log UMA statistics related to the 8 tiles shown on the NTP.
#define UMA_HISTOGRAM_NTP_TILES(name, sample) \
UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 0, 8, 9)
namespace {
// Used to track if suggestions were issued by the client or the server.
enum SuggestionsType {
CLIENT_SIDE = 0,
SERVER_SIDE = 1,
SUGGESTIONS_TYPE_COUNT = 2
};
// Number of Most Visited elements on the NTP for logging purposes.
const int kNumMostVisited = 8;
// Name of the histogram keeping track of Most Visited impressions.
const char kMostVisitedImpressionHistogramName[] =
"NewTabPage.SuggestionsImpression";
// Format string to generate the name for the histogram keeping track of
// suggestion impressions.
const char kMostVisitedImpressionHistogramWithProvider[] =
"NewTabPage.SuggestionsImpression.%s";
// Name of the histogram keeping track of Most Visited navigations.
const char kMostVisitedNavigationHistogramName[] =
"NewTabPage.MostVisited";
// Format string to generate the name for the histogram keeping track of
// suggestion navigations.
const char kMostVisitedNavigationHistogramWithProvider[] =
"NewTabPage.MostVisited.%s";
} // namespace
DEFINE_WEB_CONTENTS_USER_DATA_KEY(NTPUserDataLogger);
// Log a time event for a given |histogram| at a given |value|. This
// routine exists because regular histogram macros are cached thus can't be used
// if the name of the histogram will change at a given call site.
void logLoadTimeHistogram(const std::string& histogram, base::TimeDelta value) {
base::HistogramBase* counter = base::Histogram::FactoryTimeGet(
histogram,
base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromSeconds(60), 100,
base::Histogram::kUmaTargetedHistogramFlag);
if (counter)
counter->AddTime(value);
}
NTPUserDataLogger::~NTPUserDataLogger() {}
// static
NTPUserDataLogger* NTPUserDataLogger::GetOrCreateFromWebContents(
content::WebContents* content) {
// Calling CreateForWebContents when an instance is already attached has no
// effect, so we can do this.
NTPUserDataLogger::CreateForWebContents(content);
NTPUserDataLogger* logger = NTPUserDataLogger::FromWebContents(content);
// We record the URL of this NTP in order to identify navigations that
// originate from it. We use the NavigationController's URL since it might
// differ from the WebContents URL which is usually chrome://newtab/.
const content::NavigationEntry* entry =
content->GetController().GetVisibleEntry();
if (entry)
logger->ntp_url_ = entry->GetURL();
return logger;
}
// static
std::string NTPUserDataLogger::GetMostVisitedImpressionHistogramNameForProvider(
const std::string& provider) {
return base::StringPrintf(kMostVisitedImpressionHistogramWithProvider,
provider.c_str());
}
// static
std::string NTPUserDataLogger::GetMostVisitedNavigationHistogramNameForProvider(
const std::string& provider) {
return base::StringPrintf(kMostVisitedNavigationHistogramWithProvider,
provider.c_str());
}
void NTPUserDataLogger::EmitNtpStatistics() {
UMA_HISTOGRAM_COUNTS("NewTabPage.NumberOfMouseOvers", number_of_mouseovers_);
number_of_mouseovers_ = 0;
// We only send statistics once per page.
// And we don't send if there are no tiles recorded.
if (has_emitted_ || !number_of_tiles_)
return;
// LoadTime only gets update once per page, so we don't have it on reloads.
if (load_time_ > base::TimeDelta::FromMilliseconds(0)) {
logLoadTimeHistogram("NewTabPage.LoadTime", load_time_);
// Split between ML and MV.
std::string type = has_server_side_suggestions_ ?
"MostLikely" : "MostVisited";
logLoadTimeHistogram("NewTabPage.LoadTime." + type, load_time_);
// Split between Web and Local.
std::string source = ntp_url_.SchemeIsHTTPOrHTTPS() ? "Web" : "LocalNTP";
logLoadTimeHistogram("NewTabPage.LoadTime." + source, load_time_);
// Split between Startup and non-startup.
std::string status = during_startup_ ? "Startup" : "NewTab";
logLoadTimeHistogram("NewTabPage.LoadTime." + status, load_time_);
load_time_ = base::TimeDelta::FromMilliseconds(0);
}
UMA_HISTOGRAM_ENUMERATION(
"NewTabPage.SuggestionsType",
has_server_side_suggestions_ ? SERVER_SIDE : CLIENT_SIDE,
SUGGESTIONS_TYPE_COUNT);
has_server_side_suggestions_ = false;
has_client_side_suggestions_ = false;
UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfTiles", number_of_tiles_);
number_of_tiles_ = 0;
UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfThumbnailTiles",
number_of_thumbnail_tiles_);
number_of_thumbnail_tiles_ = 0;
UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfGrayTiles",
number_of_gray_tiles_);
number_of_gray_tiles_ = 0;
UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfExternalTiles",
number_of_external_tiles_);
number_of_external_tiles_ = 0;
UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfThumbnailErrors",
number_of_thumbnail_errors_);
number_of_thumbnail_errors_ = 0;
UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfGrayTileFallbacks",
number_of_gray_tile_fallbacks_);
number_of_gray_tile_fallbacks_ = 0;
UMA_HISTOGRAM_NTP_TILES("NewTabPage.NumberOfExternalTileFallbacks",
number_of_external_tile_fallbacks_);
number_of_external_tile_fallbacks_ = 0;
has_emitted_ = true;
during_startup_ = false;
}
void NTPUserDataLogger::LogEvent(NTPLoggingEventType event,
base::TimeDelta time) {
switch (event) {
// It is possible that our page gets update with a different set of
// suggestions if the NTP is left open enough time.
// In either case, we want to flush our stats before recounting again.
case NTP_SERVER_SIDE_SUGGESTION:
if (has_client_side_suggestions_)
EmitNtpStatistics();
has_server_side_suggestions_ = true;
break;
case NTP_CLIENT_SIDE_SUGGESTION:
if (has_server_side_suggestions_)
EmitNtpStatistics();
has_client_side_suggestions_ = true;
break;
case NTP_TILE:
number_of_tiles_++;
break;
case NTP_THUMBNAIL_TILE:
number_of_thumbnail_tiles_++;
break;
case NTP_GRAY_TILE:
number_of_gray_tiles_++;
break;
case NTP_EXTERNAL_TILE:
number_of_external_tiles_++;
break;
case NTP_THUMBNAIL_ERROR:
number_of_thumbnail_errors_++;
break;
case NTP_GRAY_TILE_FALLBACK:
number_of_gray_tile_fallbacks_++;
break;
case NTP_EXTERNAL_TILE_FALLBACK:
number_of_external_tile_fallbacks_++;
break;
case NTP_MOUSEOVER:
number_of_mouseovers_++;
break;
case NTP_TILE_LOADED:
// The time at which the last tile has loaded (title, thumbnail or single)
// is a good proxy for the total load time of the NTP, therefore we keep
// the max as the load time.
load_time_ = std::max(load_time_, time);
break;
default:
NOTREACHED();
}
}
void NTPUserDataLogger::LogMostVisitedImpression(
int position, const base::string16& provider) {
// Log the Most Visited navigation for navigations that have providers and
// those that dont.
UMA_HISTOGRAM_ENUMERATION(kMostVisitedImpressionHistogramName, position,
kNumMostVisited);
// If a provider is specified, log the metric specific to it.
if (!provider.empty()) {
// Cannot rely on UMA histograms macro because the name of the histogram is
// generated dynamically.
base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
GetMostVisitedImpressionHistogramNameForProvider(
base::UTF16ToUTF8(provider)),
1,
kNumMostVisited,
kNumMostVisited + 1,
base::Histogram::kUmaTargetedHistogramFlag);
counter->Add(position);
}
}
void NTPUserDataLogger::LogMostVisitedNavigation(
int position, const base::string16& provider) {
// Log the Most Visited navigation for navigations that have providers and
// those that dont.
UMA_HISTOGRAM_ENUMERATION(kMostVisitedNavigationHistogramName, position,
kNumMostVisited);
// If a provider is specified, log the metric specific to it.
if (!provider.empty()) {
// Cannot rely on UMA histograms macro because the name of the histogram is
// generated dynamically.
base::HistogramBase* counter = base::LinearHistogram::FactoryGet(
GetMostVisitedNavigationHistogramNameForProvider(
base::UTF16ToUTF8(provider)),
1,
kNumMostVisited,
kNumMostVisited + 1,
base::Histogram::kUmaTargetedHistogramFlag);
counter->Add(position);
}
// Records the action. This will be available as a time-stamped stream
// server-side and can be used to compute time-to-long-dwell.
content::RecordAction(base::UserMetricsAction("MostVisited_Clicked"));
}
// content::WebContentsObserver override
void NTPUserDataLogger::NavigationEntryCommitted(
const content::LoadCommittedDetails& load_details) {
if (!load_details.previous_url.is_valid())
return;
if (search::MatchesOriginAndPath(ntp_url_, load_details.previous_url)) {
EmitNtpStatistics();
}
}
NTPUserDataLogger::NTPUserDataLogger(content::WebContents* contents)
: content::WebContentsObserver(contents),
has_server_side_suggestions_(false),
has_client_side_suggestions_(false),
number_of_tiles_(0),
number_of_thumbnail_tiles_(0),
number_of_gray_tiles_(0),
number_of_external_tiles_(0),
number_of_thumbnail_errors_(0),
number_of_gray_tile_fallbacks_(0),
number_of_external_tile_fallbacks_(0),
number_of_mouseovers_(0),
has_emitted_(false),
during_startup_(false) {
during_startup_ = !AfterStartupTaskUtils::IsBrowserStartupComplete();
// We record metrics about session data here because when this class typically
// emits metrics it is too late. This session data would theoretically have
// been used to populate the page, and we want to learn about its state when
// the NTP is being generated.
if (contents) {
ProfileSyncService* sync = ProfileSyncServiceFactory::GetForProfile(
Profile::FromBrowserContext(contents->GetBrowserContext()));
if (sync) {
browser_sync::SessionsSyncManager* sessions =
static_cast<browser_sync::SessionsSyncManager*>(
sync->GetSessionsSyncableService());
if (sessions) {
sync_sessions::SyncSessionsMetrics::RecordYoungestForeignTabAgeOnNTP(
sessions);
}
}
}
}