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/search_engines/template_url_prepopulate_data.h" 22 #include "chrome/browser/ui/tabs/pinned_tab_codec.h" 23 #include "chrome/common/chrome_switches.h" 24 #include "chrome/common/pref_names.h" 25 #include "chrome/common/url_constants.h" 26 #include "components/keyed_service/content/browser_context_dependency_manager.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 31 namespace { 32 33 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax; 34 35 // Record a sample for the Settings.NewTabPage rappor metric. 36 void 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 49 PrefMetricsService::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. 63 PrefMetricsService::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 71 PrefMetricsService::~PrefMetricsService() { 72 } 73 74 void 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 147 void 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 161 void 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 171 void 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 185 void 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 195 void 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 211 PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() { 212 return Singleton<PrefMetricsService::Factory>::get(); 213 } 214 215 // static 216 PrefMetricsService* PrefMetricsService::Factory::GetForProfile( 217 Profile* profile) { 218 return static_cast<PrefMetricsService*>( 219 GetInstance()->GetServiceForBrowserContext(profile, true)); 220 } 221 222 PrefMetricsService::Factory::Factory() 223 : BrowserContextKeyedServiceFactory( 224 "PrefMetricsService", 225 BrowserContextDependencyManager::GetInstance()) { 226 } 227 228 PrefMetricsService::Factory::~Factory() { 229 } 230 231 KeyedService* PrefMetricsService::Factory::BuildServiceInstanceFor( 232 content::BrowserContext* profile) const { 233 return new PrefMetricsService(static_cast<Profile*>(profile)); 234 } 235 236 bool PrefMetricsService::Factory::ServiceIsCreatedWithBrowserContext() const { 237 return true; 238 } 239 240 bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const { 241 return false; 242 } 243 244 content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse( 245 content::BrowserContext* context) const { 246 return chrome::GetBrowserContextRedirectedInIncognito(context); 247 } 248