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/prefs/pref_metrics_service.h" 6 7#include "base/bind.h" 8#include "base/command_line.h" 9#include "base/metrics/histogram.h" 10#include "base/prefs/pref_registry_simple.h" 11#include "base/prefs/pref_service.h" 12#include "base/strings/string_number_conversions.h" 13#include "chrome/browser/browser_process.h" 14#include "chrome/browser/browser_shutdown.h" 15#include "chrome/browser/metrics/rappor/sampling.h" 16#include "chrome/browser/prefs/pref_service_syncable.h" 17#include "chrome/browser/prefs/session_startup_pref.h" 18#include "chrome/browser/prefs/synced_pref_change_registrar.h" 19#include "chrome/browser/profiles/incognito_helpers.h" 20#include "chrome/browser/profiles/profile.h" 21#include "chrome/browser/ui/tabs/pinned_tab_codec.h" 22#include "chrome/common/chrome_switches.h" 23#include "chrome/common/pref_names.h" 24#include "chrome/common/url_constants.h" 25#include "components/keyed_service/content/browser_context_dependency_manager.h" 26#include "components/search_engines/template_url_prepopulate_data.h" 27#include "content/public/browser/browser_url_handler.h" 28#include "crypto/hmac.h" 29#include "net/base/registry_controlled_domains/registry_controlled_domain.h" 30 31namespace { 32 33const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax; 34 35// Record a sample for the Settings.NewTabPage rappor metric. 36void SampleNewTabPageURL(Profile* profile) { 37 GURL ntp_url(chrome::kChromeUINewTabURL); 38 bool reverse_on_redirect = false; 39 content::BrowserURLHandler::GetInstance()->RewriteURLIfNecessary( 40 &ntp_url, 41 profile, 42 &reverse_on_redirect); 43 if (ntp_url.is_valid()) 44 rappor::SampleDomainAndRegistryFromGURL("Settings.NewTabPage", ntp_url); 45} 46 47} // namespace 48 49PrefMetricsService::PrefMetricsService(Profile* profile) 50 : profile_(profile), 51 prefs_(profile_->GetPrefs()), 52 local_state_(g_browser_process->local_state()), 53 weak_factory_(this) { 54 RecordLaunchPrefs(); 55 56 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); 57 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs)); 58 59 RegisterSyncedPrefObservers(); 60} 61 62// For unit testing only. 63PrefMetricsService::PrefMetricsService(Profile* profile, 64 PrefService* local_state) 65 : profile_(profile), 66 prefs_(profile->GetPrefs()), 67 local_state_(local_state), 68 weak_factory_(this) { 69} 70 71PrefMetricsService::~PrefMetricsService() { 72} 73 74void PrefMetricsService::RecordLaunchPrefs() { 75 bool show_home_button = prefs_->GetBoolean(prefs::kShowHomeButton); 76 bool home_page_is_ntp = prefs_->GetBoolean(prefs::kHomePageIsNewTabPage); 77 UMA_HISTOGRAM_BOOLEAN("Settings.ShowHomeButton", show_home_button); 78 if (show_home_button) { 79 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage", 80 home_page_is_ntp); 81 } 82 83 // For non-NTP homepages, see if the URL comes from the same TLD+1 as a known 84 // search engine. Note that this is only an approximation of search engine 85 // use, due to both false negatives (pages that come from unknown TLD+1 X but 86 // consist of a search box that sends to known TLD+1 Y) and false positives 87 // (pages that share a TLD+1 with a known engine but aren't actually search 88 // pages, e.g. plus.google.com). Additionally, record the TLD+1 of non-NTP 89 // homepages through the privacy-preserving Rappor service. 90 if (!home_page_is_ntp) { 91 GURL homepage_url(prefs_->GetString(prefs::kHomePage)); 92 if (homepage_url.is_valid()) { 93 UMA_HISTOGRAM_ENUMERATION( 94 "Settings.HomePageEngineType", 95 TemplateURLPrepopulateData::GetEngineType(homepage_url), 96 SEARCH_ENGINE_MAX); 97 rappor::SampleDomainAndRegistryFromGURL("Settings.HomePage2", 98 homepage_url); 99 } 100 } 101 102 SampleNewTabPageURL(profile_); 103 104 int restore_on_startup = prefs_->GetInteger(prefs::kRestoreOnStartup); 105 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings", 106 restore_on_startup, kSessionStartupPrefValueMax); 107 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) { 108 const base::ListValue* url_list = 109 prefs_->GetList(prefs::kURLsToRestoreOnStartup); 110 UMA_HISTOGRAM_CUSTOM_COUNTS("Settings.StartupPageLoadURLs", 111 url_list->GetSize(), 1, 50, 20); 112 // Similarly, check startup pages for known search engine TLD+1s. 113 std::string url_text; 114 for (size_t i = 0; i < url_list->GetSize(); ++i) { 115 if (url_list->GetString(i, &url_text)) { 116 GURL start_url(url_text); 117 if (start_url.is_valid()) { 118 UMA_HISTOGRAM_ENUMERATION( 119 "Settings.StartupPageEngineTypes", 120 TemplateURLPrepopulateData::GetEngineType(start_url), 121 SEARCH_ENGINE_MAX); 122 if (i == 0) { 123 rappor::SampleDomainAndRegistryFromGURL("Settings.FirstStartupPage", 124 start_url); 125 } 126 } 127 } 128 } 129 } 130 131#if !defined(OS_ANDROID) 132 StartupTabs startup_tabs = PinnedTabCodec::ReadPinnedTabs(profile_); 133 UMA_HISTOGRAM_CUSTOM_COUNTS("Settings.PinnedTabs", 134 startup_tabs.size(), 1, 50, 20); 135 for (size_t i = 0; i < startup_tabs.size(); ++i) { 136 GURL start_url(startup_tabs.at(i).url); 137 if (start_url.is_valid()) { 138 UMA_HISTOGRAM_ENUMERATION( 139 "Settings.PinnedTabEngineTypes", 140 TemplateURLPrepopulateData::GetEngineType(start_url), 141 SEARCH_ENGINE_MAX); 142 } 143 } 144#endif 145} 146 147void PrefMetricsService::RegisterSyncedPrefObservers() { 148 LogHistogramValueCallback booleanHandler = base::Bind( 149 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this)); 150 151 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler); 152 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage", 153 booleanHandler); 154 155 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings", 156 base::Bind(&PrefMetricsService::LogIntegerPrefChange, 157 base::Unretained(this), 158 kSessionStartupPrefValueMax)); 159} 160 161void PrefMetricsService::AddPrefObserver( 162 const std::string& path, 163 const std::string& histogram_name_prefix, 164 const LogHistogramValueCallback& callback) { 165 synced_pref_change_registrar_->Add(path.c_str(), 166 base::Bind(&PrefMetricsService::OnPrefChanged, 167 base::Unretained(this), 168 histogram_name_prefix, callback)); 169} 170 171void PrefMetricsService::OnPrefChanged( 172 const std::string& histogram_name_prefix, 173 const LogHistogramValueCallback& callback, 174 const std::string& path, 175 bool from_sync) { 176 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); 177 const PrefService::Preference* pref = prefs->FindPreference(path.c_str()); 178 DCHECK(pref); 179 std::string source_name( 180 from_sync ? ".PulledFromSync" : ".PushedToSync"); 181 std::string histogram_name("Settings." + histogram_name_prefix + source_name); 182 callback.Run(histogram_name, pref->GetValue()); 183} 184 185void PrefMetricsService::LogBooleanPrefChange(const std::string& histogram_name, 186 const base::Value* value) { 187 bool boolean_value = false; 188 if (!value->GetAsBoolean(&boolean_value)) 189 return; 190 base::HistogramBase* histogram = base::BooleanHistogram::FactoryGet( 191 histogram_name, base::HistogramBase::kUmaTargetedHistogramFlag); 192 histogram->Add(boolean_value); 193} 194 195void PrefMetricsService::LogIntegerPrefChange(int boundary_value, 196 const std::string& histogram_name, 197 const base::Value* value) { 198 int integer_value = 0; 199 if (!value->GetAsInteger(&integer_value)) 200 return; 201 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet( 202 histogram_name, 203 1, 204 boundary_value, 205 boundary_value + 1, 206 base::HistogramBase::kUmaTargetedHistogramFlag); 207 histogram->Add(integer_value); 208} 209 210// static 211PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() { 212 return Singleton<PrefMetricsService::Factory>::get(); 213} 214 215// static 216PrefMetricsService* PrefMetricsService::Factory::GetForProfile( 217 Profile* profile) { 218 return static_cast<PrefMetricsService*>( 219 GetInstance()->GetServiceForBrowserContext(profile, true)); 220} 221 222PrefMetricsService::Factory::Factory() 223 : BrowserContextKeyedServiceFactory( 224 "PrefMetricsService", 225 BrowserContextDependencyManager::GetInstance()) { 226} 227 228PrefMetricsService::Factory::~Factory() { 229} 230 231KeyedService* PrefMetricsService::Factory::BuildServiceInstanceFor( 232 content::BrowserContext* profile) const { 233 return new PrefMetricsService(static_cast<Profile*>(profile)); 234} 235 236bool PrefMetricsService::Factory::ServiceIsCreatedWithBrowserContext() const { 237 return true; 238} 239 240bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const { 241 return false; 242} 243 244content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse( 245 content::BrowserContext* context) const { 246 return chrome::GetBrowserContextRedirectedInIncognito(context); 247} 248