diff options
Diffstat (limited to 'chromium/services/preferences/tracked/pref_hash_filter.cc')
-rw-r--r-- | chromium/services/preferences/tracked/pref_hash_filter.cc | 366 |
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))); +} |