Home | History | Annotate | Download | only in prefs
      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