Roll src/third_party/WebKit 9f7fb92:f103b33 (svn 202621:202622)
[chromium-blink-merge.git] / components / user_prefs / tracked / tracked_preferences_migration.cc
blobedcbae512c5b7f271273f9fc2248605a4c78cda2
1 // Copyright 2014 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 "components/user_prefs/tracked/tracked_preferences_migration.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/macros.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/metrics/histogram.h"
12 #include "base/values.h"
13 #include "components/user_prefs/tracked/dictionary_hash_store_contents.h"
14 #include "components/user_prefs/tracked/hash_store_contents.h"
15 #include "components/user_prefs/tracked/interceptable_pref_filter.h"
16 #include "components/user_prefs/tracked/pref_hash_store.h"
17 #include "components/user_prefs/tracked/pref_hash_store_transaction.h"
19 namespace {
21 class TrackedPreferencesMigrator
22 : public base::RefCounted<TrackedPreferencesMigrator> {
23 public:
24 TrackedPreferencesMigrator(
25 const std::set<std::string>& unprotected_pref_names,
26 const std::set<std::string>& protected_pref_names,
27 const base::Callback<void(const std::string& key)>&
28 unprotected_store_cleaner,
29 const base::Callback<void(const std::string& key)>&
30 protected_store_cleaner,
31 const base::Callback<void(const base::Closure&)>&
32 register_on_successful_unprotected_store_write_callback,
33 const base::Callback<void(const base::Closure&)>&
34 register_on_successful_protected_store_write_callback,
35 scoped_ptr<PrefHashStore> unprotected_pref_hash_store,
36 scoped_ptr<PrefHashStore> protected_pref_hash_store,
37 scoped_ptr<HashStoreContents> legacy_pref_hash_store,
38 InterceptablePrefFilter* unprotected_pref_filter,
39 InterceptablePrefFilter* protected_pref_filter);
41 private:
42 friend class base::RefCounted<TrackedPreferencesMigrator>;
44 enum PrefFilterID {
45 UNPROTECTED_PREF_FILTER,
46 PROTECTED_PREF_FILTER
49 ~TrackedPreferencesMigrator();
51 // Stores the data coming in from the filter identified by |id| into this
52 // class and then calls MigrateIfReady();
53 void InterceptFilterOnLoad(
54 PrefFilterID id,
55 const InterceptablePrefFilter::FinalizeFilterOnLoadCallback&
56 finalize_filter_on_load,
57 scoped_ptr<base::DictionaryValue> prefs);
59 // Proceeds with migration if both |unprotected_prefs_| and |protected_prefs_|
60 // have been set.
61 void MigrateIfReady();
63 const std::set<std::string> unprotected_pref_names_;
64 const std::set<std::string> protected_pref_names_;
66 const base::Callback<void(const std::string& key)> unprotected_store_cleaner_;
67 const base::Callback<void(const std::string& key)> protected_store_cleaner_;
68 const base::Callback<void(const base::Closure&)>
69 register_on_successful_unprotected_store_write_callback_;
70 const base::Callback<void(const base::Closure&)>
71 register_on_successful_protected_store_write_callback_;
73 InterceptablePrefFilter::FinalizeFilterOnLoadCallback
74 finalize_unprotected_filter_on_load_;
75 InterceptablePrefFilter::FinalizeFilterOnLoadCallback
76 finalize_protected_filter_on_load_;
78 scoped_ptr<PrefHashStore> unprotected_pref_hash_store_;
79 scoped_ptr<PrefHashStore> protected_pref_hash_store_;
80 scoped_ptr<HashStoreContents> legacy_pref_hash_store_;
82 scoped_ptr<base::DictionaryValue> unprotected_prefs_;
83 scoped_ptr<base::DictionaryValue> protected_prefs_;
85 DISALLOW_COPY_AND_ASSIGN(TrackedPreferencesMigrator);
88 // Invokes |store_cleaner| for every |keys_to_clean|.
89 void CleanupPrefStore(
90 const base::Callback<void(const std::string& key)>& store_cleaner,
91 const std::set<std::string>& keys_to_clean) {
92 for (std::set<std::string>::const_iterator it = keys_to_clean.begin();
93 it != keys_to_clean.end(); ++it) {
94 store_cleaner.Run(*it);
98 // If |wait_for_commit_to_destination_store|: schedules (via
99 // |register_on_successful_destination_store_write_callback|) a cleanup of the
100 // |keys_to_clean| from the source pref store (through |source_store_cleaner|)
101 // once the destination pref store they were migrated to was successfully
102 // written to disk. Otherwise, executes the cleanup right away.
103 void ScheduleSourcePrefStoreCleanup(
104 const base::Callback<void(const base::Closure&)>&
105 register_on_successful_destination_store_write_callback,
106 const base::Callback<void(const std::string& key)>& source_store_cleaner,
107 const std::set<std::string>& keys_to_clean,
108 bool wait_for_commit_to_destination_store) {
109 DCHECK(!keys_to_clean.empty());
110 if (wait_for_commit_to_destination_store) {
111 register_on_successful_destination_store_write_callback.Run(
112 base::Bind(&CleanupPrefStore, source_store_cleaner, keys_to_clean));
113 } else {
114 CleanupPrefStore(source_store_cleaner, keys_to_clean);
118 // Removes hashes for |migrated_pref_names| from |origin_pref_store| using
119 // the configuration/implementation in |origin_pref_hash_store|.
120 void CleanupMigratedHashes(const std::set<std::string>& migrated_pref_names,
121 PrefHashStore* origin_pref_hash_store,
122 base::DictionaryValue* origin_pref_store) {
123 scoped_ptr<PrefHashStoreTransaction> transaction(
124 origin_pref_hash_store->BeginTransaction(scoped_ptr<HashStoreContents>(
125 new DictionaryHashStoreContents(origin_pref_store))));
126 for (std::set<std::string>::const_iterator it = migrated_pref_names.begin();
127 it != migrated_pref_names.end();
128 ++it) {
129 transaction->ClearHash(*it);
133 // Copies the value of each pref in |pref_names| which is set in |old_store|,
134 // but not in |new_store| into |new_store|. Sets |old_store_needs_cleanup| to
135 // true if any old duplicates remain in |old_store| and sets |new_store_altered|
136 // to true if any value was copied to |new_store|.
137 void MigratePrefsFromOldToNewStore(const std::set<std::string>& pref_names,
138 base::DictionaryValue* old_store,
139 base::DictionaryValue* new_store,
140 PrefHashStore* new_hash_store,
141 HashStoreContents* legacy_hash_store,
142 bool* old_store_needs_cleanup,
143 bool* new_store_altered,
144 bool* used_legacy_pref_hashes) {
145 const base::DictionaryValue* old_hash_store_contents =
146 DictionaryHashStoreContents(old_store).GetContents();
147 const base::DictionaryValue* legacy_hash_store_contents =
148 legacy_hash_store->GetContents();
149 scoped_ptr<PrefHashStoreTransaction> new_hash_store_transaction(
150 new_hash_store->BeginTransaction(scoped_ptr<HashStoreContents>(
151 new DictionaryHashStoreContents(new_store))));
153 for (std::set<std::string>::const_iterator it = pref_names.begin();
154 it != pref_names.end();
155 ++it) {
156 const std::string& pref_name = *it;
157 const base::Value* value_in_old_store = NULL;
159 // If the destination does not have a hash for this pref we will
160 // unconditionally attempt to move it.
161 bool destination_hash_missing =
162 !new_hash_store_transaction->HasHash(pref_name);
163 // If we migrate the value we will also attempt to migrate the hash.
164 bool migrated_value = false;
165 if (old_store->Get(pref_name, &value_in_old_store)) {
166 // Whether this value ends up being copied below or was left behind by a
167 // previous incomplete migration, it should be cleaned up.
168 *old_store_needs_cleanup = true;
170 if (!new_store->Get(pref_name, NULL)) {
171 // Copy the value from |old_store| to |new_store| rather than moving it
172 // to avoid data loss should |old_store| be flushed to disk without
173 // |new_store| having equivalently been successfully flushed to disk
174 // (e.g., on crash or in cases where |new_store| is read-only following
175 // a read error on startup).
176 new_store->Set(pref_name, value_in_old_store->DeepCopy());
177 migrated_value = true;
178 *new_store_altered = true;
182 if (destination_hash_missing || migrated_value) {
183 const base::Value* old_hash = NULL;
184 if (old_hash_store_contents)
185 old_hash_store_contents->Get(pref_name, &old_hash);
186 if (!old_hash && legacy_hash_store_contents) {
187 legacy_hash_store_contents->Get(pref_name, &old_hash);
188 if (old_hash)
189 *used_legacy_pref_hashes = true;
191 if (old_hash) {
192 new_hash_store_transaction->ImportHash(pref_name, old_hash);
193 *new_store_altered = true;
194 } else if (!destination_hash_missing) {
195 // Do not allow values to be migrated without MACs if the destination
196 // already has a MAC (http://crbug.com/414554). Remove the migrated
197 // value in order to provide the same no-op behaviour as if the pref was
198 // added to the wrong file when there was already a value for
199 // |pref_name| in |new_store|.
200 new_store->Remove(pref_name, NULL);
201 *new_store_altered = true;
207 TrackedPreferencesMigrator::TrackedPreferencesMigrator(
208 const std::set<std::string>& unprotected_pref_names,
209 const std::set<std::string>& protected_pref_names,
210 const base::Callback<void(const std::string& key)>&
211 unprotected_store_cleaner,
212 const base::Callback<void(const std::string& key)>& protected_store_cleaner,
213 const base::Callback<void(const base::Closure&)>&
214 register_on_successful_unprotected_store_write_callback,
215 const base::Callback<void(const base::Closure&)>&
216 register_on_successful_protected_store_write_callback,
217 scoped_ptr<PrefHashStore> unprotected_pref_hash_store,
218 scoped_ptr<PrefHashStore> protected_pref_hash_store,
219 scoped_ptr<HashStoreContents> legacy_pref_hash_store,
220 InterceptablePrefFilter* unprotected_pref_filter,
221 InterceptablePrefFilter* protected_pref_filter)
222 : unprotected_pref_names_(unprotected_pref_names),
223 protected_pref_names_(protected_pref_names),
224 unprotected_store_cleaner_(unprotected_store_cleaner),
225 protected_store_cleaner_(protected_store_cleaner),
226 register_on_successful_unprotected_store_write_callback_(
227 register_on_successful_unprotected_store_write_callback),
228 register_on_successful_protected_store_write_callback_(
229 register_on_successful_protected_store_write_callback),
230 unprotected_pref_hash_store_(unprotected_pref_hash_store.Pass()),
231 protected_pref_hash_store_(protected_pref_hash_store.Pass()),
232 legacy_pref_hash_store_(legacy_pref_hash_store.Pass()) {
233 // The callbacks bound below will own this TrackedPreferencesMigrator by
234 // reference.
235 unprotected_pref_filter->InterceptNextFilterOnLoad(
236 base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad,
237 this,
238 UNPROTECTED_PREF_FILTER));
239 protected_pref_filter->InterceptNextFilterOnLoad(
240 base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad,
241 this,
242 PROTECTED_PREF_FILTER));
245 TrackedPreferencesMigrator::~TrackedPreferencesMigrator() {}
247 void TrackedPreferencesMigrator::InterceptFilterOnLoad(
248 PrefFilterID id,
249 const InterceptablePrefFilter::FinalizeFilterOnLoadCallback&
250 finalize_filter_on_load,
251 scoped_ptr<base::DictionaryValue> prefs) {
252 switch (id) {
253 case UNPROTECTED_PREF_FILTER:
254 finalize_unprotected_filter_on_load_ = finalize_filter_on_load;
255 unprotected_prefs_ = prefs.Pass();
256 break;
257 case PROTECTED_PREF_FILTER:
258 finalize_protected_filter_on_load_ = finalize_filter_on_load;
259 protected_prefs_ = prefs.Pass();
260 break;
263 MigrateIfReady();
266 void TrackedPreferencesMigrator::MigrateIfReady() {
267 // Wait for both stores to have been read before proceeding.
268 if (!protected_prefs_ || !unprotected_prefs_)
269 return;
271 bool used_legacy_pref_hashes = false;
272 bool protected_prefs_need_cleanup = false;
273 bool unprotected_prefs_altered = false;
274 MigratePrefsFromOldToNewStore(unprotected_pref_names_,
275 protected_prefs_.get(),
276 unprotected_prefs_.get(),
277 unprotected_pref_hash_store_.get(),
278 legacy_pref_hash_store_.get(),
279 &protected_prefs_need_cleanup,
280 &unprotected_prefs_altered,
281 &used_legacy_pref_hashes);
282 bool unprotected_prefs_need_cleanup = false;
283 bool protected_prefs_altered = false;
284 MigratePrefsFromOldToNewStore(protected_pref_names_,
285 unprotected_prefs_.get(),
286 protected_prefs_.get(),
287 protected_pref_hash_store_.get(),
288 legacy_pref_hash_store_.get(),
289 &unprotected_prefs_need_cleanup,
290 &protected_prefs_altered,
291 &used_legacy_pref_hashes);
292 UMA_HISTOGRAM_BOOLEAN("Settings.MigratedHashesFromLocalState",
293 used_legacy_pref_hashes);
295 if (!unprotected_prefs_altered && !protected_prefs_altered) {
296 // Clean up any MACs that might have been previously migrated from the
297 // various stores. It's safe to leave them behind for a little while as they
298 // will be ignored unless the corresponding value is _also_ present. The
299 // cleanup must be deferred until the MACs have been written to their target
300 // stores, and doing so in a subsequent launch is easier than within the
301 // same process.
302 CleanupMigratedHashes(unprotected_pref_names_,
303 protected_pref_hash_store_.get(),
304 protected_prefs_.get());
305 CleanupMigratedHashes(protected_pref_names_,
306 unprotected_pref_hash_store_.get(),
307 unprotected_prefs_.get());
308 legacy_pref_hash_store_->Reset();
311 // Hand the processed prefs back to their respective filters.
312 finalize_unprotected_filter_on_load_.Run(unprotected_prefs_.Pass(),
313 unprotected_prefs_altered);
314 finalize_protected_filter_on_load_.Run(protected_prefs_.Pass(),
315 protected_prefs_altered);
317 if (unprotected_prefs_need_cleanup) {
318 // Schedule a cleanup of the |protected_pref_names_| from the unprotected
319 // prefs once the protected prefs were successfully written to disk (or
320 // do it immediately if |!protected_prefs_altered|).
321 ScheduleSourcePrefStoreCleanup(
322 register_on_successful_protected_store_write_callback_,
323 unprotected_store_cleaner_,
324 protected_pref_names_,
325 protected_prefs_altered);
328 if (protected_prefs_need_cleanup) {
329 // Schedule a cleanup of the |unprotected_pref_names_| from the protected
330 // prefs once the unprotected prefs were successfully written to disk (or
331 // do it immediately if |!unprotected_prefs_altered|).
332 ScheduleSourcePrefStoreCleanup(
333 register_on_successful_unprotected_store_write_callback_,
334 protected_store_cleaner_,
335 unprotected_pref_names_,
336 unprotected_prefs_altered);
340 } // namespace
342 void SetupTrackedPreferencesMigration(
343 const std::set<std::string>& unprotected_pref_names,
344 const std::set<std::string>& protected_pref_names,
345 const base::Callback<void(const std::string& key)>&
346 unprotected_store_cleaner,
347 const base::Callback<void(const std::string& key)>& protected_store_cleaner,
348 const base::Callback<void(const base::Closure&)>&
349 register_on_successful_unprotected_store_write_callback,
350 const base::Callback<void(const base::Closure&)>&
351 register_on_successful_protected_store_write_callback,
352 scoped_ptr<PrefHashStore> unprotected_pref_hash_store,
353 scoped_ptr<PrefHashStore> protected_pref_hash_store,
354 scoped_ptr<HashStoreContents> legacy_pref_hash_store,
355 InterceptablePrefFilter* unprotected_pref_filter,
356 InterceptablePrefFilter* protected_pref_filter) {
357 scoped_refptr<TrackedPreferencesMigrator> prefs_migrator(
358 new TrackedPreferencesMigrator(
359 unprotected_pref_names,
360 protected_pref_names,
361 unprotected_store_cleaner,
362 protected_store_cleaner,
363 register_on_successful_unprotected_store_write_callback,
364 register_on_successful_protected_store_write_callback,
365 unprotected_pref_hash_store.Pass(),
366 protected_pref_hash_store.Pass(),
367 legacy_pref_hash_store.Pass(),
368 unprotected_pref_filter,
369 protected_pref_filter));