Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/prefs/pref_metrics_service.h" | 5 #include "chrome/browser/prefs/pref_metrics_service.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/json/json_string_value_serializer.h" | |
| 9 #include "base/md5.h" | |
| 8 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| 11 #include "base/prefs/pref_registry_simple.h" | |
| 9 #include "base/prefs/pref_service.h" | 12 #include "base/prefs/pref_service.h" |
| 13 #include "chrome/browser/browser_process.h" | |
| 14 #include "chrome/browser/browser_shutdown.h" | |
| 15 #include "chrome/browser/extensions/api/music_manager_private/device_id.h" | |
|
Mattias Nissler (ping if slow)
2013/08/15 10:14:51
I'd consider this a layering violation. chrome/bro
bbudge
2013/08/15 17:33:49
This appears to be the only way to get a unique pe
Mattias Nissler (ping if slow)
2013/08/20 12:53:54
That'd be appreciated. Can you file a bug and put
bbudge
2013/08/20 18:17:50
Done.
| |
| 16 #include "chrome/browser/extensions/extension_prefs.h" | |
| 10 #include "chrome/browser/prefs/pref_service_syncable.h" | 17 #include "chrome/browser/prefs/pref_service_syncable.h" |
| 18 #include "chrome/browser/prefs/scoped_user_pref_update.h" | |
| 11 #include "chrome/browser/prefs/session_startup_pref.h" | 19 #include "chrome/browser/prefs/session_startup_pref.h" |
| 12 #include "chrome/browser/prefs/synced_pref_change_registrar.h" | 20 #include "chrome/browser/prefs/synced_pref_change_registrar.h" |
| 13 #include "chrome/browser/profiles/incognito_helpers.h" | 21 #include "chrome/browser/profiles/incognito_helpers.h" |
| 14 #include "chrome/browser/profiles/profile.h" | 22 #include "chrome/browser/profiles/profile.h" |
| 15 #include "chrome/common/pref_names.h" | 23 #include "chrome/common/pref_names.h" |
| 16 #include "components/browser_context_keyed_service/browser_context_dependency_ma nager.h" | 24 #include "components/browser_context_keyed_service/browser_context_dependency_ma nager.h" |
| 17 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 25 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
| 18 | 26 |
| 19 namespace { | 27 namespace { |
| 20 | 28 |
| 21 // Converts a host name into a domain name for easier matching. | 29 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax; |
| 22 std::string GetDomainFromHost(const std::string& host) { | 30 |
| 23 return net::registry_controlled_domains::GetDomainAndRegistry( | 31 // In order to detect changes to Preferences that happen outside of Chrome, we |
| 24 host, | 32 // save selected prefs in Local Settings. When Chrome is started up, we compare |
|
Mattias Nissler (ping if slow)
2013/08/15 10:14:51
local state, here and below
bbudge
2013/08/15 17:33:49
Done.
| |
| 25 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES); | 33 // the saved values against the values observed in the user's Preferences file. |
| 34 // We add a dictionary of dictionaries to Local Settings to hold the saved | |
| 35 // values, grouped by profile. To make the system faster and more resistant to | |
|
Mattias Nissler (ping if slow)
2013/08/15 10:14:51
I don't think hashing makes things faster, given t
bbudge
2013/08/15 17:33:49
I was thinking of I/O and comparing values, but I
| |
| 36 // spoofing, each preference value is hashed with its path and the device id. | |
| 37 const char kProfilePreferenceHashes[] = "profile.preference_hashes"; | |
| 38 | |
| 39 // These preferences must be kept in sync with the TrackedPreference enum in | |
| 40 // tools/metrics/histograms/histograms.xml. To add a new preference, append it | |
| 41 // to the array and add a corresponding value to the histogram enum. | |
| 42 const char* kTrackedPrefs[] = { | |
| 43 prefs::kShowHomeButton, | |
| 44 prefs::kHomePageIsNewTabPage, | |
| 45 prefs::kHomePage, | |
| 46 prefs::kRestoreOnStartup, | |
| 47 prefs::kURLsToRestoreOnStartup, | |
| 48 extensions::ExtensionPrefs::kExtensionsPref, | |
|
Mattias Nissler (ping if slow)
2013/08/15 10:14:51
FWIW, this should be defined in pref_names.{h,cc}
bbudge
2013/08/15 17:33:49
Perhaps a separate CL.
| |
| 49 }; | |
| 50 | |
| 51 const int kTrackedPrefsCount = arraysize(kTrackedPrefs); | |
| 52 | |
| 53 bool BrowserShuttingDown() { | |
| 54 return browser_shutdown::GetShutdownType() != browser_shutdown::NOT_VALID; | |
| 26 } | 55 } |
| 27 | 56 |
| 28 const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax; | |
| 29 | |
| 30 } // namespace | 57 } // namespace |
| 31 | 58 |
| 32 PrefMetricsService::PrefMetricsService(Profile* profile) | 59 PrefMetricsService::PrefMetricsService(Profile* profile) |
| 33 : profile_(profile) { | 60 : weak_factory_(this), |
| 61 profile_(profile) { | |
| 34 RecordLaunchPrefs(); | 62 RecordLaunchPrefs(); |
| 35 | 63 |
| 64 // Make sure local state is initialized. | |
| 65 g_browser_process->local_state(); | |
|
Mattias Nissler (ping if slow)
2013/08/15 10:14:51
This shouldn't be required, local state gets initi
bbudge
2013/08/15 17:33:49
I thought it might fix some tests that are crashin
| |
| 66 | |
| 67 #if !defined(OS_ANDROID) | |
| 68 // We need the machine id to compute pref value hashes. Fetch that, and then | |
| 69 // call CheckTrackedPreferences in the callback. | |
| 70 extensions::api::DeviceId::GetDeviceId( | |
| 71 "PrefMetricsService", // non-empty string to obfuscate the device id. | |
| 72 Bind(&PrefMetricsService::GetDeviceIdCallback, | |
| 73 weak_factory_.GetWeakPtr())); | |
| 74 #else | |
| 75 // Android has no GetDeviceId. | |
| 76 CheckTrackedPreferences(); | |
| 77 #endif // !defined(OS_ANDROID) | |
| 78 | |
| 79 // It's tricky to save preference value hashes on browser shutdown. Instead, | |
| 80 // register for notifications when tracked preferences change so we can update | |
| 81 // the hashes in Local Settings. | |
| 82 pref_registrar_.Init(profile_->GetPrefs()); | |
| 83 for (size_t i = 0; i < arraysize(kTrackedPrefs); ++i) { | |
| 84 pref_registrar_.Add( | |
| 85 kTrackedPrefs[i], | |
| 86 base::Bind(&PrefMetricsService::UpdateTrackedPreference, | |
| 87 weak_factory_.GetWeakPtr(), | |
| 88 kTrackedPrefs[i])); | |
| 89 } | |
| 90 | |
| 36 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); | 91 PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_); |
| 37 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs)); | 92 synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs)); |
| 38 | 93 |
| 39 RegisterSyncedPrefObservers(); | 94 RegisterSyncedPrefObservers(); |
| 40 } | 95 } |
| 41 | 96 |
| 42 PrefMetricsService::~PrefMetricsService() { | 97 PrefMetricsService::~PrefMetricsService() { |
| 43 } | 98 } |
| 44 | 99 |
| 45 void PrefMetricsService::RecordLaunchPrefs() { | 100 void PrefMetricsService::RecordLaunchPrefs() { |
| 46 PrefService* prefs = profile_->GetPrefs(); | 101 PrefService* prefs = profile_->GetPrefs(); |
| 47 bool show_home_button = prefs->GetBoolean(prefs::kShowHomeButton); | 102 bool show_home_button = prefs->GetBoolean(prefs::kShowHomeButton); |
| 48 bool home_page_is_ntp = prefs->GetBoolean(prefs::kHomePageIsNewTabPage); | 103 bool home_page_is_ntp = prefs->GetBoolean(prefs::kHomePageIsNewTabPage); |
| 49 UMA_HISTOGRAM_BOOLEAN("Settings.ShowHomeButton", show_home_button); | 104 UMA_HISTOGRAM_BOOLEAN("Settings.ShowHomeButton", show_home_button); |
| 50 if (show_home_button) { | 105 if (show_home_button) { |
| 51 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage", | 106 UMA_HISTOGRAM_BOOLEAN("Settings.GivenShowHomeButton_HomePageIsNewTabPage", |
| 52 home_page_is_ntp); | 107 home_page_is_ntp); |
| 53 } | 108 } |
| 54 int restore_on_startup = prefs->GetInteger(prefs::kRestoreOnStartup); | 109 int restore_on_startup = prefs->GetInteger(prefs::kRestoreOnStartup); |
| 55 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings", | 110 UMA_HISTOGRAM_ENUMERATION("Settings.StartupPageLoadSettings", |
| 56 restore_on_startup, kSessionStartupPrefValueMax); | 111 restore_on_startup, kSessionStartupPrefValueMax); |
| 57 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) { | 112 if (restore_on_startup == SessionStartupPref::kPrefValueURLs) { |
| 58 const int url_list_size = prefs->GetList( | 113 const int url_list_size = prefs->GetList( |
| 59 prefs::kURLsToRestoreOnStartup)->GetSize(); | 114 prefs::kURLsToRestoreOnStartup)->GetSize(); |
| 60 UMA_HISTOGRAM_CUSTOM_COUNTS( | 115 UMA_HISTOGRAM_CUSTOM_COUNTS( |
| 61 "Settings.StartupPageLoadURLs", url_list_size, 1, 50, 20); | 116 "Settings.StartupPageLoadURLs", url_list_size, 1, 50, 20); |
| 62 } | 117 } |
| 63 } | 118 } |
| 64 | 119 |
| 120 // static | |
| 121 void PrefMetricsService::RegisterPrefs(PrefRegistrySimple* registry) { | |
| 122 // Register the top level dictionary to map profile names to dictionaries of | |
| 123 // tracked preferences. | |
| 124 registry->RegisterDictionaryPref(kProfilePreferenceHashes); | |
| 125 } | |
| 126 | |
| 65 void PrefMetricsService::RegisterSyncedPrefObservers() { | 127 void PrefMetricsService::RegisterSyncedPrefObservers() { |
| 66 LogHistogramValueCallback booleanHandler = base::Bind( | 128 LogHistogramValueCallback booleanHandler = base::Bind( |
| 67 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this)); | 129 &PrefMetricsService::LogBooleanPrefChange, base::Unretained(this)); |
| 68 | 130 |
| 69 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler); | 131 AddPrefObserver(prefs::kShowHomeButton, "ShowHomeButton", booleanHandler); |
| 70 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage", | 132 AddPrefObserver(prefs::kHomePageIsNewTabPage, "HomePageIsNewTabPage", |
| 71 booleanHandler); | 133 booleanHandler); |
| 72 | 134 |
| 73 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings", | 135 AddPrefObserver(prefs::kRestoreOnStartup, "StartupPageLoadSettings", |
| 74 base::Bind(&PrefMetricsService::LogIntegerPrefChange, | 136 base::Bind(&PrefMetricsService::LogIntegerPrefChange, |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 132 const ListValue* items = NULL; | 194 const ListValue* items = NULL; |
| 133 if (!value->GetAsList(&items)) | 195 if (!value->GetAsList(&items)) |
| 134 return; | 196 return; |
| 135 for (size_t i = 0; i < items->GetSize(); ++i) { | 197 for (size_t i = 0; i < items->GetSize(); ++i) { |
| 136 const Value *item_value = NULL; | 198 const Value *item_value = NULL; |
| 137 if (items->Get(i, &item_value)) | 199 if (items->Get(i, &item_value)) |
| 138 item_callback.Run(histogram_name, item_value); | 200 item_callback.Run(histogram_name, item_value); |
| 139 } | 201 } |
| 140 } | 202 } |
| 141 | 203 |
| 204 void PrefMetricsService::GetDeviceIdCallback(const std::string& device_id) { | |
| 205 device_id_ = device_id; | |
| 206 CheckTrackedPreferences(); | |
| 207 } | |
| 208 | |
| 209 void PrefMetricsService::CheckTrackedPreferences() { | |
| 210 PrefService* local_state = g_browser_process->local_state(); | |
| 211 const base::DictionaryValue* profiles = | |
| 212 local_state->GetDictionary(kProfilePreferenceHashes); | |
| 213 DCHECK(profiles); | |
| 214 // Get the hashed prefs dictionary if it exists. If it doesn't, it will be | |
| 215 // created if we set preference values below. | |
| 216 const base::DictionaryValue* hashed_prefs = NULL; | |
| 217 profiles->GetDictionary(profile_->GetProfileName(), &hashed_prefs); | |
| 218 const PrefService* pref_service = profile_->GetPrefs(); | |
| 219 for (size_t i = 0; i < arraysize(kTrackedPrefs); ++i) { | |
| 220 const base::Value* value = | |
| 221 pref_service->GetUserPrefValue(kTrackedPrefs[i]); | |
| 222 if (value) { | |
| 223 std::string value_hash = GetHashedPrefValue(kTrackedPrefs[i], value); | |
| 224 std::string last_hash; | |
| 225 if (hashed_prefs && | |
| 226 hashed_prefs->GetString(kTrackedPrefs[i], &last_hash)) { | |
| 227 if (value_hash != last_hash) { | |
| 228 // Record that the preference changed from its last value. | |
| 229 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceChanged", | |
| 230 i, arraysize(kTrackedPrefs)); | |
| 231 UpdateTrackedPreference(kTrackedPrefs[i]); | |
| 232 } | |
| 233 } else { | |
| 234 // Record that we haven't tracked this preference yet, or the hash in | |
| 235 // local settings was removed. | |
| 236 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceInitialized", | |
| 237 i, arraysize(kTrackedPrefs)); | |
| 238 UpdateTrackedPreference(kTrackedPrefs[i]); | |
| 239 } | |
| 240 } else { | |
| 241 // There is no preference set. Remove any hashed value from local settings | |
| 242 // and if one was present, record that a pref was removed. | |
| 243 if (RemoveTrackedPreference(kTrackedPrefs[i])) { | |
| 244 UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceRemoved", | |
| 245 i, arraysize(kTrackedPrefs)); | |
| 246 } | |
| 247 } | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 void PrefMetricsService::UpdateTrackedPreference(const char* path) { | |
| 252 // Don't try to change local settings during browser shutdown. | |
| 253 if (BrowserShuttingDown()) | |
| 254 return; | |
| 255 | |
| 256 PrefService* pref_service = profile_->GetPrefs(); | |
| 257 const base::Value* value = pref_service->GetUserPrefValue(path); | |
| 258 if (value) { | |
| 259 PrefService* local_state = g_browser_process->local_state(); | |
| 260 DictionaryPrefUpdate update(local_state, kProfilePreferenceHashes); | |
| 261 base::DictionaryValue* prefs = update.Get(); | |
| 262 if (prefs) { | |
| 263 prefs->SetString(GetHashedPrefPath(path), | |
| 264 GetHashedPrefValue(path, value)); | |
| 265 } | |
| 266 } else { | |
| 267 RemoveTrackedPreference(path); | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 bool PrefMetricsService::RemoveTrackedPreference(const char* path) { | |
| 272 // Don't try to change local settings during browser shutdown. | |
| 273 if (BrowserShuttingDown()) | |
| 274 return false; | |
| 275 | |
| 276 PrefService* local_state = g_browser_process->local_state(); | |
| 277 DictionaryPrefUpdate update(local_state, kProfilePreferenceHashes); | |
| 278 base::DictionaryValue* prefs = update.Get(); | |
| 279 if (prefs) | |
| 280 return prefs->Remove(GetHashedPrefPath(path), NULL); | |
| 281 | |
| 282 return false; | |
| 283 } | |
| 284 | |
| 285 std::string PrefMetricsService::GetHashedPrefPath(const char* path) { | |
| 286 std::string hash_pref_path(profile_->GetProfileName()); | |
| 287 hash_pref_path.append("."); | |
| 288 hash_pref_path.append(path); | |
| 289 return hash_pref_path; | |
| 290 } | |
| 291 | |
| 292 std::string PrefMetricsService::GetHashedPrefValue( | |
| 293 const char* path, | |
| 294 const base::Value* value) { | |
| 295 DCHECK(value); | |
| 296 std::string value_string; | |
| 297 JSONStringValueSerializer serializer(&value_string); | |
| 298 serializer.Serialize(*value); | |
| 299 | |
| 300 base::MD5Context context; | |
| 301 base::MD5Init(&context); | |
| 302 base::MD5Update(&context, device_id_); // Salt with device id. | |
| 303 base::MD5Update(&context, path); // Salt with pref path. | |
| 304 base::MD5Update(&context, value_string); | |
| 305 base::MD5Digest digest; | |
| 306 base::MD5Final(&digest, &context); | |
| 307 return base::MD5DigestToBase16(digest); | |
| 308 } | |
| 309 | |
| 142 // static | 310 // static |
| 143 PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() { | 311 PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() { |
| 144 return Singleton<PrefMetricsService::Factory>::get(); | 312 return Singleton<PrefMetricsService::Factory>::get(); |
| 145 } | 313 } |
| 146 | 314 |
| 147 // static | 315 // static |
| 148 PrefMetricsService* PrefMetricsService::Factory::GetForProfile( | 316 PrefMetricsService* PrefMetricsService::Factory::GetForProfile( |
| 149 Profile* profile) { | 317 Profile* profile) { |
| 150 return static_cast<PrefMetricsService*>( | 318 return static_cast<PrefMetricsService*>( |
| 151 GetInstance()->GetServiceForBrowserContext(profile, true)); | 319 GetInstance()->GetServiceForBrowserContext(profile, true)); |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 171 } | 339 } |
| 172 | 340 |
| 173 bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const { | 341 bool PrefMetricsService::Factory::ServiceIsNULLWhileTesting() const { |
| 174 return false; | 342 return false; |
| 175 } | 343 } |
| 176 | 344 |
| 177 content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse( | 345 content::BrowserContext* PrefMetricsService::Factory::GetBrowserContextToUse( |
| 178 content::BrowserContext* context) const { | 346 content::BrowserContext* context) const { |
| 179 return chrome::GetBrowserContextRedirectedInIncognito(context); | 347 return chrome::GetBrowserContextRedirectedInIncognito(context); |
| 180 } | 348 } |
| OLD | NEW |