summaryrefslogtreecommitdiff
path: root/chromium/services/preferences/tracked/pref_hash_filter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/services/preferences/tracked/pref_hash_filter.cc')
-rw-r--r--chromium/services/preferences/tracked/pref_hash_filter.cc366
1 files changed, 366 insertions, 0 deletions
diff --git a/chromium/services/preferences/tracked/pref_hash_filter.cc b/chromium/services/preferences/tracked/pref_hash_filter.cc
new file mode 100644
index 00000000000..29d6b9ab830
--- /dev/null
+++ b/chromium/services/preferences/tracked/pref_hash_filter.cc
@@ -0,0 +1,366 @@
+// 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 "services/preferences/tracked/pref_hash_filter.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/pref_store.h"
+#include "services/preferences/public/cpp/tracked/pref_names.h"
+#include "services/preferences/tracked/dictionary_hash_store_contents.h"
+#include "services/preferences/tracked/pref_hash_store.h"
+#include "services/preferences/tracked/pref_hash_store_transaction.h"
+#include "services/preferences/tracked/tracked_atomic_preference.h"
+#include "services/preferences/tracked/tracked_split_preference.h"
+
+namespace {
+
+void CleanupDeprecatedTrackedPreferences(
+ base::DictionaryValue* pref_store_contents,
+ PrefHashStoreTransaction* hash_store_transaction) {
+ // Add deprecated previously tracked preferences below for them to be cleaned
+ // up from both the pref files and the hash store.
+ static const char* const kDeprecatedTrackedPreferences[] = {
+ // TODO(a-v-y): Remove in M60+,
+ "default_search_provider.search_url", "default_search_provider.name",
+ "default_search_provider.keyword"};
+
+ for (size_t i = 0; i < arraysize(kDeprecatedTrackedPreferences); ++i) {
+ const char* key = kDeprecatedTrackedPreferences[i];
+ pref_store_contents->Remove(key, NULL);
+ hash_store_transaction->ClearHash(key);
+ }
+}
+
+} // namespace
+
+using PrefTrackingStrategy =
+ prefs::mojom::TrackedPreferenceMetadata::PrefTrackingStrategy;
+
+PrefHashFilter::PrefHashFilter(
+ std::unique_ptr<PrefHashStore> pref_hash_store,
+ StoreContentsPair external_validation_hash_store_pair,
+ const std::vector<prefs::mojom::TrackedPreferenceMetadataPtr>&
+ tracked_preferences,
+ prefs::mojom::ResetOnLoadObserverPtr reset_on_load_observer,
+ prefs::mojom::TrackedPreferenceValidationDelegate* delegate,
+ size_t reporting_ids_count,
+ bool report_super_mac_validity)
+ : pref_hash_store_(std::move(pref_hash_store)),
+ external_validation_hash_store_pair_(
+ external_validation_hash_store_pair.first
+ ? base::make_optional(
+ std::move(external_validation_hash_store_pair))
+ : base::nullopt),
+ reset_on_load_observer_(std::move(reset_on_load_observer)),
+ report_super_mac_validity_(report_super_mac_validity) {
+ DCHECK(pref_hash_store_);
+ DCHECK_GE(reporting_ids_count, tracked_preferences.size());
+ // Verify that, if |external_validation_hash_store_pair_| is present, both its
+ // items are non-null.
+ DCHECK(!external_validation_hash_store_pair_.has_value() ||
+ (external_validation_hash_store_pair_->first &&
+ external_validation_hash_store_pair_->second));
+
+ for (size_t i = 0; i < tracked_preferences.size(); ++i) {
+ const prefs::mojom::TrackedPreferenceMetadata& metadata =
+ *tracked_preferences[i];
+
+ std::unique_ptr<TrackedPreference> tracked_preference;
+ switch (metadata.strategy) {
+ case PrefTrackingStrategy::ATOMIC:
+ tracked_preference.reset(new TrackedAtomicPreference(
+ metadata.name, metadata.reporting_id, reporting_ids_count,
+ metadata.enforcement_level, metadata.value_type, delegate));
+ break;
+ case PrefTrackingStrategy::SPLIT:
+ tracked_preference.reset(new TrackedSplitPreference(
+ metadata.name, metadata.reporting_id, reporting_ids_count,
+ metadata.enforcement_level, metadata.value_type, delegate));
+ break;
+ }
+ DCHECK(tracked_preference);
+
+ bool is_new = tracked_paths_
+ .insert(std::make_pair(metadata.name,
+ std::move(tracked_preference)))
+ .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(
+ user_prefs::kPreferenceResetTime,
+ base::Int64ToString(base::Time().ToInternalValue()));
+}
+
+// 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_t internal_value = base::Time().ToInternalValue();
+ if (!base::StringToInt64(
+ user_prefs->GetString(user_prefs::kPreferenceResetTime),
+ &internal_value)) {
+ // Somehow the value stored on disk is not a valid int64_t.
+ NOTREACHED();
+ return base::Time();
+ }
+ return base::Time::FromInternalValue(internal_value);
+}
+
+// static
+void PrefHashFilter::ClearResetTime(PrefService* user_prefs) {
+ user_prefs->ClearPref(user_prefs::kPreferenceResetTime);
+}
+
+void PrefHashFilter::Initialize(base::DictionaryValue* pref_store_contents) {
+ DictionaryHashStoreContents dictionary_contents(pref_store_contents);
+ std::unique_ptr<PrefHashStoreTransaction> hash_store_transaction(
+ pref_hash_store_->BeginTransaction(&dictionary_contents));
+ for (auto it = tracked_paths_.begin(); it != tracked_paths_.end(); ++it) {
+ const std::string& initialized_path = it->first;
+ const TrackedPreference* initialized_preference = it->second.get();
+ const base::Value* value = nullptr;
+ 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) {
+ auto it = tracked_paths_.find(path);
+ if (it != tracked_paths_.end())
+ changed_paths_.insert(std::make_pair(path, it->second.get()));
+}
+
+// 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).
+PrefFilter::OnWriteCallbackPair PrefHashFilter::FilterSerializeData(
+ base::DictionaryValue* pref_store_contents) {
+ // Generate the callback pair before clearing |changed_paths_|.
+ PrefFilter::OnWriteCallbackPair callback_pair =
+ GetOnWriteSynchronousCallbacks(pref_store_contents);
+
+ if (!changed_paths_.empty()) {
+ base::TimeTicks checkpoint = base::TimeTicks::Now();
+ {
+ DictionaryHashStoreContents dictionary_contents(pref_store_contents);
+ std::unique_ptr<PrefHashStoreTransaction> hash_store_transaction(
+ pref_hash_store_->BeginTransaction(&dictionary_contents));
+
+ std::unique_ptr<PrefHashStoreTransaction>
+ external_validation_hash_store_transaction;
+ if (external_validation_hash_store_pair_) {
+ external_validation_hash_store_transaction =
+ external_validation_hash_store_pair_->first->BeginTransaction(
+ external_validation_hash_store_pair_->second.get());
+ }
+
+ 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 = nullptr;
+ pref_store_contents->Get(changed_path, &value);
+ changed_preference->OnNewValue(value, hash_store_transaction.get());
+ }
+ changed_paths_.clear();
+ }
+ UMA_HISTOGRAM_TIMES("Settings.FilterSerializeDataTime",
+ base::TimeTicks::Now() - checkpoint);
+ }
+
+ return callback_pair;
+}
+
+void PrefHashFilter::FinalizeFilterOnLoad(
+ const PostFilterOnLoadCallback& post_filter_on_load_callback,
+ std::unique_ptr<base::DictionaryValue> pref_store_contents,
+ bool prefs_altered) {
+ DCHECK(pref_store_contents);
+ base::TimeTicks checkpoint = base::TimeTicks::Now();
+
+ bool did_reset = false;
+ {
+ DictionaryHashStoreContents dictionary_contents(pref_store_contents.get());
+ std::unique_ptr<PrefHashStoreTransaction> hash_store_transaction(
+ pref_hash_store_->BeginTransaction(&dictionary_contents));
+
+ std::unique_ptr<PrefHashStoreTransaction>
+ external_validation_hash_store_transaction;
+ if (external_validation_hash_store_pair_) {
+ external_validation_hash_store_transaction =
+ external_validation_hash_store_pair_->first->BeginTransaction(
+ external_validation_hash_store_pair_->second.get());
+ }
+
+ CleanupDeprecatedTrackedPreferences(pref_store_contents.get(),
+ hash_store_transaction.get());
+
+ if (report_super_mac_validity_) {
+ UMA_HISTOGRAM_BOOLEAN("Settings.HashesDictionaryTrusted",
+ hash_store_transaction->IsSuperMACValid());
+ }
+
+ for (auto it = tracked_paths_.begin(); it != tracked_paths_.end(); ++it) {
+ if (it->second->EnforceAndReport(
+ pref_store_contents.get(), hash_store_transaction.get(),
+ external_validation_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(user_prefs::kPreferenceResetTime,
+ new base::Value(base::Int64ToString(
+ base::Time::Now().ToInternalValue())));
+ FilterUpdate(user_prefs::kPreferenceResetTime);
+
+ if (reset_on_load_observer_)
+ reset_on_load_observer_->OnResetOnLoad();
+ }
+ reset_on_load_observer_.reset();
+
+ UMA_HISTOGRAM_TIMES("Settings.FilterOnLoadTime",
+ base::TimeTicks::Now() - checkpoint);
+
+ post_filter_on_load_callback.Run(std::move(pref_store_contents),
+ prefs_altered);
+}
+
+// static
+void PrefHashFilter::ClearFromExternalStore(
+ HashStoreContents* external_validation_hash_store_contents,
+ const base::DictionaryValue* changed_paths_and_macs) {
+ DCHECK(!changed_paths_and_macs->empty());
+
+ for (base::DictionaryValue::Iterator it(*changed_paths_and_macs);
+ !it.IsAtEnd(); it.Advance()) {
+ external_validation_hash_store_contents->RemoveEntry(it.key());
+ }
+}
+
+// static
+void PrefHashFilter::FlushToExternalStore(
+ std::unique_ptr<HashStoreContents> external_validation_hash_store_contents,
+ std::unique_ptr<base::DictionaryValue> changed_paths_and_macs,
+ bool write_success) {
+ DCHECK(!changed_paths_and_macs->empty());
+ DCHECK(external_validation_hash_store_contents);
+ if (!write_success)
+ return;
+
+ for (base::DictionaryValue::Iterator it(*changed_paths_and_macs);
+ !it.IsAtEnd(); it.Advance()) {
+ const std::string& changed_path = it.key();
+
+ const base::DictionaryValue* split_values = nullptr;
+ if (it.value().GetAsDictionary(&split_values)) {
+ for (base::DictionaryValue::Iterator inner_it(*split_values);
+ !inner_it.IsAtEnd(); inner_it.Advance()) {
+ std::string mac;
+ bool is_string = inner_it.value().GetAsString(&mac);
+ DCHECK(is_string);
+
+ external_validation_hash_store_contents->SetSplitMac(
+ changed_path, inner_it.key(), mac);
+ }
+ } else {
+ const base::Value* value_as_string;
+ bool is_string = it.value().GetAsString(&value_as_string);
+ DCHECK(is_string);
+
+ external_validation_hash_store_contents->SetMac(
+ changed_path, value_as_string->GetString());
+ }
+ }
+}
+
+PrefFilter::OnWriteCallbackPair PrefHashFilter::GetOnWriteSynchronousCallbacks(
+ base::DictionaryValue* pref_store_contents) {
+ if (changed_paths_.empty() || !external_validation_hash_store_pair_) {
+ return std::make_pair(base::Closure(),
+ base::Callback<void(bool success)>());
+ }
+
+ std::unique_ptr<base::DictionaryValue> changed_paths_macs =
+ base::MakeUnique<base::DictionaryValue>();
+
+ 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;
+
+ switch (changed_preference->GetType()) {
+ case TrackedPreferenceType::ATOMIC: {
+ const base::Value* new_value = nullptr;
+ pref_store_contents->Get(changed_path, &new_value);
+ changed_paths_macs->SetStringWithoutPathExpansion(
+ changed_path,
+ external_validation_hash_store_pair_->first->ComputeMac(
+ changed_path, new_value));
+ break;
+ }
+ case TrackedPreferenceType::SPLIT: {
+ const base::DictionaryValue* dict_value = nullptr;
+ pref_store_contents->GetDictionary(changed_path, &dict_value);
+ changed_paths_macs->SetWithoutPathExpansion(
+ changed_path,
+ external_validation_hash_store_pair_->first->ComputeSplitMacs(
+ changed_path, dict_value));
+ break;
+ }
+ }
+ }
+
+ DCHECK(external_validation_hash_store_pair_->second->IsCopyable())
+ << "External HashStoreContents must be copyable as it needs to be used "
+ "off-thread";
+
+ std::unique_ptr<HashStoreContents> hash_store_contents_copy =
+ external_validation_hash_store_pair_->second->MakeCopy();
+
+ // We can use raw pointers for the first callback instead of making more
+ // copies as it will be executed in sequence before the second callback,
+ // which owns the pointers.
+ HashStoreContents* raw_contents = hash_store_contents_copy.get();
+ base::DictionaryValue* raw_changed_paths_macs = changed_paths_macs.get();
+
+ return std::make_pair(
+ base::Bind(&ClearFromExternalStore, base::Unretained(raw_contents),
+ base::Unretained(raw_changed_paths_macs)),
+ base::Bind(&FlushToExternalStore, base::Passed(&hash_store_contents_copy),
+ base::Passed(&changed_paths_macs)));
+}