Home | History | Annotate | Download | only in tracked
      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|.
     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.
    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|.
    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|.
    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 
    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 
    240 TrackedPreferencesMigrator::~TrackedPreferencesMigrator() {}
    241 
    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 
    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 
    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