blob: 9c2a62e6784890f56559158599f24238e26c4fea [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/prefs/pref_hash_filter.h"
#include <algorithm>
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "base/prefs/pref_store.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/prefs/pref_hash_store.h"
#include "chrome/browser/prefs/pref_hash_store_transaction.h"
#include "chrome/browser/prefs/tracked/dictionary_hash_store_contents.h"
#include "chrome/browser/prefs/tracked/tracked_atomic_preference.h"
#include "chrome/browser/prefs/tracked/tracked_split_preference.h"
#include "chrome/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
PrefHashFilter::PrefHashFilter(
scoped_ptr<PrefHashStore> pref_hash_store,
const std::vector<TrackedPreferenceMetadata>& tracked_preferences,
const base::Closure& on_reset_on_load,
TrackedPreferenceValidationDelegate* delegate,
size_t reporting_ids_count,
bool report_super_mac_validity)
: pref_hash_store_(pref_hash_store.Pass()),
on_reset_on_load_(on_reset_on_load),
report_super_mac_validity_(report_super_mac_validity) {
DCHECK(pref_hash_store_);
DCHECK_GE(reporting_ids_count, tracked_preferences.size());
for (size_t i = 0; i < tracked_preferences.size(); ++i) {
const TrackedPreferenceMetadata& metadata = tracked_preferences[i];
scoped_ptr<TrackedPreference> tracked_preference;
switch (metadata.strategy) {
case TRACKING_STRATEGY_ATOMIC:
tracked_preference.reset(
new TrackedAtomicPreference(metadata.name,
metadata.reporting_id,
reporting_ids_count,
metadata.enforcement_level,
delegate));
break;
case TRACKING_STRATEGY_SPLIT:
tracked_preference.reset(
new TrackedSplitPreference(metadata.name,
metadata.reporting_id,
reporting_ids_count,
metadata.enforcement_level,
delegate));
break;
}
DCHECK(tracked_preference);
bool is_new = tracked_paths_.add(metadata.name,
tracked_preference.Pass()).second;
DCHECK(is_new);
}
}
PrefHashFilter::~PrefHashFilter() {
// Ensure new values for all |changed_paths_| have been flushed to
// |pref_hash_store_| already.
DCHECK(changed_paths_.empty());
}
// static
void PrefHashFilter::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
// See GetResetTime for why this is a StringPref and not Int64Pref.
registry->RegisterStringPref(
prefs::kPreferenceResetTime,
base::Int64ToString(base::Time().ToInternalValue()),
user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
}
// static
base::Time PrefHashFilter::GetResetTime(PrefService* user_prefs) {
// Provide our own implementation (identical to the PrefService::GetInt64) in
// order to ensure it remains consistent with the way we store this value
// (which we do via a PrefStore, preventing us from reusing
// PrefService::SetInt64).
int64 internal_value = base::Time().ToInternalValue();
if (!base::StringToInt64(
user_prefs->GetString(prefs::kPreferenceResetTime),
&internal_value)) {
// Somehow the value stored on disk is not a valid int64.
NOTREACHED();
return base::Time();
}
return base::Time::FromInternalValue(internal_value);
}
// static
void PrefHashFilter::ClearResetTime(PrefService* user_prefs) {
user_prefs->ClearPref(prefs::kPreferenceResetTime);
}
void PrefHashFilter::Initialize(base::DictionaryValue* pref_store_contents) {
scoped_ptr<PrefHashStoreTransaction> hash_store_transaction(
pref_hash_store_->BeginTransaction(scoped_ptr<HashStoreContents>(
new DictionaryHashStoreContents(pref_store_contents))));
for (TrackedPreferencesMap::const_iterator it = tracked_paths_.begin();
it != tracked_paths_.end(); ++it) {
const std::string& initialized_path = it->first;
const TrackedPreference* initialized_preference = it->second;
const base::Value* value = NULL;
pref_store_contents->Get(initialized_path, &value);
initialized_preference->OnNewValue(value, hash_store_transaction.get());
}
}
// Marks |path| has having changed if it is part of |tracked_paths_|. A new hash
// will be stored for it the next time FilterSerializeData() is invoked.
void PrefHashFilter::FilterUpdate(const std::string& path) {
TrackedPreferencesMap::const_iterator it = tracked_paths_.find(path);
if (it != tracked_paths_.end())
changed_paths_.insert(std::make_pair(path, it->second));
}
// Updates the stored hashes for |changed_paths_| before serializing data to
// disk. This is required as storing the hash everytime a pref's value changes
// is too expensive (see perf regression @ http://crbug.com/331273).
void PrefHashFilter::FilterSerializeData(
base::DictionaryValue* pref_store_contents) {
if (!changed_paths_.empty()) {
base::TimeTicks checkpoint = base::TimeTicks::Now();
{
scoped_ptr<PrefHashStoreTransaction> hash_store_transaction(
pref_hash_store_->BeginTransaction(scoped_ptr<HashStoreContents>(
new DictionaryHashStoreContents(pref_store_contents))));
for (ChangedPathsMap::const_iterator it = changed_paths_.begin();
it != changed_paths_.end(); ++it) {
const std::string& changed_path = it->first;
const TrackedPreference* changed_preference = it->second;
const base::Value* value = NULL;
pref_store_contents->Get(changed_path, &value);
changed_preference->OnNewValue(value, hash_store_transaction.get());
}
changed_paths_.clear();
}
// TODO(gab): Remove this histogram by Feb 21 2014; after sufficient timing
// data has been gathered from the wild to be confident this doesn't
// significantly affect performance on the UI thread.
UMA_HISTOGRAM_TIMES("Settings.FilterSerializeDataTime",
base::TimeTicks::Now() - checkpoint);
}
}
void PrefHashFilter::FinalizeFilterOnLoad(
const PostFilterOnLoadCallback& post_filter_on_load_callback,
scoped_ptr<base::DictionaryValue> pref_store_contents,
bool prefs_altered) {
DCHECK(pref_store_contents);
base::TimeTicks checkpoint = base::TimeTicks::Now();
bool did_reset = false;
{
scoped_ptr<PrefHashStoreTransaction> hash_store_transaction(
pref_hash_store_->BeginTransaction(scoped_ptr<HashStoreContents>(
new DictionaryHashStoreContents(pref_store_contents.get()))));
if (report_super_mac_validity_) {
UMA_HISTOGRAM_BOOLEAN("Settings.HashesDictionaryTrusted",
hash_store_transaction->IsSuperMACValid());
}
for (TrackedPreferencesMap::const_iterator it = tracked_paths_.begin();
it != tracked_paths_.end(); ++it) {
if (it->second->EnforceAndReport(pref_store_contents.get(),
hash_store_transaction.get())) {
did_reset = true;
prefs_altered = true;
}
}
if (hash_store_transaction->StampSuperMac())
prefs_altered = true;
}
if (did_reset) {
pref_store_contents->Set(prefs::kPreferenceResetTime,
new base::StringValue(base::Int64ToString(
base::Time::Now().ToInternalValue())));
FilterUpdate(prefs::kPreferenceResetTime);
if (!on_reset_on_load_.is_null())
on_reset_on_load_.Run();
}
// TODO(gab): Remove this histogram by Feb 21 2014; after sufficient timing
// data has been gathered from the wild to be confident this doesn't
// significantly affect startup.
UMA_HISTOGRAM_TIMES("Settings.FilterOnLoadTime",
base::TimeTicks::Now() - checkpoint);
post_filter_on_load_callback.Run(pref_store_contents.Pass(), prefs_altered);
}