• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/prefs/tracked/tracked_preferences_migration.h"
6 
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 "chrome/browser/prefs/interceptable_pref_filter.h"
14 #include "chrome/browser/prefs/pref_hash_store.h"
15 #include "chrome/browser/prefs/pref_hash_store_transaction.h"
16 #include "chrome/browser/prefs/tracked/dictionary_hash_store_contents.h"
17 #include "chrome/browser/prefs/tracked/hash_store_contents.h"
18 
19 namespace {
20 
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);
40 
41  private:
42   friend class base::RefCounted<TrackedPreferencesMigrator>;
43 
44   enum PrefFilterID {
45     UNPROTECTED_PREF_FILTER,
46     PROTECTED_PREF_FILTER
47   };
48 
49   ~TrackedPreferencesMigrator();
50 
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);
58 
59   // Proceeds with migration if both |unprotected_prefs_| and |protected_prefs_|
60   // have been set.
61   void MigrateIfReady();
62 
63   const std::set<std::string> unprotected_pref_names_;
64   const std::set<std::string> protected_pref_names_;
65 
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_;
72 
73   InterceptablePrefFilter::FinalizeFilterOnLoadCallback
74       finalize_unprotected_filter_on_load_;
75   InterceptablePrefFilter::FinalizeFilterOnLoadCallback
76       finalize_protected_filter_on_load_;
77 
78   scoped_ptr<PrefHashStore> unprotected_pref_hash_store_;
79   scoped_ptr<PrefHashStore> protected_pref_hash_store_;
80   scoped_ptr<HashStoreContents> legacy_pref_hash_store_;
81 
82   scoped_ptr<base::DictionaryValue> unprotected_prefs_;
83   scoped_ptr<base::DictionaryValue> protected_prefs_;
84 
85   DISALLOW_COPY_AND_ASSIGN(TrackedPreferencesMigrator);
86 };
87 
88 // Invokes |store_cleaner| for every |keys_to_clean|.
CleanupPrefStore(const base::Callback<void (const std::string & key)> & store_cleaner,const std::set<std::string> & 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);
95   }
96 }
97 
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.
ScheduleSourcePrefStoreCleanup(const base::Callback<void (const base::Closure &)> & register_on_successful_destination_store_write_callback,const base::Callback<void (const std::string & key)> & source_store_cleaner,const std::set<std::string> & keys_to_clean,bool wait_for_commit_to_destination_store)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);
115   }
116 }
117 
118 // Removes hashes for |migrated_pref_names| from |origin_pref_store| using
119 // the configuration/implementation in |origin_pref_hash_store|.
CleanupMigratedHashes(const std::set<std::string> & migrated_pref_names,PrefHashStore * origin_pref_hash_store,base::DictionaryValue * origin_pref_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);
130   }
131 }
132 
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|.
MigratePrefsFromOldToNewStore(const std::set<std::string> & pref_names,base::DictionaryValue * old_store,base::DictionaryValue * new_store,PrefHashStore * new_hash_store,HashStoreContents * legacy_hash_store,bool * old_store_needs_cleanup,bool * new_store_altered,bool * used_legacy_pref_hashes)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))));
152 
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;
158 
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;
169 
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;
179       }
180     }
181 
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;
190       }
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         new_hash_store_transaction->ClearHash(pref_name);
196         *new_store_altered = true;
197       }
198     }
199   }
200 }
201 
TrackedPreferencesMigrator(const std::set<std::string> & unprotected_pref_names,const std::set<std::string> & protected_pref_names,const base::Callback<void (const std::string & key)> & unprotected_store_cleaner,const base::Callback<void (const std::string & key)> & protected_store_cleaner,const base::Callback<void (const base::Closure &)> & register_on_successful_unprotected_store_write_callback,const base::Callback<void (const base::Closure &)> & register_on_successful_protected_store_write_callback,scoped_ptr<PrefHashStore> unprotected_pref_hash_store,scoped_ptr<PrefHashStore> protected_pref_hash_store,scoped_ptr<HashStoreContents> legacy_pref_hash_store,InterceptablePrefFilter * unprotected_pref_filter,InterceptablePrefFilter * protected_pref_filter)202 TrackedPreferencesMigrator::TrackedPreferencesMigrator(
203     const std::set<std::string>& unprotected_pref_names,
204     const std::set<std::string>& protected_pref_names,
205     const base::Callback<void(const std::string& key)>&
206         unprotected_store_cleaner,
207     const base::Callback<void(const std::string& key)>& protected_store_cleaner,
208     const base::Callback<void(const base::Closure&)>&
209         register_on_successful_unprotected_store_write_callback,
210     const base::Callback<void(const base::Closure&)>&
211         register_on_successful_protected_store_write_callback,
212     scoped_ptr<PrefHashStore> unprotected_pref_hash_store,
213     scoped_ptr<PrefHashStore> protected_pref_hash_store,
214     scoped_ptr<HashStoreContents> legacy_pref_hash_store,
215     InterceptablePrefFilter* unprotected_pref_filter,
216     InterceptablePrefFilter* protected_pref_filter)
217     : unprotected_pref_names_(unprotected_pref_names),
218       protected_pref_names_(protected_pref_names),
219       unprotected_store_cleaner_(unprotected_store_cleaner),
220       protected_store_cleaner_(protected_store_cleaner),
221       register_on_successful_unprotected_store_write_callback_(
222           register_on_successful_unprotected_store_write_callback),
223       register_on_successful_protected_store_write_callback_(
224           register_on_successful_protected_store_write_callback),
225       unprotected_pref_hash_store_(unprotected_pref_hash_store.Pass()),
226       protected_pref_hash_store_(protected_pref_hash_store.Pass()),
227       legacy_pref_hash_store_(legacy_pref_hash_store.Pass()) {
228   // The callbacks bound below will own this TrackedPreferencesMigrator by
229   // reference.
230   unprotected_pref_filter->InterceptNextFilterOnLoad(
231       base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad,
232                  this,
233                  UNPROTECTED_PREF_FILTER));
234   protected_pref_filter->InterceptNextFilterOnLoad(
235       base::Bind(&TrackedPreferencesMigrator::InterceptFilterOnLoad,
236                  this,
237                  PROTECTED_PREF_FILTER));
238 }
239 
~TrackedPreferencesMigrator()240 TrackedPreferencesMigrator::~TrackedPreferencesMigrator() {}
241 
InterceptFilterOnLoad(PrefFilterID id,const InterceptablePrefFilter::FinalizeFilterOnLoadCallback & finalize_filter_on_load,scoped_ptr<base::DictionaryValue> prefs)242 void TrackedPreferencesMigrator::InterceptFilterOnLoad(
243     PrefFilterID id,
244     const InterceptablePrefFilter::FinalizeFilterOnLoadCallback&
245         finalize_filter_on_load,
246     scoped_ptr<base::DictionaryValue> prefs) {
247   switch (id) {
248     case UNPROTECTED_PREF_FILTER:
249       finalize_unprotected_filter_on_load_ = finalize_filter_on_load;
250       unprotected_prefs_ = prefs.Pass();
251       break;
252     case PROTECTED_PREF_FILTER:
253       finalize_protected_filter_on_load_ = finalize_filter_on_load;
254       protected_prefs_ = prefs.Pass();
255       break;
256   }
257 
258   MigrateIfReady();
259 }
260 
MigrateIfReady()261 void TrackedPreferencesMigrator::MigrateIfReady() {
262   // Wait for both stores to have been read before proceeding.
263   if (!protected_prefs_ || !unprotected_prefs_)
264     return;
265 
266   bool used_legacy_pref_hashes = false;
267   bool protected_prefs_need_cleanup = false;
268   bool unprotected_prefs_altered = false;
269   MigratePrefsFromOldToNewStore(unprotected_pref_names_,
270                                 protected_prefs_.get(),
271                                 unprotected_prefs_.get(),
272                                 unprotected_pref_hash_store_.get(),
273                                 legacy_pref_hash_store_.get(),
274                                 &protected_prefs_need_cleanup,
275                                 &unprotected_prefs_altered,
276                                 &used_legacy_pref_hashes);
277   bool unprotected_prefs_need_cleanup = false;
278   bool protected_prefs_altered = false;
279   MigratePrefsFromOldToNewStore(protected_pref_names_,
280                                 unprotected_prefs_.get(),
281                                 protected_prefs_.get(),
282                                 protected_pref_hash_store_.get(),
283                                 legacy_pref_hash_store_.get(),
284                                 &unprotected_prefs_need_cleanup,
285                                 &protected_prefs_altered,
286                                 &used_legacy_pref_hashes);
287   UMA_HISTOGRAM_BOOLEAN("Settings.MigratedHashesFromLocalState",
288                         used_legacy_pref_hashes);
289 
290   if (!unprotected_prefs_altered && !protected_prefs_altered) {
291     // Clean up any MACs that might have been previously migrated from the
292     // various stores. It's safe to leave them behind for a little while as they
293     // will be ignored unless the corresponding value is _also_ present. The
294     // cleanup must be deferred until the MACs have been written to their target
295     // stores, and doing so in a subsequent launch is easier than within the
296     // same process.
297     CleanupMigratedHashes(unprotected_pref_names_,
298                           protected_pref_hash_store_.get(),
299                           protected_prefs_.get());
300     CleanupMigratedHashes(protected_pref_names_,
301                           unprotected_pref_hash_store_.get(),
302                           unprotected_prefs_.get());
303     legacy_pref_hash_store_->Reset();
304   }
305 
306   // Hand the processed prefs back to their respective filters.
307   finalize_unprotected_filter_on_load_.Run(unprotected_prefs_.Pass(),
308                                            unprotected_prefs_altered);
309   finalize_protected_filter_on_load_.Run(protected_prefs_.Pass(),
310                                          protected_prefs_altered);
311 
312   if (unprotected_prefs_need_cleanup) {
313     // Schedule a cleanup of the |protected_pref_names_| from the unprotected
314     // prefs once the protected prefs were successfully written to disk (or
315     // do it immediately if |!protected_prefs_altered|).
316     ScheduleSourcePrefStoreCleanup(
317         register_on_successful_protected_store_write_callback_,
318         unprotected_store_cleaner_,
319         protected_pref_names_,
320         protected_prefs_altered);
321   }
322 
323   if (protected_prefs_need_cleanup) {
324     // Schedule a cleanup of the |unprotected_pref_names_| from the protected
325     // prefs once the unprotected prefs were successfully written to disk (or
326     // do it immediately if |!unprotected_prefs_altered|).
327     ScheduleSourcePrefStoreCleanup(
328         register_on_successful_unprotected_store_write_callback_,
329         protected_store_cleaner_,
330         unprotected_pref_names_,
331         unprotected_prefs_altered);
332   }
333 }
334 
335 }  // namespace
336 
SetupTrackedPreferencesMigration(const std::set<std::string> & unprotected_pref_names,const std::set<std::string> & protected_pref_names,const base::Callback<void (const std::string & key)> & unprotected_store_cleaner,const base::Callback<void (const std::string & key)> & protected_store_cleaner,const base::Callback<void (const base::Closure &)> & register_on_successful_unprotected_store_write_callback,const base::Callback<void (const base::Closure &)> & register_on_successful_protected_store_write_callback,scoped_ptr<PrefHashStore> unprotected_pref_hash_store,scoped_ptr<PrefHashStore> protected_pref_hash_store,scoped_ptr<HashStoreContents> legacy_pref_hash_store,InterceptablePrefFilter * unprotected_pref_filter,InterceptablePrefFilter * protected_pref_filter)337 void SetupTrackedPreferencesMigration(
338     const std::set<std::string>& unprotected_pref_names,
339     const std::set<std::string>& protected_pref_names,
340     const base::Callback<void(const std::string& key)>&
341         unprotected_store_cleaner,
342     const base::Callback<void(const std::string& key)>& protected_store_cleaner,
343     const base::Callback<void(const base::Closure&)>&
344         register_on_successful_unprotected_store_write_callback,
345     const base::Callback<void(const base::Closure&)>&
346         register_on_successful_protected_store_write_callback,
347     scoped_ptr<PrefHashStore> unprotected_pref_hash_store,
348     scoped_ptr<PrefHashStore> protected_pref_hash_store,
349     scoped_ptr<HashStoreContents> legacy_pref_hash_store,
350     InterceptablePrefFilter* unprotected_pref_filter,
351     InterceptablePrefFilter* protected_pref_filter) {
352   scoped_refptr<TrackedPreferencesMigrator> prefs_migrator(
353       new TrackedPreferencesMigrator(
354           unprotected_pref_names,
355           protected_pref_names,
356           unprotected_store_cleaner,
357           protected_store_cleaner,
358           register_on_successful_unprotected_store_write_callback,
359           register_on_successful_protected_store_write_callback,
360           unprotected_pref_hash_store.Pass(),
361           protected_pref_hash_store.Pass(),
362           legacy_pref_hash_store.Pass(),
363           unprotected_pref_filter,
364           protected_pref_filter));
365 }
366