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