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 PrefHashFilter::PrefHashFilter(
     25     scoped_ptr<PrefHashStore> pref_hash_store,
     26     const std::vector<TrackedPreferenceMetadata>& tracked_preferences,
     27     const base::Closure& on_reset_on_load,
     28     TrackedPreferenceValidationDelegate* delegate,
     29     size_t reporting_ids_count,
     30     bool report_super_mac_validity)
     31     : pref_hash_store_(pref_hash_store.Pass()),
     32       on_reset_on_load_(on_reset_on_load),
     33       report_super_mac_validity_(report_super_mac_validity) {
     34   DCHECK(pref_hash_store_);
     35   DCHECK_GE(reporting_ids_count, tracked_preferences.size());
     36 
     37   for (size_t i = 0; i < tracked_preferences.size(); ++i) {
     38     const TrackedPreferenceMetadata& metadata = tracked_preferences[i];
     39 
     40     scoped_ptr<TrackedPreference> tracked_preference;
     41     switch (metadata.strategy) {
     42       case TRACKING_STRATEGY_ATOMIC:
     43         tracked_preference.reset(
     44             new TrackedAtomicPreference(metadata.name,
     45                                         metadata.reporting_id,
     46                                         reporting_ids_count,
     47                                         metadata.enforcement_level,
     48                                         delegate));
     49         break;
     50       case TRACKING_STRATEGY_SPLIT:
     51         tracked_preference.reset(
     52             new TrackedSplitPreference(metadata.name,
     53                                        metadata.reporting_id,
     54                                        reporting_ids_count,
     55                                        metadata.enforcement_level,
     56                                        delegate));
     57         break;
     58     }
     59     DCHECK(tracked_preference);
     60 
     61     bool is_new = tracked_paths_.add(metadata.name,
     62                                      tracked_preference.Pass()).second;
     63     DCHECK(is_new);
     64   }
     65 }
     66 
     67 PrefHashFilter::~PrefHashFilter() {
     68   // Ensure new values for all |changed_paths_| have been flushed to
     69   // |pref_hash_store_| already.
     70   DCHECK(changed_paths_.empty());
     71 }
     72 
     73 // static
     74 void PrefHashFilter::RegisterProfilePrefs(
     75     user_prefs::PrefRegistrySyncable* registry) {
     76   // See GetResetTime for why this is a StringPref and not Int64Pref.
     77   registry->RegisterStringPref(
     78       prefs::kPreferenceResetTime,
     79       base::Int64ToString(base::Time().ToInternalValue()),
     80       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
     81 }
     82 
     83 // static
     84 base::Time PrefHashFilter::GetResetTime(PrefService* user_prefs) {
     85   // Provide our own implementation (identical to the PrefService::GetInt64) in
     86   // order to ensure it remains consistent with the way we store this value
     87   // (which we do via a PrefStore, preventing us from reusing
     88   // PrefService::SetInt64).
     89   int64 internal_value = base::Time().ToInternalValue();
     90   if (!base::StringToInt64(
     91           user_prefs->GetString(prefs::kPreferenceResetTime),
     92           &internal_value)) {
     93     // Somehow the value stored on disk is not a valid int64.
     94     NOTREACHED();
     95     return base::Time();
     96   }
     97   return base::Time::FromInternalValue(internal_value);
     98 }
     99 
    100 // static
    101 void PrefHashFilter::ClearResetTime(PrefService* user_prefs) {
    102   user_prefs->ClearPref(prefs::kPreferenceResetTime);
    103 }
    104 
    105 void PrefHashFilter::Initialize(base::DictionaryValue* pref_store_contents) {
    106   scoped_ptr<PrefHashStoreTransaction> hash_store_transaction(
    107       pref_hash_store_->BeginTransaction(scoped_ptr<HashStoreContents>(
    108           new DictionaryHashStoreContents(pref_store_contents))));
    109   for (TrackedPreferencesMap::const_iterator it = tracked_paths_.begin();
    110        it != tracked_paths_.end(); ++it) {
    111     const std::string& initialized_path = it->first;
    112     const TrackedPreference* initialized_preference = it->second;
    113     const base::Value* value = NULL;
    114     pref_store_contents->Get(initialized_path, &value);
    115     initialized_preference->OnNewValue(value, hash_store_transaction.get());
    116   }
    117 }
    118 
    119 // Marks |path| has having changed if it is part of |tracked_paths_|. A new hash
    120 // will be stored for it the next time FilterSerializeData() is invoked.
    121 void PrefHashFilter::FilterUpdate(const std::string& path) {
    122   TrackedPreferencesMap::const_iterator it = tracked_paths_.find(path);
    123   if (it != tracked_paths_.end())
    124     changed_paths_.insert(std::make_pair(path, it->second));
    125 }
    126 
    127 // Updates the stored hashes for |changed_paths_| before serializing data to
    128 // disk. This is required as storing the hash everytime a pref's value changes
    129 // is too expensive (see perf regression @ http://crbug.com/331273).
    130 void PrefHashFilter::FilterSerializeData(
    131     base::DictionaryValue* pref_store_contents) {
    132   if (!changed_paths_.empty()) {
    133     base::TimeTicks checkpoint = base::TimeTicks::Now();
    134     {
    135       scoped_ptr<PrefHashStoreTransaction> hash_store_transaction(
    136           pref_hash_store_->BeginTransaction(scoped_ptr<HashStoreContents>(
    137               new DictionaryHashStoreContents(pref_store_contents))));
    138       for (ChangedPathsMap::const_iterator it = changed_paths_.begin();
    139            it != changed_paths_.end(); ++it) {
    140         const std::string& changed_path = it->first;
    141         const TrackedPreference* changed_preference = it->second;
    142         const base::Value* value = NULL;
    143         pref_store_contents->Get(changed_path, &value);
    144         changed_preference->OnNewValue(value, hash_store_transaction.get());
    145       }
    146       changed_paths_.clear();
    147     }
    148     // TODO(gab): Remove this histogram by Feb 21 2014; after sufficient timing
    149     // data has been gathered from the wild to be confident this doesn't
    150     // significantly affect performance on the UI thread.
    151     UMA_HISTOGRAM_TIMES("Settings.FilterSerializeDataTime",
    152                         base::TimeTicks::Now() - checkpoint);
    153   }
    154 }
    155 
    156 void PrefHashFilter::FinalizeFilterOnLoad(
    157     const PostFilterOnLoadCallback& post_filter_on_load_callback,
    158     scoped_ptr<base::DictionaryValue> pref_store_contents,
    159     bool prefs_altered) {
    160   DCHECK(pref_store_contents);
    161   base::TimeTicks checkpoint = base::TimeTicks::Now();
    162 
    163   bool did_reset = false;
    164   {
    165     scoped_ptr<PrefHashStoreTransaction> hash_store_transaction(
    166         pref_hash_store_->BeginTransaction(scoped_ptr<HashStoreContents>(
    167             new DictionaryHashStoreContents(pref_store_contents.get()))));
    168     if (report_super_mac_validity_) {
    169       UMA_HISTOGRAM_BOOLEAN("Settings.HashesDictionaryTrusted",
    170                             hash_store_transaction->IsSuperMACValid());
    171     }
    172 
    173     for (TrackedPreferencesMap::const_iterator it = tracked_paths_.begin();
    174          it != tracked_paths_.end(); ++it) {
    175       if (it->second->EnforceAndReport(pref_store_contents.get(),
    176                                        hash_store_transaction.get())) {
    177         did_reset = true;
    178         prefs_altered = true;
    179       }
    180     }
    181     if (hash_store_transaction->StampSuperMac())
    182       prefs_altered = true;
    183   }
    184 
    185   if (did_reset) {
    186     pref_store_contents->Set(prefs::kPreferenceResetTime,
    187                              new base::StringValue(base::Int64ToString(
    188                                  base::Time::Now().ToInternalValue())));
    189     FilterUpdate(prefs::kPreferenceResetTime);
    190 
    191     if (!on_reset_on_load_.is_null())
    192       on_reset_on_load_.Run();
    193   }
    194 
    195   // TODO(gab): Remove this histogram by Feb 21 2014; after sufficient timing
    196   // data has been gathered from the wild to be confident this doesn't
    197   // significantly affect startup.
    198   UMA_HISTOGRAM_TIMES("Settings.FilterOnLoadTime",
    199                       base::TimeTicks::Now() - checkpoint);
    200 
    201   post_filter_on_load_callback.Run(pref_store_contents.Pass(), prefs_altered);
    202 }
    203