Index: chrome/browser/prefs/pref_hash_filter.cc |
diff --git a/chrome/browser/prefs/pref_hash_filter.cc b/chrome/browser/prefs/pref_hash_filter.cc |
index d50d66c0e550ad3b6cb8f73994592d1b09a321e6..8376b0359e2a9dd2d470bc9383d34a3f7a59b2a7 100644 |
--- a/chrome/browser/prefs/pref_hash_filter.cc |
+++ b/chrome/browser/prefs/pref_hash_filter.cc |
@@ -7,90 +7,179 @@ |
#include "base/logging.h" |
#include "base/metrics/histogram.h" |
#include "base/values.h" |
-#include "chrome/browser/prefs/pref_hash_store.h" |
#include "chrome/common/pref_names.h" |
namespace { |
-// These preferences must be kept in sync with the TrackedPreference enum in |
-// tools/metrics/histograms/histograms.xml. To add a new preference, append it |
-// to the array and add a corresponding value to the histogram enum. Replace |
-// removed preferences with "". |
-const char* kTrackedPrefs[] = { |
- prefs::kShowHomeButton, |
- prefs::kHomePageIsNewTabPage, |
- prefs::kHomePage, |
- prefs::kRestoreOnStartup, |
- prefs::kURLsToRestoreOnStartup, |
- prefs::kExtensionsPref, |
- prefs::kGoogleServicesLastUsername, |
- prefs::kSearchProviderOverrides, |
- prefs::kDefaultSearchProviderSearchURL, |
- prefs::kDefaultSearchProviderKeyword, |
- prefs::kDefaultSearchProviderName, |
-#if !defined(OS_ANDROID) |
- prefs::kPinnedTabs, |
-#else |
- "", |
-#endif |
- prefs::kExtensionKnownDisabled, |
- prefs::kProfileResetPromptMemento, |
-}; |
- |
void ReportValidationResult(PrefHashStore::ValueState value_state, |
- size_t value_index) { |
+ size_t value_index, |
+ size_t num_values) { |
switch (value_state) { |
case PrefHashStore::UNCHANGED: |
UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceUnchanged", |
- value_index, arraysize(kTrackedPrefs)); |
+ value_index, num_values); |
return; |
case PrefHashStore::CLEARED: |
UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceCleared", |
- value_index, arraysize(kTrackedPrefs)); |
+ value_index, num_values); |
return; |
case PrefHashStore::MIGRATED: |
UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceMigrated", |
- value_index, arraysize(kTrackedPrefs)); |
+ value_index, num_values); |
return; |
case PrefHashStore::CHANGED: |
UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceChanged", |
- value_index, arraysize(kTrackedPrefs)); |
+ value_index, num_values); |
return; |
case PrefHashStore::UNKNOWN_VALUE: |
UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceInitialized", |
- value_index, arraysize(kTrackedPrefs)); |
+ value_index, num_values); |
return; |
} |
NOTREACHED() << "Unexpected PrefHashStore::ValueState: " << value_state; |
} |
+void ReportSplitPreferenceChangedCount(const std::string& path, size_t count) { |
+ // Handle each known split preference explicitly as UMA macros result in |
+ // static blocks which aren't compatible with dynamically generated names. |
+ if (path == prefs::kExtensionsPref) { |
+ UMA_HISTOGRAM_COUNTS_100( |
+ "Settings.TrackedSplitPreferenceChangedCount.kExtensionsPref", |
+ count); |
+ return; |
+ } |
+ // All split preferences need to be handled above. |
+ NOTREACHED(); |
+} |
+ |
} // namespace |
PrefHashFilter::PrefHashFilter(scoped_ptr<PrefHashStore> pref_hash_store) |
- : pref_hash_store_(pref_hash_store.Pass()), |
- tracked_paths_(kTrackedPrefs, kTrackedPrefs + arraysize(kTrackedPrefs)) {} |
+ : pref_hash_store_(pref_hash_store.Pass()) { |
+ // These preferences must be kept in sync with the TrackedPreference enum in |
+ // tools/metrics/histograms/histograms.xml. To add a new preference, append it |
+ // to the array and add a corresponding value to the histogram enum. New split |
+ // preferences must also be added to ReportSplitPreferenceChangedCount above. |
+ // The index of each tracked preference must not change; replace removed |
+ // preferences' strategies with TRACKING_STRATEGY_NONE rather than removing |
+ // them. |
+ static const TrackedPreference kTrackedPrefs[] = { |
+ { prefs::kShowHomeButton, TRACKING_STRATEGY_ATOMIC }, |
erikwright (departed)
2013/12/17 02:22:50
Suggestion: Make the 'reporting id' a member of th
gab
2013/12/17 18:08:05
I also considered this, I don't like that it adds
|
+ { prefs::kHomePageIsNewTabPage, TRACKING_STRATEGY_ATOMIC }, |
+ { prefs::kHomePage, TRACKING_STRATEGY_ATOMIC }, |
+ { prefs::kRestoreOnStartup, TRACKING_STRATEGY_ATOMIC }, |
+ { prefs::kURLsToRestoreOnStartup, TRACKING_STRATEGY_ATOMIC }, |
+ { prefs::kExtensionsPref, TRACKING_STRATEGY_SPLIT }, |
+ { prefs::kGoogleServicesLastUsername, TRACKING_STRATEGY_ATOMIC }, |
+ { prefs::kSearchProviderOverrides, TRACKING_STRATEGY_ATOMIC }, |
+ { prefs::kDefaultSearchProviderSearchURL, TRACKING_STRATEGY_ATOMIC }, |
+ { prefs::kDefaultSearchProviderKeyword, TRACKING_STRATEGY_ATOMIC }, |
+ { prefs::kDefaultSearchProviderName, TRACKING_STRATEGY_ATOMIC }, |
+ #if !defined(OS_ANDROID) |
+ { prefs::kPinnedTabs, TRACKING_STRATEGY_ATOMIC }, |
+ #else |
+ { NULL, TRACKING_STRATEGY_NONE }, |
+ #endif |
+ { prefs::kExtensionKnownDisabled, TRACKING_STRATEGY_ATOMIC }, |
+ { prefs::kProfileResetPromptMemento, TRACKING_STRATEGY_ATOMIC }, |
+ }; |
+ |
+ InitTrackedPreferences(kTrackedPrefs, arraysize(kTrackedPrefs)); |
+} |
+ |
+PrefHashFilter::PrefHashFilter( |
+ scoped_ptr<PrefHashStore> pref_hash_store, |
+ const PrefHashFilter::TrackedPreference tracked_preferences[], |
+ size_t tracked_preferences_size) |
+ : pref_hash_store_(pref_hash_store.Pass()) { |
+ InitTrackedPreferences(tracked_preferences, tracked_preferences_size); |
+} |
PrefHashFilter::~PrefHashFilter() {} |
// Updates the stored hash to correspond to the updated preference value. |
void PrefHashFilter::FilterUpdate(const std::string& path, |
const base::Value* value) { |
- if (tracked_paths_.find(path) != tracked_paths_.end()) |
- pref_hash_store_->StoreHash(path, value); |
+ TrackedPreferencesMap::const_iterator it = tracked_paths_.find(path); |
+ if (it != tracked_paths_.end()) |
+ StoreHashBasedOnStrategy(path, value, it->second.strategy); |
} |
// Validates loaded preference values according to stored hashes, reports |
// validation results via UMA, and updates hashes in case of mismatch. |
void PrefHashFilter::FilterOnLoad(base::DictionaryValue* pref_store_contents) { |
- for (size_t i = 0; i < arraysize(kTrackedPrefs); ++i) { |
- if (kTrackedPrefs[i][0] == '\0') |
- continue; |
+ for (TrackedPreferencesMap::const_iterator it = tracked_paths_.begin(); |
+ it != tracked_paths_.end(); ++it) { |
+ const std::string& pref_path = it->first; |
+ const TrackedPreferenceMetaData& metadata = it->second; |
const base::Value* value = NULL; |
- pref_store_contents->Get(kTrackedPrefs[i], &value); |
+ pref_store_contents->Get(pref_path, &value); |
PrefHashStore::ValueState value_state = |
- pref_hash_store_->CheckValue(kTrackedPrefs[i], value); |
- ReportValidationResult(value_state, i); |
+ CheckValueBasedOnStrategy(pref_path, value, metadata.strategy); |
+ ReportValidationResult(value_state, metadata.index, tracked_paths_.size()); |
if (value_state != PrefHashStore::UNCHANGED) |
- pref_hash_store_->StoreHash(kTrackedPrefs[i], value); |
+ StoreHashBasedOnStrategy(pref_path, value, metadata.strategy); |
+ } |
+} |
+ |
+void PrefHashFilter::InitTrackedPreferences( |
+ const PrefHashFilter::TrackedPreference tracked_preferences[], |
erikwright (departed)
2013/12/17 02:22:50
verify whether it's more common to use [] or * in
gab
2013/12/17 18:08:05
Here are 161 cases where an array is passed as the
|
+ size_t tracked_preferences_size) { |
+ for (size_t i = 0; i < tracked_preferences_size; ++i) { |
+ if (tracked_preferences[i].strategy == TRACKING_STRATEGY_NONE) |
+ continue; |
+ bool is_new = tracked_paths_.insert(std::make_pair( |
+ std::string(tracked_preferences[i].name), |
+ TrackedPreferenceMetaData(i, tracked_preferences[i].strategy))).second; |
+ DCHECK(is_new); |
+ } |
+} |
+ |
+void PrefHashFilter::StoreHashBasedOnStrategy(const std::string& path, |
+ const base::Value* value, |
+ PrefTrackingStrategy strategy) { |
+ switch (strategy) { |
+ case TRACKING_STRATEGY_NONE: |
+ NOTREACHED(); |
+ break; |
+ case TRACKING_STRATEGY_ATOMIC: |
+ pref_hash_store_->StoreHash(path, value); |
+ break; |
+ case TRACKING_STRATEGY_SPLIT: |
+ const base::DictionaryValue* dict_value = NULL; |
+ if (value && !value->GetAsDictionary(&dict_value)) { |
+ NOTREACHED(); |
+ break; |
erikwright (departed)
2013/12/17 02:22:50
I find this a little foreign (two breaks in a case
gab
2013/12/17 18:08:05
I considered switching the break; for a return; bu
|
+ } |
+ pref_hash_store_->StoreSplitHash(path, dict_value); |
+ break; |
+ } |
erikwright (departed)
2013/12/17 02:22:50
Are unhandled enums handled here too? Or is it onl
gab
2013/12/17 18:08:05
Unhandled enums will throw a couple error on gcc/c
|
+} |
+ |
+PrefHashStore::ValueState PrefHashFilter::CheckValueBasedOnStrategy( |
+ const std::string& path, |
+ const base::Value* value, |
+ PrefTrackingStrategy strategy) { |
+ switch (strategy) { |
+ case TRACKING_STRATEGY_NONE: |
+ NOTREACHED(); |
+ return PrefHashStore::UNKNOWN_VALUE; |
+ case TRACKING_STRATEGY_ATOMIC: |
+ return pref_hash_store_->CheckValue(path, value); |
+ case TRACKING_STRATEGY_SPLIT: |
+ const base::DictionaryValue* dict_value = NULL; |
+ if (value && !value->GetAsDictionary(&dict_value)) { |
+ NOTREACHED(); |
+ return PrefHashStore::UNKNOWN_VALUE; |
+ } |
+ |
+ std::vector<std::string> invalid_keys; |
+ PrefHashStore::ValueState state = |
+ pref_hash_store_->CheckSplitValue(path, dict_value, &invalid_keys); |
+ if (state == PrefHashStore::CHANGED) |
+ ReportSplitPreferenceChangedCount(path, invalid_keys.size()); |
+ return state; |
} |
+ NOTREACHED(); |
+ return PrefHashStore::UNKNOWN_VALUE; |
} |