| OLD | NEW |
| (Empty) |
| 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/tracked/dictionary_hash_store_contents.h" | |
| 14 #include "chrome/browser/prefs/tracked/hash_store_contents.h" | |
| 15 #include "chrome/browser/prefs/tracked/interceptable_pref_filter.h" | |
| 16 #include "chrome/browser/prefs/tracked/pref_hash_store.h" | |
| 17 #include "chrome/browser/prefs/tracked/pref_hash_store_transaction.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 // 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; | |
| 202 } | |
| 203 } | |
| 204 } | |
| 205 } | |
| 206 | |
| 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)); | |
| 243 } | |
| 244 | |
| 245 TrackedPreferencesMigrator::~TrackedPreferencesMigrator() {} | |
| 246 | |
| 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; | |
| 261 } | |
| 262 | |
| 263 MigrateIfReady(); | |
| 264 } | |
| 265 | |
| 266 void TrackedPreferencesMigrator::MigrateIfReady() { | |
| 267 // Wait for both stores to have been read before proceeding. | |
| 268 if (!protected_prefs_ || !unprotected_prefs_) | |
| 269 return; | |
| 270 | |
| 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); | |
| 294 | |
| 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(); | |
| 309 } | |
| 310 | |
| 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); | |
| 316 | |
| 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); | |
| 326 } | |
| 327 | |
| 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); | |
| 337 } | |
| 338 } | |
| 339 | |
| 340 } // namespace | |
| 341 | |
| 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)); | |
| 370 } | |
| OLD | NEW |