Home | History | Annotate | Download | only in prefs
      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.
      4 
      5 #include "chrome/browser/prefs/pref_hash_filter.h"
      6 
      7 #include <algorithm>
      8 
      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"
     23 
     24 namespace {
     25 
     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",
     34   };
     35 
     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);
     40   }
     41 }
     42 
     43 }  // namespace
     44 
     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());
     57 
     58   for (size_t i = 0; i < tracked_preferences.size(); ++i) {
     59     const TrackedPreferenceMetadata& metadata = tracked_preferences[i];
     60 
     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,
     67                                         reporting_ids_count,
     68                                         metadata.enforcement_level,
     69                                         delegate));
     70         break;
     71       case TRACKING_STRATEGY_SPLIT:
     72         tracked_preference.reset(
     73             new TrackedSplitPreference(metadata.name,
     74                                        metadata.reporting_id,
     75                                        reporting_ids_count,
     76                                        metadata.enforcement_level,
     77                                        delegate));
     78         break;
     79     }
     80     DCHECK(tracked_preference);
     81 
     82     bool is_new = tracked_paths_.add(metadata.name,
     83                                      tracked_preference.Pass()).second;
     84     DCHECK(is_new);
     85   }
     86 }
     87 
     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());
     92 }
     93 
     94 // static
     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);
    102 }
    103 
    104 // static
    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),
    113           &internal_value)) {
    114     // Somehow the value stored on disk is not a valid int64.
    115     NOTREACHED();
    116     return base::Time();
    117   }
    118   return base::Time::FromInternalValue(internal_value);
    119 }
    120 
    121 // static
    122 void PrefHashFilter::ClearResetTime(PrefService* user_prefs) {
    123   user_prefs->ClearPref(prefs::kPreferenceResetTime);
    124 }
    125 
    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());
    137   }
    138 }
    139 
    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));
    146 }
    147 
    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();
    155     {
    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());
    166       }
    167       changed_paths_.clear();
    168     }
    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);
    174   }
    175 }
    176 
    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();
    183 
    184   bool did_reset = false;
    185   {
    186     scoped_ptr<PrefHashStoreTransaction> hash_store_transaction(
    187         pref_hash_store_->BeginTransaction(scoped_ptr<HashStoreContents>(
    188             new DictionaryHashStoreContents(pref_store_contents.get()))));
    189 
    190     CleanupDeprecatedTrackedPreferences(
    191         pref_store_contents.get(), hash_store_transaction.get());
    192 
    193     if (report_super_mac_validity_) {
    194       UMA_HISTOGRAM_BOOLEAN("Settings.HashesDictionaryTrusted",
    195                             hash_store_transaction->IsSuperMACValid());
    196     }
    197 
    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())) {
    202         did_reset = true;
    203         prefs_altered = true;
    204       }
    205     }
    206     if (hash_store_transaction->StampSuperMac())
    207       prefs_altered = true;
    208   }
    209 
    210   if (did_reset) {
    211     pref_store_contents->Set(prefs::kPreferenceResetTime,
    212                              new base::StringValue(base::Int64ToString(
    213                                  base::Time::Now().ToInternalValue())));
    214     FilterUpdate(prefs::kPreferenceResetTime);
    215 
    216     if (!on_reset_on_load_.is_null())
    217       on_reset_on_load_.Run();
    218   }
    219 
    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);
    225 
    226   post_filter_on_load_callback.Run(pref_store_contents.Pass(), prefs_altered);
    227 }
    228