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.
5 #include "chrome/browser/prefs/pref_hash_filter.h"
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"
26 void 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",
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);
45 PrefHashFilter::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());
58 for (size_t i = 0; i < tracked_preferences.size(); ++i) {
59 const TrackedPreferenceMetadata& metadata = tracked_preferences[i];
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,
68 metadata.enforcement_level,
71 case TRACKING_STRATEGY_SPLIT:
72 tracked_preference.reset(
73 new TrackedSplitPreference(metadata.name,
74 metadata.reporting_id,
76 metadata.enforcement_level,
80 DCHECK(tracked_preference);
82 bool is_new = tracked_paths_.add(metadata.name,
83 tracked_preference.Pass()).second;
88 PrefHashFilter::~PrefHashFilter() {
89 // Ensure new values for all |changed_paths_| have been flushed to
90 // |pref_hash_store_| already.
91 DCHECK(changed_paths_.empty());
95 void 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);
105 base::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),
114 // Somehow the value stored on disk is not a valid int64.
118 return base::Time::FromInternalValue(internal_value);
122 void PrefHashFilter::ClearResetTime(PrefService* user_prefs) {
123 user_prefs->ClearPref(prefs::kPreferenceResetTime);
126 void 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());
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.
142 void 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));
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).
151 void PrefHashFilter::FilterSerializeData(
152 base::DictionaryValue* pref_store_contents) {
153 if (!changed_paths_.empty()) {
154 base::TimeTicks checkpoint = base::TimeTicks::Now();
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());
167 changed_paths_.clear();
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);
177 void 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();
184 bool did_reset = false;
186 scoped_ptr<PrefHashStoreTransaction> hash_store_transaction(
187 pref_hash_store_->BeginTransaction(scoped_ptr<HashStoreContents>(
188 new DictionaryHashStoreContents(pref_store_contents.get()))));
190 CleanupDeprecatedTrackedPreferences(
191 pref_store_contents.get(), hash_store_transaction.get());
193 if (report_super_mac_validity_) {
194 UMA_HISTOGRAM_BOOLEAN("Settings.HashesDictionaryTrusted",
195 hash_store_transaction->IsSuperMACValid());
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())) {
203 prefs_altered = true;
206 if (hash_store_transaction->StampSuperMac())
207 prefs_altered = true;
211 pref_store_contents->Set(prefs::kPreferenceResetTime,
212 new base::StringValue(base::Int64ToString(
213 base::Time::Now().ToInternalValue())));
214 FilterUpdate(prefs::kPreferenceResetTime);
216 if (!on_reset_on_load_.is_null())
217 on_reset_on_load_.Run();
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);
226 post_filter_on_load_callback.Run(pref_store_contents.Pass(), prefs_altered);