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_hash_filter.h" 6 7 #include <algorithm> 8 9 #include "base/logging.h" 10 #include "base/metrics/histogram.h" 11 #include "base/prefs/pref_service.h" 12 #include "base/prefs/pref_store.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/time/time.h" 15 #include "base/values.h" 16 #include "chrome/browser/prefs/pref_hash_store.h" 17 #include "chrome/browser/prefs/pref_hash_store_transaction.h" 18 #include "chrome/browser/prefs/tracked/dictionary_hash_store_contents.h" 19 #include "chrome/browser/prefs/tracked/tracked_atomic_preference.h" 20 #include "chrome/browser/prefs/tracked/tracked_split_preference.h" 21 #include "chrome/common/pref_names.h" 22 #include "components/pref_registry/pref_registry_syncable.h" 23 24 PrefHashFilter::PrefHashFilter( 25 scoped_ptr<PrefHashStore> pref_hash_store, 26 const std::vector<TrackedPreferenceMetadata>& tracked_preferences, 27 const base::Closure& on_reset_on_load, 28 TrackedPreferenceValidationDelegate* delegate, 29 size_t reporting_ids_count, 30 bool report_super_mac_validity) 31 : pref_hash_store_(pref_hash_store.Pass()), 32 on_reset_on_load_(on_reset_on_load), 33 report_super_mac_validity_(report_super_mac_validity) { 34 DCHECK(pref_hash_store_); 35 DCHECK_GE(reporting_ids_count, tracked_preferences.size()); 36 37 for (size_t i = 0; i < tracked_preferences.size(); ++i) { 38 const TrackedPreferenceMetadata& metadata = tracked_preferences[i]; 39 40 scoped_ptr<TrackedPreference> tracked_preference; 41 switch (metadata.strategy) { 42 case TRACKING_STRATEGY_ATOMIC: 43 tracked_preference.reset( 44 new TrackedAtomicPreference(metadata.name, 45 metadata.reporting_id, 46 reporting_ids_count, 47 metadata.enforcement_level, 48 delegate)); 49 break; 50 case TRACKING_STRATEGY_SPLIT: 51 tracked_preference.reset( 52 new TrackedSplitPreference(metadata.name, 53 metadata.reporting_id, 54 reporting_ids_count, 55 metadata.enforcement_level, 56 delegate)); 57 break; 58 } 59 DCHECK(tracked_preference); 60 61 bool is_new = tracked_paths_.add(metadata.name, 62 tracked_preference.Pass()).second; 63 DCHECK(is_new); 64 } 65 } 66 67 PrefHashFilter::~PrefHashFilter() { 68 // Ensure new values for all |changed_paths_| have been flushed to 69 // |pref_hash_store_| already. 70 DCHECK(changed_paths_.empty()); 71 } 72 73 // static 74 void PrefHashFilter::RegisterProfilePrefs( 75 user_prefs::PrefRegistrySyncable* registry) { 76 // See GetResetTime for why this is a StringPref and not Int64Pref. 77 registry->RegisterStringPref( 78 prefs::kPreferenceResetTime, 79 base::Int64ToString(base::Time().ToInternalValue()), 80 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 81 } 82 83 // static 84 base::Time PrefHashFilter::GetResetTime(PrefService* user_prefs) { 85 // Provide our own implementation (identical to the PrefService::GetInt64) in 86 // order to ensure it remains consistent with the way we store this value 87 // (which we do via a PrefStore, preventing us from reusing 88 // PrefService::SetInt64). 89 int64 internal_value = base::Time().ToInternalValue(); 90 if (!base::StringToInt64( 91 user_prefs->GetString(prefs::kPreferenceResetTime), 92 &internal_value)) { 93 // Somehow the value stored on disk is not a valid int64. 94 NOTREACHED(); 95 return base::Time(); 96 } 97 return base::Time::FromInternalValue(internal_value); 98 } 99 100 // static 101 void PrefHashFilter::ClearResetTime(PrefService* user_prefs) { 102 user_prefs->ClearPref(prefs::kPreferenceResetTime); 103 } 104 105 void PrefHashFilter::Initialize(base::DictionaryValue* pref_store_contents) { 106 scoped_ptr<PrefHashStoreTransaction> hash_store_transaction( 107 pref_hash_store_->BeginTransaction(scoped_ptr<HashStoreContents>( 108 new DictionaryHashStoreContents(pref_store_contents)))); 109 for (TrackedPreferencesMap::const_iterator it = tracked_paths_.begin(); 110 it != tracked_paths_.end(); ++it) { 111 const std::string& initialized_path = it->first; 112 const TrackedPreference* initialized_preference = it->second; 113 const base::Value* value = NULL; 114 pref_store_contents->Get(initialized_path, &value); 115 initialized_preference->OnNewValue(value, hash_store_transaction.get()); 116 } 117 } 118 119 // Marks |path| has having changed if it is part of |tracked_paths_|. A new hash 120 // will be stored for it the next time FilterSerializeData() is invoked. 121 void PrefHashFilter::FilterUpdate(const std::string& path) { 122 TrackedPreferencesMap::const_iterator it = tracked_paths_.find(path); 123 if (it != tracked_paths_.end()) 124 changed_paths_.insert(std::make_pair(path, it->second)); 125 } 126 127 // Updates the stored hashes for |changed_paths_| before serializing data to 128 // disk. This is required as storing the hash everytime a pref's value changes 129 // is too expensive (see perf regression @ http://crbug.com/331273). 130 void PrefHashFilter::FilterSerializeData( 131 base::DictionaryValue* pref_store_contents) { 132 if (!changed_paths_.empty()) { 133 base::TimeTicks checkpoint = base::TimeTicks::Now(); 134 { 135 scoped_ptr<PrefHashStoreTransaction> hash_store_transaction( 136 pref_hash_store_->BeginTransaction(scoped_ptr<HashStoreContents>( 137 new DictionaryHashStoreContents(pref_store_contents)))); 138 for (ChangedPathsMap::const_iterator it = changed_paths_.begin(); 139 it != changed_paths_.end(); ++it) { 140 const std::string& changed_path = it->first; 141 const TrackedPreference* changed_preference = it->second; 142 const base::Value* value = NULL; 143 pref_store_contents->Get(changed_path, &value); 144 changed_preference->OnNewValue(value, hash_store_transaction.get()); 145 } 146 changed_paths_.clear(); 147 } 148 // TODO(gab): Remove this histogram by Feb 21 2014; after sufficient timing 149 // data has been gathered from the wild to be confident this doesn't 150 // significantly affect performance on the UI thread. 151 UMA_HISTOGRAM_TIMES("Settings.FilterSerializeDataTime", 152 base::TimeTicks::Now() - checkpoint); 153 } 154 } 155 156 void PrefHashFilter::FinalizeFilterOnLoad( 157 const PostFilterOnLoadCallback& post_filter_on_load_callback, 158 scoped_ptr<base::DictionaryValue> pref_store_contents, 159 bool prefs_altered) { 160 DCHECK(pref_store_contents); 161 base::TimeTicks checkpoint = base::TimeTicks::Now(); 162 163 bool did_reset = false; 164 { 165 scoped_ptr<PrefHashStoreTransaction> hash_store_transaction( 166 pref_hash_store_->BeginTransaction(scoped_ptr<HashStoreContents>( 167 new DictionaryHashStoreContents(pref_store_contents.get())))); 168 if (report_super_mac_validity_) { 169 UMA_HISTOGRAM_BOOLEAN("Settings.HashesDictionaryTrusted", 170 hash_store_transaction->IsSuperMACValid()); 171 } 172 173 for (TrackedPreferencesMap::const_iterator it = tracked_paths_.begin(); 174 it != tracked_paths_.end(); ++it) { 175 if (it->second->EnforceAndReport(pref_store_contents.get(), 176 hash_store_transaction.get())) { 177 did_reset = true; 178 prefs_altered = true; 179 } 180 } 181 if (hash_store_transaction->StampSuperMac()) 182 prefs_altered = true; 183 } 184 185 if (did_reset) { 186 pref_store_contents->Set(prefs::kPreferenceResetTime, 187 new base::StringValue(base::Int64ToString( 188 base::Time::Now().ToInternalValue()))); 189 FilterUpdate(prefs::kPreferenceResetTime); 190 191 if (!on_reset_on_load_.is_null()) 192 on_reset_on_load_.Run(); 193 } 194 195 // TODO(gab): Remove this histogram by Feb 21 2014; after sufficient timing 196 // data has been gathered from the wild to be confident this doesn't 197 // significantly affect startup. 198 UMA_HISTOGRAM_TIMES("Settings.FilterOnLoadTime", 199 base::TimeTicks::Now() - checkpoint); 200 201 post_filter_on_load_callback.Run(pref_store_contents.Pass(), prefs_altered); 202 } 203