summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/base.gyp1
-rw-r--r--base/prefs/json_pref_store.cc15
-rw-r--r--base/prefs/json_pref_store.h6
-rw-r--r--base/prefs/json_pref_store_unittest.cc36
-rw-r--r--base/prefs/pref_filter.h35
-rw-r--r--base/prefs/pref_service_factory.cc4
-rw-r--r--chrome/browser/managed_mode/managed_user_settings_service.cc4
-rw-r--r--chrome/browser/prefs/browser_prefs.cc4
-rw-r--r--chrome/browser/prefs/chrome_pref_service_factory.cc14
-rw-r--r--chrome/browser/prefs/chrome_pref_service_factory.h2
-rw-r--r--chrome/browser/prefs/pref_hash_calculator.cc90
-rw-r--r--chrome/browser/prefs/pref_hash_calculator.h53
-rw-r--r--chrome/browser/prefs/pref_hash_calculator_unittest.cc124
-rw-r--r--chrome/browser/prefs/pref_hash_filter.cc96
-rw-r--r--chrome/browser/prefs/pref_hash_filter.h44
-rw-r--r--chrome/browser/prefs/pref_hash_filter_unittest.cc226
-rw-r--r--chrome/browser/prefs/pref_hash_store.h49
-rw-r--r--chrome/browser/prefs/pref_hash_store_impl.cc76
-rw-r--r--chrome/browser/prefs/pref_hash_store_impl.h56
-rw-r--r--chrome/browser/prefs/pref_hash_store_impl_unittest.cc59
-rw-r--r--chrome/browser/prefs/pref_metrics_service.cc245
-rw-r--r--chrome/browser/prefs/pref_metrics_service.h50
-rw-r--r--chrome/browser/prefs/pref_metrics_service_unittest.cc478
-rw-r--r--chrome/browser/prefs/pref_service_browsertest.cc14
-rw-r--r--chrome/browser/profiles/profile_impl.cc33
-rw-r--r--chrome/chrome_browser.gypi7
-rw-r--r--chrome/chrome_tests_unit.gypi4
-rw-r--r--chrome/service/service_process_prefs.cc5
28 files changed, 1036 insertions, 794 deletions
diff --git a/base/base.gyp b/base/base.gyp
index d378d14..bb1085d 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -352,6 +352,7 @@
'prefs/persistent_pref_store.h',
'prefs/pref_change_registrar.cc',
'prefs/pref_change_registrar.h',
+ 'prefs/pref_filter.h',
'prefs/pref_member.cc',
'prefs/pref_member.h',
'prefs/pref_notifier.h',
diff --git a/base/prefs/json_pref_store.cc b/base/prefs/json_pref_store.cc
index ad97b84..f417b8b 100644
--- a/base/prefs/json_pref_store.cc
+++ b/base/prefs/json_pref_store.cc
@@ -13,6 +13,7 @@
#include "base/json/json_string_value_serializer.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/prefs/pref_filter.h"
#include "base/sequenced_task_runner.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/values.h"
@@ -151,12 +152,14 @@ scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile(
}
JsonPrefStore::JsonPrefStore(const base::FilePath& filename,
- base::SequencedTaskRunner* sequenced_task_runner)
+ base::SequencedTaskRunner* sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter)
: path_(filename),
sequenced_task_runner_(sequenced_task_runner),
prefs_(new base::DictionaryValue()),
read_only_(false),
writer_(filename, sequenced_task_runner),
+ pref_filter_(pref_filter.Pass()),
initialized_(false),
read_error_(PREF_READ_ERROR_OTHER) {}
@@ -264,7 +267,14 @@ void JsonPrefStore::CommitPendingWrite() {
}
void JsonPrefStore::ReportValueChanged(const std::string& key) {
+ if (pref_filter_) {
+ const base::Value* tmp = NULL;
+ prefs_->Get(key, &tmp);
+ pref_filter_->FilterUpdate(key, tmp);
+ }
+
FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
+
if (!read_only_)
writer_.ScheduleWrite(this);
}
@@ -307,6 +317,9 @@ void JsonPrefStore::OnFileRead(base::Value* value_owned,
NOTREACHED() << "Unknown error: " << error;
}
+ if (pref_filter_)
+ pref_filter_->FilterOnLoad(prefs_.get());
+
if (error_delegate_.get() && error != PREF_READ_ERROR_NONE)
error_delegate_->OnError(error);
diff --git a/base/prefs/json_pref_store.h b/base/prefs/json_pref_store.h
index 21fc8f9..ad13feb 100644
--- a/base/prefs/json_pref_store.h
+++ b/base/prefs/json_pref_store.h
@@ -18,6 +18,8 @@
#include "base/prefs/base_prefs_export.h"
#include "base/prefs/persistent_pref_store.h"
+class PrefFilter;
+
namespace base {
class DictionaryValue;
class FilePath;
@@ -41,7 +43,8 @@ class BASE_PREFS_EXPORT JsonPrefStore
// |sequenced_task_runner| is must be a shutdown-blocking task runner, ideally
// created by GetTaskRunnerForFile() method above.
JsonPrefStore(const base::FilePath& pref_filename,
- base::SequencedTaskRunner* sequenced_task_runner);
+ base::SequencedTaskRunner* sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter);
// PrefStore overrides:
virtual bool GetValue(const std::string& key,
@@ -87,6 +90,7 @@ class BASE_PREFS_EXPORT JsonPrefStore
// Helper for safely writing pref data.
base::ImportantFileWriter writer_;
+ scoped_ptr<PrefFilter> pref_filter_;
ObserverList<PrefStore::Observer, true> observers_;
scoped_ptr<ReadErrorDelegate> error_delegate_;
diff --git a/base/prefs/json_pref_store_unittest.cc b/base/prefs/json_pref_store_unittest.cc
index a26afd7..119a29c 100644
--- a/base/prefs/json_pref_store_unittest.cc
+++ b/base/prefs/json_pref_store_unittest.cc
@@ -9,6 +9,7 @@
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
+#include "base/prefs/pref_filter.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -60,7 +61,9 @@ TEST_F(JsonPrefStoreTest, NonExistentFile) {
base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
ASSERT_FALSE(PathExists(bogus_input_file));
scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
- bogus_input_file, message_loop_.message_loop_proxy().get());
+ bogus_input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
pref_store->ReadPrefs());
EXPECT_FALSE(pref_store->ReadOnly());
@@ -72,7 +75,9 @@ TEST_F(JsonPrefStoreTest, InvalidFile) {
base::FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json");
ASSERT_TRUE(base::CopyFile(invalid_file_original, invalid_file));
scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(invalid_file, message_loop_.message_loop_proxy().get());
+ new JsonPrefStore(invalid_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
pref_store->ReadPrefs());
EXPECT_FALSE(pref_store->ReadOnly());
@@ -157,8 +162,10 @@ TEST_F(JsonPrefStoreTest, Basic) {
// Test that the persistent value can be loaded.
base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
ASSERT_TRUE(PathExists(input_file));
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(input_file, message_loop_.message_loop_proxy().get());
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
ASSERT_FALSE(pref_store->ReadOnly());
@@ -183,8 +190,10 @@ TEST_F(JsonPrefStoreTest, BasicAsync) {
// Test that the persistent value can be loaded.
base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
ASSERT_TRUE(PathExists(input_file));
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(input_file, message_loop_.message_loop_proxy().get());
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
{
MockPrefStoreObserver mock_observer;
@@ -219,8 +228,10 @@ TEST_F(JsonPrefStoreTest, BasicAsync) {
TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json");
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(pref_file, message_loop_.message_loop_proxy());
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ pref_file,
+ message_loop_.message_loop_proxy(),
+ scoped_ptr<PrefFilter>());
// Set some keys with empty values.
pref_store->SetValue("list", new base::ListValue);
@@ -231,7 +242,10 @@ TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
MessageLoop::current()->RunUntilIdle();
// Reload.
- pref_store = new JsonPrefStore(pref_file, message_loop_.message_loop_proxy());
+ pref_store = new JsonPrefStore(
+ pref_file,
+ message_loop_.message_loop_proxy(),
+ scoped_ptr<PrefFilter>());
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
ASSERT_FALSE(pref_store->ReadOnly());
@@ -248,7 +262,9 @@ TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
ASSERT_FALSE(PathExists(bogus_input_file));
scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
- bogus_input_file, message_loop_.message_loop_proxy().get());
+ bogus_input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
MockPrefStoreObserver mock_observer;
pref_store->AddObserver(&mock_observer);
diff --git a/base/prefs/pref_filter.h b/base/prefs/pref_filter.h
new file mode 100644
index 0000000..3d136f7
--- /dev/null
+++ b/base/prefs/pref_filter.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef BASE_PREFS_PREF_FILTER_H_
+#define BASE_PREFS_PREF_FILTER_H_
+
+#include <string>
+
+#include "base/prefs/base_prefs_export.h"
+
+namespace base {
+class DictionaryValue;
+class Value;
+} // namespace base
+
+// Filters preferences as they are loaded from disk or updated at runtime.
+// Currently supported only by JsonPrefStore.
+class BASE_PREFS_EXPORT PrefFilter {
+ public:
+ virtual ~PrefFilter() {}
+
+ // Receives notification when the pref store data has been loaded but before
+ // Observers are notified.
+ // Changes made by a PrefFilter during FilterOnLoad do not result in
+ // notifications to |PrefStore::Observer|s.
+ virtual void FilterOnLoad(base::DictionaryValue* pref_store_contents) = 0;
+
+ // Receives notification when a pref store value is changed, before Observers
+ // are notified.
+ virtual void FilterUpdate(const std::string& path,
+ const base::Value* value) = 0;
+};
+
+#endif // BASE_PREFS_PREF_FILTER_H_
diff --git a/base/prefs/pref_service_factory.cc b/base/prefs/pref_service_factory.cc
index 9c59853..d644cb1 100644
--- a/base/prefs/pref_service_factory.cc
+++ b/base/prefs/pref_service_factory.cc
@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "base/prefs/default_pref_store.h"
#include "base/prefs/json_pref_store.h"
+#include "base/prefs/pref_filter.h"
#include "base/prefs/pref_notifier_impl.h"
#include "base/prefs/pref_service.h"
@@ -37,7 +38,8 @@ PrefServiceFactory::~PrefServiceFactory() {}
void PrefServiceFactory::SetUserPrefsFile(
const base::FilePath& prefs_file,
base::SequencedTaskRunner* task_runner) {
- user_prefs_ = new JsonPrefStore(prefs_file, task_runner);
+ user_prefs_ = new JsonPrefStore(
+ prefs_file, task_runner, scoped_ptr<PrefFilter>());
}
scoped_ptr<PrefService> PrefServiceFactory::Create(
diff --git a/chrome/browser/managed_mode/managed_user_settings_service.cc b/chrome/browser/managed_mode/managed_user_settings_service.cc
index 9f229b6..9b24e97 100644
--- a/chrome/browser/managed_mode/managed_user_settings_service.cc
+++ b/chrome/browser/managed_mode/managed_user_settings_service.cc
@@ -8,6 +8,7 @@
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/prefs/json_pref_store.h"
+#include "base/prefs/pref_filter.h"
#include "base/strings/string_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "chrome/browser/managed_mode/managed_mode_url_filter.h"
@@ -59,7 +60,8 @@ void ManagedUserSettingsService::Init(
bool load_synchronously) {
base::FilePath path =
profile_path.Append(chrome::kManagedUserSettingsFilename);
- PersistentPrefStore* store = new JsonPrefStore(path, sequenced_task_runner);
+ PersistentPrefStore* store = new JsonPrefStore(
+ path, sequenced_task_runner, scoped_ptr<PrefFilter>());
Init(store);
if (load_synchronously)
store_->ReadPrefs();
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index d6b9f73..1f370ae 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -56,7 +56,7 @@
#include "chrome/browser/pepper_flash_settings_manager.h"
#include "chrome/browser/plugins/plugin_finder.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
-#include "chrome/browser/prefs/pref_metrics_service.h"
+#include "chrome/browser/prefs/pref_hash_store_impl.h"
#include "chrome/browser/prefs/pref_service_syncable.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/printing/cloud_print/cloud_print_url.h"
@@ -236,7 +236,7 @@ void RegisterLocalState(PrefRegistrySimple* registry) {
MetricsLog::RegisterPrefs(registry);
MetricsService::RegisterPrefs(registry);
metrics::CachingPermutedEntropyProvider::RegisterPrefs(registry);
- PrefMetricsService::RegisterPrefs(registry);
+ PrefHashStoreImpl::RegisterPrefs(registry);
PrefProxyConfigTrackerImpl::RegisterPrefs(registry);
ProfileInfoCache::RegisterPrefs(registry);
profiles::RegisterPrefs(registry);
diff --git a/chrome/browser/prefs/chrome_pref_service_factory.cc b/chrome/browser/prefs/chrome_pref_service_factory.cc
index 7fe3d3c..a459ea8 100644
--- a/chrome/browser/prefs/chrome_pref_service_factory.cc
+++ b/chrome/browser/prefs/chrome_pref_service_factory.cc
@@ -17,6 +17,8 @@
#include "base/prefs/pref_value_store.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/prefs/command_line_pref_store.h"
+#include "chrome/browser/prefs/pref_hash_filter.h"
+#include "chrome/browser/prefs/pref_hash_store.h"
#include "chrome/browser/prefs/pref_model_associator.h"
#include "chrome/browser/prefs/pref_service_syncable.h"
#include "chrome/browser/prefs/pref_service_syncable_factory.h"
@@ -80,6 +82,7 @@ void PrepareBuilder(
base::SequencedTaskRunner* pref_io_task_runner,
policy::PolicyService* policy_service,
ManagedUserSettingsService* managed_user_settings,
+ scoped_ptr<PrefHashStore> pref_hash_store,
const scoped_refptr<PrefStore>& extension_prefs,
bool async) {
#if defined(OS_LINUX)
@@ -122,8 +125,14 @@ void PrepareBuilder(
make_scoped_refptr(
new CommandLinePrefStore(CommandLine::ForCurrentProcess())));
factory->set_read_error_callback(base::Bind(&HandleReadError));
+ scoped_ptr<PrefFilter> pref_filter;
+ if (pref_hash_store)
+ pref_filter.reset(new PrefHashFilter(pref_hash_store.Pass()));
factory->set_user_prefs(
- new JsonPrefStore(pref_filename, pref_io_task_runner));
+ new JsonPrefStore(
+ pref_filename,
+ pref_io_task_runner,
+ pref_filter.Pass()));
}
} // namespace
@@ -142,6 +151,7 @@ scoped_ptr<PrefService> CreateLocalState(
pref_io_task_runner,
policy_service,
NULL,
+ scoped_ptr<PrefHashStore>(),
NULL,
async);
return factory.Create(pref_registry.get());
@@ -152,6 +162,7 @@ scoped_ptr<PrefServiceSyncable> CreateProfilePrefs(
base::SequencedTaskRunner* pref_io_task_runner,
policy::PolicyService* policy_service,
ManagedUserSettingsService* managed_user_settings,
+ scoped_ptr<PrefHashStore> pref_hash_store,
const scoped_refptr<PrefStore>& extension_prefs,
const scoped_refptr<user_prefs::PrefRegistrySyncable>& pref_registry,
bool async) {
@@ -162,6 +173,7 @@ scoped_ptr<PrefServiceSyncable> CreateProfilePrefs(
pref_io_task_runner,
policy_service,
managed_user_settings,
+ pref_hash_store.Pass(),
extension_prefs,
async);
return factory.CreateSyncable(pref_registry.get());
diff --git a/chrome/browser/prefs/chrome_pref_service_factory.h b/chrome/browser/prefs/chrome_pref_service_factory.h
index b1322b7..3456463 100644
--- a/chrome/browser/prefs/chrome_pref_service_factory.h
+++ b/chrome/browser/prefs/chrome_pref_service_factory.h
@@ -22,6 +22,7 @@ class PrefRegistrySyncable;
}
class ManagedUserSettingsService;
+class PrefHashStore;
class PrefRegistry;
class PrefService;
class PrefServiceSyncable;
@@ -56,6 +57,7 @@ scoped_ptr<PrefServiceSyncable> CreateProfilePrefs(
base::SequencedTaskRunner* pref_io_task_runner,
policy::PolicyService* policy_service,
ManagedUserSettingsService* managed_user_settings,
+ scoped_ptr<PrefHashStore> pref_hash_store,
const scoped_refptr<PrefStore>& extension_prefs,
const scoped_refptr<user_prefs::PrefRegistrySyncable>& pref_registry,
bool async);
diff --git a/chrome/browser/prefs/pref_hash_calculator.cc b/chrome/browser/prefs/pref_hash_calculator.cc
new file mode 100644
index 0000000..4a787a8
--- /dev/null
+++ b/chrome/browser/prefs/pref_hash_calculator.cc
@@ -0,0 +1,90 @@
+// 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_calculator.h"
+
+#include <vector>
+
+#include "base/json/json_string_value_serializer.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "crypto/hmac.h"
+
+namespace {
+
+// Renders |value| as a string. |value| may be NULL, in which case the result
+// is an empty string.
+std::string ValueAsString(const base::Value* value) {
+ // Dictionary values may contain empty lists and sub-dictionaries. Make a
+ // deep copy with those removed to make the hash more stable.
+ const base::DictionaryValue* dict_value;
+ scoped_ptr<DictionaryValue> canonical_dict_value;
+ if (value && value->GetAsDictionary(&dict_value)) {
+ canonical_dict_value.reset(dict_value->DeepCopyWithoutEmptyChildren());
+ value = canonical_dict_value.get();
+ }
+
+ std::string value_as_string;
+ if (value) {
+ JSONStringValueSerializer serializer(&value_as_string);
+ serializer.Serialize(*value);
+ }
+
+ return value_as_string;
+}
+
+// Common helper for all hash algorithms.
+std::string CalculateFromValueAndComponents(
+ const std::string& seed,
+ const base::Value* value,
+ const std::vector<std::string>& extra_components) {
+ static const size_t kSHA256DigestSize = 32;
+
+ std::string message = JoinString(extra_components, "") + ValueAsString(value);
+
+ crypto::HMAC hmac(crypto::HMAC::SHA256);
+ unsigned char digest[kSHA256DigestSize];
+ if (!hmac.Init(seed) || !hmac.Sign(message, digest, arraysize(digest))) {
+ NOTREACHED();
+ return std::string();
+ }
+
+ return base::HexEncode(digest, arraysize(digest));
+}
+
+} // namespace
+
+PrefHashCalculator::PrefHashCalculator(const std::string& seed,
+ const std::string& device_id)
+ : seed_(seed), device_id_(device_id) {}
+
+std::string PrefHashCalculator::Calculate(const std::string& path,
+ const base::Value* value) const {
+ std::vector<std::string> components;
+ if (!device_id_.empty())
+ components.push_back(device_id_);
+ components.push_back(path);
+ return CalculateFromValueAndComponents(seed_, value, components);
+}
+
+PrefHashCalculator::ValidationResult PrefHashCalculator::Validate(
+ const std::string& path,
+ const base::Value* value,
+ const std::string& hash) const {
+ if (hash == Calculate(path, value))
+ return VALID;
+ if (hash == CalculateLegacyHash(path, value))
+ return VALID_LEGACY;
+ return INVALID;
+}
+
+std::string PrefHashCalculator::CalculateLegacyHash(
+ const std::string& path, const base::Value* value) const {
+ return CalculateFromValueAndComponents(seed_,
+ value,
+ std::vector<std::string>());
+}
diff --git a/chrome/browser/prefs/pref_hash_calculator.h b/chrome/browser/prefs/pref_hash_calculator.h
new file mode 100644
index 0000000..6f0fd85
--- /dev/null
+++ b/chrome/browser/prefs/pref_hash_calculator.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PREFS_PREF_HASH_CALCULATOR_H_
+#define CHROME_BROWSER_PREFS_PREF_HASH_CALCULATOR_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace base {
+class Value;
+} // namespace base
+
+// Calculates and validates preference value hashes.
+class PrefHashCalculator {
+ public:
+ enum ValidationResult {
+ INVALID,
+ VALID,
+ VALID_LEGACY,
+ };
+
+ // Constructs a PrefHashCalculator using |seed| and |device_id|. The same
+ // parameters must be used in order to successfully validate generated hashes.
+ // |device_id| may be empty.
+ PrefHashCalculator(const std::string& seed, const std::string& device_id);
+
+ // Calculates a hash value for the supplied preference |path| and |value|.
+ // |value| may be null if the preference has no value.
+ std::string Calculate(const std::string& path, const base::Value* value)
+ const;
+
+ // Validates the provided preference hash using current and legacy hashing
+ // algorithms.
+ ValidationResult Validate(const std::string& path,
+ const base::Value* value,
+ const std::string& hash) const;
+
+ private:
+ // Calculate a hash using a deprecated hash algorithm. For validating old
+ // hashes during migration.
+ std::string CalculateLegacyHash(const std::string& path,
+ const base::Value* value) const;
+
+ std::string seed_;
+ std::string device_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefHashCalculator);
+};
+
+#endif // CHROME_BROWSER_PREFS_PREF_HASH_CALCULATOR_H_
diff --git a/chrome/browser/prefs/pref_hash_calculator_unittest.cc b/chrome/browser/prefs/pref_hash_calculator_unittest.cc
new file mode 100644
index 0000000..1f5ad7c
--- /dev/null
+++ b/chrome/browser/prefs/pref_hash_calculator_unittest.cc
@@ -0,0 +1,124 @@
+// 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_calculator.h"
+
+#include <string>
+
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(PrefHashCalculatorTest, TestCurrentAlgorithm) {
+ base::StringValue string_value_1("string value 1");
+ base::StringValue string_value_2("string value 2");
+ base::DictionaryValue dictionary_value_1;
+ dictionary_value_1.SetInteger("int value", 1);
+ dictionary_value_1.Set("nested empty map", new DictionaryValue);
+ base::DictionaryValue dictionary_value_1_equivalent;
+ dictionary_value_1_equivalent.SetInteger("int value", 1);
+ base::DictionaryValue dictionary_value_2;
+ dictionary_value_2.SetInteger("int value", 2);
+
+ PrefHashCalculator calc1("seed1", "deviceid");
+ PrefHashCalculator calc1_dup("seed1", "deviceid");
+ PrefHashCalculator calc2("seed2", "deviceid");
+ PrefHashCalculator calc3("seed1", "deviceid2");
+
+ // Two calculators with same seed produce same hash.
+ ASSERT_EQ(calc1.Calculate("pref_path", &string_value_1),
+ calc1_dup.Calculate("pref_path", &string_value_1));
+ ASSERT_EQ(PrefHashCalculator::VALID,
+ calc1_dup.Validate(
+ "pref_path",
+ &string_value_1,
+ calc1.Calculate("pref_path", &string_value_1)));
+
+ // Different seeds, different hashes.
+ ASSERT_NE(calc1.Calculate("pref_path", &string_value_1),
+ calc2.Calculate("pref_path", &string_value_1));
+ ASSERT_EQ(PrefHashCalculator::INVALID,
+ calc2.Validate(
+ "pref_path",
+ &string_value_1,
+ calc1.Calculate("pref_path", &string_value_1)));
+
+ // Different device IDs, different hashes.
+ ASSERT_NE(calc1.Calculate("pref_path", &string_value_1),
+ calc3.Calculate("pref_path", &string_value_1));
+
+ // Different values, different hashes.
+ ASSERT_NE(calc1.Calculate("pref_path", &string_value_1),
+ calc1.Calculate("pref_path", &string_value_2));
+
+ // Different paths, different hashes.
+ ASSERT_NE(calc1.Calculate("pref_path", &string_value_1),
+ calc1.Calculate("pref_path_2", &string_value_1));
+
+ // Works for dictionaries.
+ ASSERT_EQ(calc1.Calculate("pref_path", &dictionary_value_1),
+ calc1.Calculate("pref_path", &dictionary_value_1));
+ ASSERT_NE(calc1.Calculate("pref_path", &dictionary_value_1),
+ calc1.Calculate("pref_path", &dictionary_value_2));
+
+ // Empty dictionary children are pruned.
+ ASSERT_EQ(calc1.Calculate("pref_path", &dictionary_value_1),
+ calc1.Calculate("pref_path", &dictionary_value_1_equivalent));
+
+ // NULL value is supported.
+ ASSERT_FALSE(calc1.Calculate("pref_path", NULL).empty());
+}
+
+// Tests the output against a known value to catch unexpected algorithm changes.
+TEST(PrefHashCalculatorTest, CatchHashChanges) {
+ const char* kDeviceId = "test_device_id1";
+ {
+ static const char kExpectedValue[] =
+ "5CE37D7EBCBC9BE510F0F5E7C326CA92C1673713C3717839610AEA1A217D8BB8";
+
+ base::ListValue list;
+ list.Set(0, new base::FundamentalValue(true));
+ list.Set(1, new base::FundamentalValue(100));
+ list.Set(2, new base::FundamentalValue(1.0));
+
+ // 32 NULL bytes is the seed that was used to generate the hash in old
+ // tests. Use it again to ensure that we haven't altered the algorithm.
+ EXPECT_EQ(PrefHashCalculator::VALID,
+ PrefHashCalculator(std::string(32u, 0), kDeviceId).Validate(
+ "pref.path2", &list, kExpectedValue));
+ }
+ {
+ static const char kExpectedValue[] =
+ "A50FE7EB31BFBC32B8A27E71730AF15421178A9B5815644ACE174B18966735B9";
+
+ DictionaryValue dict;
+ dict.Set("a", new StringValue("foo"));
+ dict.Set("d", new StringValue("bad"));
+ dict.Set("b", new StringValue("bar"));
+ dict.Set("c", new StringValue("baz"));
+
+ // 32 NULL bytes is the seed that was used to generate the hash in old
+ // tests. Use it again to ensure that we haven't altered the algorithm.
+ EXPECT_EQ(PrefHashCalculator::VALID,
+ PrefHashCalculator(std::string(32u, 0), kDeviceId).Validate(
+ "pref.path1", &dict, kExpectedValue));
+ }
+}
+
+TEST(PrefHashCalculatorTest, TestLegacyAlgorithm) {
+ const char* kExpectedValue =
+ "C503FB7C65EEFD5C07185F616A0AA67923C069909933F362022B1F187E73E9A2";
+ const char* kDeviceId = "deviceid";
+
+ DictionaryValue dict;
+ dict.Set("a", new StringValue("foo"));
+ dict.Set("d", new StringValue("bad"));
+ dict.Set("b", new StringValue("bar"));
+ dict.Set("c", new StringValue("baz"));
+
+ // 32 NULL bytes is the seed that was used to generate the legacy hash.
+ EXPECT_EQ(PrefHashCalculator::VALID_LEGACY,
+ PrefHashCalculator(std::string(32u, 0), kDeviceId).Validate(
+ "pref.path1", &dict, kExpectedValue));
+
+}
diff --git a/chrome/browser/prefs/pref_hash_filter.cc b/chrome/browser/prefs/pref_hash_filter.cc
new file mode 100644
index 0000000..803ff13
--- /dev/null
+++ b/chrome/browser/prefs/pref_hash_filter.cc
@@ -0,0 +1,96 @@
+// 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 "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/values.h"
+#include "chrome/browser/prefs/pref_hash_store.h"
+#include "chrome/common/pref_names.h"
+
+namespace {
+
+// These preferences must be kept in sync with the TrackedPreference enum in
+// tools/metrics/histograms/histograms.xml. To add a new preference, append it
+// to the array and add a corresponding value to the histogram enum. Replace
+// removed preferences with "".
+const char* kTrackedPrefs[] = {
+ prefs::kShowHomeButton,
+ prefs::kHomePageIsNewTabPage,
+ prefs::kHomePage,
+ prefs::kRestoreOnStartup,
+ prefs::kURLsToRestoreOnStartup,
+ prefs::kExtensionsPref,
+ prefs::kGoogleServicesLastUsername,
+ prefs::kSearchProviderOverrides,
+ prefs::kDefaultSearchProviderSearchURL,
+ prefs::kDefaultSearchProviderKeyword,
+ prefs::kDefaultSearchProviderName,
+#if !defined(OS_ANDROID)
+ prefs::kPinnedTabs,
+#else
+ "",
+#endif
+ prefs::kExtensionKnownDisabled,
+ prefs::kProfileResetPromptMemento,
+};
+
+void ReportValidationResult(PrefHashStore::ValueState value_state,
+ size_t value_index) {
+ switch (value_state) {
+ case PrefHashStore::UNCHANGED:
+ UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceUnchanged",
+ value_index, arraysize(kTrackedPrefs));
+ return;
+ case PrefHashStore::CLEARED:
+ UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceCleared",
+ value_index, arraysize(kTrackedPrefs));
+ return;
+ case PrefHashStore::MIGRATED:
+ UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceMigrated",
+ value_index, arraysize(kTrackedPrefs));
+ return;
+ case PrefHashStore::CHANGED:
+ UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceChanged",
+ value_index, arraysize(kTrackedPrefs));
+ return;
+ case PrefHashStore::UNKNOWN_VALUE:
+ UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceInitialized",
+ value_index, arraysize(kTrackedPrefs));
+ return;
+ }
+ NOTREACHED() << "Unexpected PrefHashStore::ValueState: " << value_state;
+}
+
+} // namespace
+
+PrefHashFilter::PrefHashFilter(scoped_ptr<PrefHashStore> pref_hash_store)
+ : pref_hash_store_(pref_hash_store.Pass()),
+ tracked_paths_(kTrackedPrefs, kTrackedPrefs + arraysize(kTrackedPrefs)) {}
+
+PrefHashFilter::~PrefHashFilter() {}
+
+// Updates the stored hash to correspond to the updated preference value.
+void PrefHashFilter::FilterUpdate(const std::string& path,
+ const base::Value* value) {
+ if (tracked_paths_.find(path) != tracked_paths_.end())
+ pref_hash_store_->StoreHash(path, value);
+}
+
+// Validates loaded preference values according to stored hashes, reports
+// validation results via UMA, and updates hashes in case of mismatch.
+void PrefHashFilter::FilterOnLoad(base::DictionaryValue* pref_store_contents) {
+ for (size_t i = 0; i < arraysize(kTrackedPrefs); ++i) {
+ if (kTrackedPrefs[i][0] == '\0')
+ continue;
+ const base::Value* value = NULL;
+ pref_store_contents->Get(kTrackedPrefs[i], &value);
+ PrefHashStore::ValueState value_state =
+ pref_hash_store_->CheckValue(kTrackedPrefs[i], value);
+ ReportValidationResult(value_state, i);
+ if (value_state != PrefHashStore::UNCHANGED)
+ pref_hash_store_->StoreHash(kTrackedPrefs[i], value);
+ }
+}
diff --git a/chrome/browser/prefs/pref_hash_filter.h b/chrome/browser/prefs/pref_hash_filter.h
new file mode 100644
index 0000000..2c74694
--- /dev/null
+++ b/chrome/browser/prefs/pref_hash_filter.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PREFS_PREF_HASH_FILTER_H_
+#define CHROME_BROWSER_PREFS_PREF_HASH_FILTER_H_
+
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/prefs/pref_filter.h"
+
+class PrefHashStore;
+
+namespace base {
+class DictionaryValue;
+class Value;
+} // namespace base
+
+// Intercepts preference values as they are loaded from disk and verifies them
+// using a PrefHashStore. Keeps the PrefHashStore contents up to date as values
+// are changed.
+class PrefHashFilter : public PrefFilter {
+ public:
+ explicit PrefHashFilter(scoped_ptr<PrefHashStore> pref_hash_store);
+ virtual ~PrefHashFilter();
+
+ // PrefFilter implementation.
+ virtual void FilterOnLoad(base::DictionaryValue* pref_store_contents)
+ OVERRIDE;
+ virtual void FilterUpdate(const std::string& path,
+ const base::Value* value) OVERRIDE;
+
+ private:
+ scoped_ptr<PrefHashStore> pref_hash_store_;
+ std::set<std::string> tracked_paths_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefHashFilter);
+};
+
+#endif // CHROME_BROWSER_PREFS_PREF_HASH_FILTER_H_
diff --git a/chrome/browser/prefs/pref_hash_filter_unittest.cc b/chrome/browser/prefs/pref_hash_filter_unittest.cc
new file mode 100644
index 0000000..d26fe70
--- /dev/null
+++ b/chrome/browser/prefs/pref_hash_filter_unittest.cc
@@ -0,0 +1,226 @@
+// 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 <map>
+#include <set>
+#include <string>
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/prefs/pref_hash_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// A PrefHashStore that allows simulation of CheckValue results and captures
+// checked and stored values.
+class MockPrefHashStore : public PrefHashStore {
+ public:
+ static const char kNullPlaceholder[];
+
+ MockPrefHashStore() {}
+
+ // Set the result that will be returned when |path| is passed to |CheckValue|.
+ void SetCheckResult(const std::string& path,
+ PrefHashStore::ValueState result);
+
+ // Returns paths that have been passed to |CheckValue|.
+ const std::set<std::string>& checked_paths() const {
+ return checked_paths_;
+ }
+
+ // Returns paths that have been passed to |StoreHash|.
+ const std::set<std::string>& stored_paths() const {
+ return stored_paths_;
+ }
+
+ // Returns the value that was passed to |CheckValue| for |path|, rendered to a
+ // string. If the checked value was NULL, the string is kNullPlaceholder.
+ std::string checked_value(const std::string& path) const {
+ std::map<std::string, std::string>::iterator value =
+ checked_values_.find(path);
+ if (value != checked_values_.end())
+ return value->second;
+ return std::string("No checked value.");
+ }
+
+ // Returns the value that was passed to |StoreHash| for |path|, rendered to a
+ // string. If the stored value was NULL, the string is kNullPlaceholder.
+ std::string stored_value(const std::string& path) const {
+ std::map<std::string, std::string>::const_iterator value =
+ stored_values_.find(path);
+ if (value != stored_values_.end())
+ return value->second;
+ return std::string("No stored value.");
+ }
+
+ // PrefHashStore implementation.
+ virtual PrefHashStore::ValueState CheckValue(
+ const std::string& path, const base::Value* value) const OVERRIDE;
+ virtual void StoreHash(const std::string& path,
+ const base::Value* new_value) OVERRIDE;
+
+ private:
+ std::map<std::string, PrefHashStore::ValueState> check_results_;
+ mutable std::set<std::string> checked_paths_;
+ std::set<std::string> stored_paths_;
+ mutable std::map<std::string, std::string> checked_values_;
+ std::map<std::string, std::string> stored_values_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockPrefHashStore);
+};
+
+const char MockPrefHashStore::kNullPlaceholder[] = "NULL";
+
+void MockPrefHashStore::SetCheckResult(
+ const std::string& path, PrefHashStore::ValueState result) {
+ check_results_.insert(std::make_pair(path, result));
+}
+
+PrefHashStore::ValueState MockPrefHashStore::CheckValue(
+ const std::string& path, const base::Value* value) const {
+ EXPECT_TRUE(checked_paths_.insert(path).second);
+ std::string as_string = kNullPlaceholder;
+ if (value)
+ EXPECT_TRUE(value->GetAsString(&as_string));
+
+ checked_values_.insert(std::make_pair(path, as_string));
+ std::map<std::string, PrefHashStore::ValueState>::const_iterator result =
+ check_results_.find(path);
+ if (result != check_results_.end())
+ return result->second;
+ return PrefHashStore::UNCHANGED;
+}
+
+void MockPrefHashStore::StoreHash(const std::string& path,
+ const base::Value* new_value) {
+ std::string as_string = kNullPlaceholder;
+ if (new_value)
+ EXPECT_TRUE(new_value->GetAsString(&as_string));
+ stored_paths_.insert(path);
+ stored_values_.insert(std::make_pair(path, as_string));
+}
+
+// Creates a PrefHashFilter that uses a MockPrefHashStore. The
+// MockPrefHashStore (owned by the PrefHashFilter) is returned in
+// |mock_pref_hash_store|.
+scoped_ptr<PrefHashFilter> CreatePrefHashFilter(
+ MockPrefHashStore** mock_pref_hash_store) {
+ scoped_ptr<MockPrefHashStore> temp_mock_pref_hash_store(
+ new MockPrefHashStore);
+ if (mock_pref_hash_store)
+ *mock_pref_hash_store = temp_mock_pref_hash_store.get();
+ return scoped_ptr<PrefHashFilter>(
+ new PrefHashFilter(temp_mock_pref_hash_store.PassAs<PrefHashStore>()));
+}
+
+class PrefHashFilterTest : public testing::Test {
+ public:
+ PrefHashFilterTest() : mock_pref_hash_store_(NULL) {}
+
+ virtual void SetUp() OVERRIDE {
+ // Capture the name of one tracked pref.
+ MockPrefHashStore* temp_hash_store = NULL;
+ scoped_ptr<PrefHashFilter> temp_filter =
+ CreatePrefHashFilter(&temp_hash_store);
+ temp_filter->FilterOnLoad(&pref_store_contents_);
+ ASSERT_FALSE(temp_hash_store->checked_paths().empty());
+ tracked_path_ = *temp_hash_store->checked_paths().begin();
+
+ // Construct a PrefHashFilter and MockPrefHashStore for the test.
+ pref_hash_filter_ = CreatePrefHashFilter(&mock_pref_hash_store_);
+ }
+
+ protected:
+ std::string tracked_path_;
+ MockPrefHashStore* mock_pref_hash_store_;
+ base::DictionaryValue pref_store_contents_;
+ scoped_ptr<PrefHashFilter> pref_hash_filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefHashFilterTest);
+};
+
+TEST_F(PrefHashFilterTest, EmptyAndUnchanged) {
+ pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
+ // More than 0 paths checked.
+ ASSERT_LT(0u, mock_pref_hash_store_->checked_paths().size());
+ // No paths stored, since they all return |UNCHANGED|.
+ ASSERT_EQ(0u, mock_pref_hash_store_->stored_paths().size());
+ // Since there was nothing in |pref_store_contents_| the checked value should
+ // have been NULL.
+ ASSERT_EQ(MockPrefHashStore::kNullPlaceholder,
+ mock_pref_hash_store_->checked_value(tracked_path_));
+}
+
+TEST_F(PrefHashFilterTest, FilterUpdate) {
+ base::StringValue string_value("string value");
+ pref_hash_filter_->FilterUpdate(tracked_path_, &string_value);
+ // One path should be stored.
+ ASSERT_EQ(1u, mock_pref_hash_store_->stored_paths().size());
+ ASSERT_EQ(tracked_path_, *mock_pref_hash_store_->stored_paths().begin());
+ ASSERT_EQ("string value", mock_pref_hash_store_->stored_value(tracked_path_));
+}
+
+TEST_F(PrefHashFilterTest, EmptyAndUnknown){
+ mock_pref_hash_store_->SetCheckResult(tracked_path_,
+ PrefHashStore::UNKNOWN_VALUE);
+ pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
+ ASSERT_LT(0u, mock_pref_hash_store_->checked_paths().size());
+ ASSERT_EQ(1u, mock_pref_hash_store_->stored_paths().size());
+ ASSERT_EQ(tracked_path_, *mock_pref_hash_store_->stored_paths().begin());
+ ASSERT_EQ(MockPrefHashStore::kNullPlaceholder,
+ mock_pref_hash_store_->stored_value(tracked_path_));
+}
+
+TEST_F(PrefHashFilterTest, InitialValueUnknown) {
+ pref_store_contents_.Set(tracked_path_,
+ new base::StringValue("string value"));
+
+ mock_pref_hash_store_->SetCheckResult(tracked_path_,
+ PrefHashStore::UNKNOWN_VALUE);
+ pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
+ ASSERT_LT(0u, mock_pref_hash_store_->checked_paths().size());
+ ASSERT_EQ(1u, mock_pref_hash_store_->stored_paths().size());
+ ASSERT_EQ(tracked_path_, *mock_pref_hash_store_->stored_paths().begin());
+ ASSERT_EQ("string value", mock_pref_hash_store_->stored_value(tracked_path_));
+}
+
+TEST_F(PrefHashFilterTest, InitialValueChanged) {
+ pref_store_contents_.Set(tracked_path_,
+ new base::StringValue("string value"));
+
+ mock_pref_hash_store_->SetCheckResult(tracked_path_,
+ PrefHashStore::CHANGED);
+ pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
+ ASSERT_LT(0u, mock_pref_hash_store_->checked_paths().size());
+ ASSERT_EQ(1u, mock_pref_hash_store_->stored_paths().size());
+ ASSERT_EQ(tracked_path_, *mock_pref_hash_store_->stored_paths().begin());
+ ASSERT_EQ("string value", mock_pref_hash_store_->stored_value(tracked_path_));
+}
+
+TEST_F(PrefHashFilterTest, EmptyCleared) {
+ mock_pref_hash_store_->SetCheckResult(tracked_path_,
+ PrefHashStore::CLEARED);
+ pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
+ ASSERT_LT(0u, mock_pref_hash_store_->checked_paths().size());
+ ASSERT_EQ(1u, mock_pref_hash_store_->stored_paths().size());
+ ASSERT_EQ(tracked_path_, *mock_pref_hash_store_->stored_paths().begin());
+ ASSERT_EQ(MockPrefHashStore::kNullPlaceholder,
+ mock_pref_hash_store_->stored_value(tracked_path_));
+}
+
+TEST_F(PrefHashFilterTest, EmptyMigrated) {
+ mock_pref_hash_store_->SetCheckResult(tracked_path_,
+ PrefHashStore::MIGRATED);
+ pref_hash_filter_->FilterOnLoad(&pref_store_contents_);
+ ASSERT_LT(0u, mock_pref_hash_store_->checked_paths().size());
+ ASSERT_EQ(1u, mock_pref_hash_store_->stored_paths().size());
+ ASSERT_EQ(tracked_path_, *mock_pref_hash_store_->stored_paths().begin());
+ ASSERT_EQ(MockPrefHashStore::kNullPlaceholder,
+ mock_pref_hash_store_->stored_value(tracked_path_));
+}
diff --git a/chrome/browser/prefs/pref_hash_store.h b/chrome/browser/prefs/pref_hash_store.h
new file mode 100644
index 0000000..ebef8c0
--- /dev/null
+++ b/chrome/browser/prefs/pref_hash_store.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PREFS_PREF_HASH_STORE_H_
+#define CHROME_BROWSER_PREFS_PREF_HASH_STORE_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+class PrefHashTracker;
+
+namespace base {
+class Value;
+} // namespace base
+
+// Stores hashes of and verifies preference values. To use, first call
+// |InitializeTrackedValue| with each preference that should be tracked. Then
+// call |OnPrefValueChanged| to update the hash store when preference values
+// change.
+class PrefHashStore {
+ public:
+ virtual ~PrefHashStore() {}
+
+ enum ValueState {
+ // The preference value corresponds to its stored hash.
+ UNCHANGED,
+ // The preference has been cleared since the last hash.
+ CLEARED,
+ // The preference value corresponds to its stored hash, which was calculated
+ // using a legacy hash algorithm.
+ MIGRATED,
+ // The preference value has been changed since the last hash.
+ CHANGED,
+ // No stored hash exists for the preference value.
+ UNKNOWN_VALUE,
+ };
+
+ // Checks |initial_value| against the existing stored value hash.
+ virtual ValueState CheckValue(
+ const std::string& path, const base::Value* initial_value) const = 0;
+
+ // Stores a hash of the current value of the preference at |path|.
+ virtual void StoreHash(const std::string& path,
+ const base::Value* value) = 0;
+};
+
+#endif // CHROME_BROWSER_PREFS_PREF_HASH_STORE_H_
diff --git a/chrome/browser/prefs/pref_hash_store_impl.cc b/chrome/browser/prefs/pref_hash_store_impl.cc
new file mode 100644
index 0000000..0d08c55
--- /dev/null
+++ b/chrome/browser/prefs/pref_hash_store_impl.cc
@@ -0,0 +1,76 @@
+// 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_store_impl.h"
+
+#include "base/logging.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/pref_service.h"
+#include "base/prefs/scoped_user_pref_update.h"
+#include "base/values.h"
+#include "chrome/common/pref_names.h"
+
+PrefHashStoreImpl::PrefHashStoreImpl(const std::string& hash_store_id,
+ const std::string& seed,
+ const std::string& device_id,
+ PrefService* local_state)
+ : hash_store_id_(hash_store_id),
+ pref_hash_calculator_(seed, device_id),
+ local_state_(local_state) {}
+
+// static
+void PrefHashStoreImpl::RegisterPrefs(PrefRegistrySimple* registry) {
+ // Register the top level dictionary to map profile names to dictionaries of
+ // tracked preferences.
+ registry->RegisterDictionaryPref(prefs::kProfilePreferenceHashes);
+}
+
+PrefHashStore::ValueState PrefHashStoreImpl::CheckValue(
+ const std::string& path, const base::Value* initial_value) const {
+ const base::DictionaryValue* pref_hash_dicts =
+ local_state_->GetDictionary(prefs::kProfilePreferenceHashes);
+ const base::DictionaryValue* hashed_prefs = NULL;
+ pref_hash_dicts->GetDictionaryWithoutPathExpansion(hash_store_id_,
+ &hashed_prefs);
+
+ std::string last_hash;
+ if (!hashed_prefs || !hashed_prefs->GetString(path, &last_hash))
+ return PrefHashStore::UNKNOWN_VALUE;
+
+ PrefHashCalculator::ValidationResult validation_result =
+ pref_hash_calculator_.Validate(path, initial_value, last_hash);
+ switch (validation_result) {
+ case PrefHashCalculator::VALID:
+ return PrefHashStore::UNCHANGED;
+ case PrefHashCalculator::VALID_LEGACY:
+ return PrefHashStore::MIGRATED;
+ case PrefHashCalculator::INVALID:
+ return initial_value ? PrefHashStore::CHANGED : PrefHashStore::CLEARED;
+ }
+ NOTREACHED() << "Unexpected PrefHashCalculator::ValidationResult: "
+ << validation_result;
+ return PrefHashStore::UNKNOWN_VALUE;
+}
+
+void PrefHashStoreImpl::StoreHash(
+ const std::string& path, const base::Value* new_value) {
+ {
+ DictionaryPrefUpdate update(local_state_, prefs::kProfilePreferenceHashes);
+ DictionaryValue* child_dictionary = NULL;
+
+ // Get the dictionary corresponding to the profile name, which may have a
+ // '.'
+ if (!update->GetDictionaryWithoutPathExpansion(hash_store_id_,
+ &child_dictionary)) {
+ child_dictionary = new DictionaryValue;
+ update->SetWithoutPathExpansion(hash_store_id_, child_dictionary);
+ }
+
+ child_dictionary->SetString(
+ path, pref_hash_calculator_.Calculate(path, new_value));
+ }
+ // TODO(erikwright): During tests, pending writes were still waiting when the
+ // IO thread is already gone. Consider other solutions.
+ local_state_->CommitPendingWrite();
+}
diff --git a/chrome/browser/prefs/pref_hash_store_impl.h b/chrome/browser/prefs/pref_hash_store_impl.h
new file mode 100644
index 0000000..1d3f581
--- /dev/null
+++ b/chrome/browser/prefs/pref_hash_store_impl.h
@@ -0,0 +1,56 @@
+// 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.
+
+#ifndef CHROME_BROWSER_PREFS_PREF_HASH_STORE_IMPL_H_
+#define CHROME_BROWSER_PREFS_PREF_HASH_STORE_IMPL_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/prefs/pref_hash_calculator.h"
+#include "chrome/browser/prefs/pref_hash_store.h"
+
+class PrefRegistrySimple;
+class PrefService;
+
+namespace base {
+class Value;
+} // namespace base
+
+// Implements PrefHashStoreImpl by storing preference hashes in a PrefService.
+class PrefHashStoreImpl : public PrefHashStore {
+ public:
+ // Constructs a PrefHashStoreImpl that calculates hashes using
+ // |seed| and |device_id| and stores them in |local_state|. Multiple hash
+ // stores can use the same |local_state| with distinct |hash_store_id|s.
+ //
+ // The same |seed|, |device_id|, and |hash_store_id| must be used to load and
+ // validate previously stored hashes in |local_state|.
+ //
+ // |local_state| must have previously been passed to |RegisterPrefs|.
+ PrefHashStoreImpl(const std::string& hash_store_id,
+ const std::string& seed,
+ const std::string& device_id,
+ PrefService* local_state);
+
+ // Registers required local state preferences.
+ static void RegisterPrefs(PrefRegistrySimple* registry);
+
+ // PrefHashStore implementation.
+ virtual ValueState CheckValue(const std::string& path,
+ const base::Value* value) const OVERRIDE;
+ virtual void StoreHash(const std::string& path,
+ const base::Value* value) OVERRIDE;
+
+ private:
+ std::string hash_store_id_;
+ PrefHashCalculator pref_hash_calculator_;
+ PrefService* local_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefHashStoreImpl);
+};
+
+#endif // CHROME_BROWSER_PREFS_PREF_HASH_STORE_IMPL_H_
diff --git a/chrome/browser/prefs/pref_hash_store_impl_unittest.cc b/chrome/browser/prefs/pref_hash_store_impl_unittest.cc
new file mode 100644
index 0000000..330879a
--- /dev/null
+++ b/chrome/browser/prefs/pref_hash_store_impl_unittest.cc
@@ -0,0 +1,59 @@
+// 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_store_impl.h"
+
+#include <string>
+
+#include "base/prefs/pref_service.h"
+#include "base/prefs/scoped_user_pref_update.h"
+#include "base/prefs/testing_pref_service.h"
+#include "base/values.h"
+#include "chrome/browser/prefs/pref_hash_store.h"
+#include "chrome/common/pref_names.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(PrefHashStoreImplTest, TestCase) {
+ base::StringValue string_1("string1");
+ base::StringValue string_2("string2");
+
+ TestingPrefServiceSimple local_state;
+ PrefHashStoreImpl::RegisterPrefs(local_state.registry());
+
+ // 32 NULL bytes is the seed that was used to generate the legacy hash.
+ PrefHashStoreImpl pref_hash_store(
+ "store_id", std::string(32,0), "device_id", &local_state);
+
+ ASSERT_EQ(PrefHashStore::UNKNOWN_VALUE,
+ pref_hash_store.CheckValue("path1", &string_1));
+ pref_hash_store.StoreHash("path1", &string_1);
+ ASSERT_EQ(PrefHashStore::UNCHANGED,
+ pref_hash_store.CheckValue("path1", &string_1));
+ ASSERT_EQ(PrefHashStore::CLEARED, pref_hash_store.CheckValue("path1", NULL));
+ pref_hash_store.StoreHash("path1", NULL);
+ ASSERT_EQ(PrefHashStore::UNCHANGED,
+ pref_hash_store.CheckValue("path1", NULL));
+ ASSERT_EQ(PrefHashStore::CHANGED,
+ pref_hash_store.CheckValue("path1", &string_2));
+
+ DictionaryValue dict;
+ dict.Set("a", new StringValue("foo"));
+ dict.Set("d", new StringValue("bad"));
+ dict.Set("b", new StringValue("bar"));
+ dict.Set("c", new StringValue("baz"));
+
+ // Manually shove in a legacy hash.
+ DictionaryPrefUpdate update(&local_state, prefs::kProfilePreferenceHashes);
+ DictionaryValue* child_dictionary = NULL;
+ ASSERT_TRUE(update->GetDictionary("store_id", &child_dictionary));
+ child_dictionary->SetString(
+ "path1",
+ "C503FB7C65EEFD5C07185F616A0AA67923C069909933F362022B1F187E73E9A2");
+
+ ASSERT_EQ(PrefHashStore::MIGRATED,
+ pref_hash_store.CheckValue("path1", &dict));
+ pref_hash_store.StoreHash("path1", &dict);
+ ASSERT_EQ(PrefHashStore::UNCHANGED,
+ pref_hash_store.CheckValue("path1", &dict));
+}
diff --git a/chrome/browser/prefs/pref_metrics_service.cc b/chrome/browser/prefs/pref_metrics_service.cc
index dfce44b..8b6f389 100644
--- a/chrome/browser/prefs/pref_metrics_service.cc
+++ b/chrome/browser/prefs/pref_metrics_service.cc
@@ -6,18 +6,12 @@
#include "base/bind.h"
#include "base/command_line.h"
-#include "base/json/json_string_value_serializer.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
-#include "base/prefs/scoped_user_pref_update.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_shutdown.h"
-// Accessing the Device ID API here is a layering violation.
-// TODO(bbudge) Move the API so it's usable here.
-// http://crbug.com/276485
-#include "chrome/browser/extensions/api/music_manager_private/device_id.h"
#include "chrome/browser/prefs/pref_service_syncable.h"
#include "chrome/browser/prefs/session_startup_pref.h"
#include "chrome/browser/prefs/synced_pref_change_registrar.h"
@@ -29,97 +23,34 @@
#include "chrome/common/pref_names.h"
#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
#include "crypto/hmac.h"
-#include "grit/browser_resources.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
-#include "ui/base/resource/resource_bundle.h"
namespace {
const int kSessionStartupPrefValueMax = SessionStartupPref::kPrefValueMax;
-// An unregistered preference to fill in indices in kTrackedPrefs below for
-// preferences that aren't defined on every platform. This is fine as the code
-// below (e.g. CheckTrackedPreferences()) skips unregistered preferences and
-// should thus never report any data about that index on the platforms where
-// that preference is unimplemented.
-const char kUnregisteredPreference[] = "_";
-
-// These preferences must be kept in sync with the TrackedPreference enum in
-// tools/metrics/histograms/histograms.xml. To add a new preference, append it
-// to the array and add a corresponding value to the histogram enum.
-const char* kTrackedPrefs[] = {
- prefs::kShowHomeButton,
- prefs::kHomePageIsNewTabPage,
- prefs::kHomePage,
- prefs::kRestoreOnStartup,
- prefs::kURLsToRestoreOnStartup,
- prefs::kExtensionsPref,
- prefs::kGoogleServicesLastUsername,
- prefs::kSearchProviderOverrides,
- prefs::kDefaultSearchProviderSearchURL,
- prefs::kDefaultSearchProviderKeyword,
- prefs::kDefaultSearchProviderName,
-#if !defined(OS_ANDROID)
- prefs::kPinnedTabs,
-#else
- kUnregisteredPreference,
-#endif
- prefs::kExtensionKnownDisabled,
- prefs::kProfileResetPromptMemento,
-};
-
-const size_t kSHA256DigestSize = 32;
-
} // namespace
PrefMetricsService::PrefMetricsService(Profile* profile)
: profile_(profile),
prefs_(profile_->GetPrefs()),
local_state_(g_browser_process->local_state()),
- profile_name_(profile_->GetPath().AsUTF8Unsafe()),
- tracked_pref_paths_(kTrackedPrefs),
- tracked_pref_path_count_(arraysize(kTrackedPrefs)),
- checked_tracked_prefs_(false),
weak_factory_(this) {
- pref_hash_seed_ = ResourceBundle::GetSharedInstance().GetRawDataResource(
- IDR_PREF_HASH_SEED_BIN).as_string();
-
RecordLaunchPrefs();
PrefServiceSyncable* prefs = PrefServiceSyncable::FromProfile(profile_);
synced_pref_change_registrar_.reset(new SyncedPrefChangeRegistrar(prefs));
RegisterSyncedPrefObservers();
-
- // The following code might cause callbacks into this instance before we exit
- // the constructor. This instance should be initialized at this point.
-#if defined(OS_WIN) || defined(OS_MACOSX)
- // We need the machine id to compute pref value hashes. Fetch that, and then
- // call CheckTrackedPreferences in the callback.
- extensions::api::DeviceId::GetDeviceId(
- "PrefMetricsService", // non-empty string to obfuscate the device id.
- Bind(&PrefMetricsService::GetDeviceIdCallback,
- weak_factory_.GetWeakPtr()));
-#endif // defined(OS_WIN) || defined(OS_MACOSX)
}
// For unit testing only.
PrefMetricsService::PrefMetricsService(Profile* profile,
- PrefService* local_state,
- const std::string& device_id,
- const char** tracked_pref_paths,
- int tracked_pref_path_count)
+ PrefService* local_state)
: profile_(profile),
prefs_(profile->GetPrefs()),
local_state_(local_state),
- profile_name_(profile_->GetPath().AsUTF8Unsafe()),
- pref_hash_seed_(kSHA256DigestSize, 0),
- device_id_(device_id),
- tracked_pref_paths_(tracked_pref_paths),
- tracked_pref_path_count_(tracked_pref_path_count),
- checked_tracked_prefs_(false),
weak_factory_(this) {
- CheckTrackedPreferences();
}
PrefMetricsService::~PrefMetricsService() {
@@ -189,13 +120,6 @@ void PrefMetricsService::RecordLaunchPrefs() {
#endif
}
-// static
-void PrefMetricsService::RegisterPrefs(PrefRegistrySimple* registry) {
- // Register the top level dictionary to map profile names to dictionaries of
- // tracked preferences.
- registry->RegisterDictionaryPref(prefs::kProfilePreferenceHashes);
-}
-
void PrefMetricsService::RegisterSyncedPrefObservers() {
LogHistogramValueCallback booleanHandler = base::Bind(
&PrefMetricsService::LogBooleanPrefChange, base::Unretained(this));
@@ -259,173 +183,6 @@ void PrefMetricsService::LogIntegerPrefChange(int boundary_value,
histogram->Add(integer_value);
}
-void PrefMetricsService::GetDeviceIdCallback(const std::string& device_id) {
-#if !defined(OS_WIN) || defined(ENABLE_RLZ)
- // A device_id is expected in all scenarios except when RLZ is disabled on
- // Windows.
- DCHECK(!device_id.empty());
-#endif
-
- device_id_ = device_id;
- CheckTrackedPreferences();
-}
-
-// To detect changes to Preferences that happen outside of Chrome, we hash
-// selected pref values and save them in local state. CheckTrackedPreferences
-// compares the saved values to the values observed in the profile's prefs. A
-// dictionary of dictionaries in local state holds the hashed values, grouped by
-// profile. To make the system more resistant to spoofing, pref values are
-// hashed with the pref path and the device id.
-void PrefMetricsService::CheckTrackedPreferences() {
- // Make sure this is only called once per instance of this service.
- DCHECK(!checked_tracked_prefs_);
- checked_tracked_prefs_ = true;
-
- const base::DictionaryValue* pref_hash_dicts =
- local_state_->GetDictionary(prefs::kProfilePreferenceHashes);
- // Get the hashed prefs dictionary if it exists. If it doesn't, it will be
- // created if we set preference values below.
- const base::DictionaryValue* hashed_prefs = NULL;
- pref_hash_dicts->GetDictionaryWithoutPathExpansion(profile_name_,
- &hashed_prefs);
- for (int i = 0; i < tracked_pref_path_count_; ++i) {
- if (!prefs_->FindPreference(tracked_pref_paths_[i])) {
- // All tracked preferences need to have been registered already.
- DCHECK_EQ(kUnregisteredPreference, tracked_pref_paths_[i]);
- continue;
- }
-
- const base::Value* value = prefs_->GetUserPrefValue(tracked_pref_paths_[i]);
- std::string last_hash;
- // First try to get the stored expected hash...
- if (hashed_prefs &&
- hashed_prefs->GetString(tracked_pref_paths_[i], &last_hash)) {
- // ... if we have one get the hash of the current value...
- const std::string value_hash =
- GetHashedPrefValue(tracked_pref_paths_[i], value,
- HASHED_PREF_STYLE_NEW);
- // ... and check that it matches...
- if (value_hash == last_hash) {
- UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceUnchanged",
- i, tracked_pref_path_count_);
- } else {
- // ... if it doesn't: was the value simply cleared?
- if (!value) {
- UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceCleared",
- i, tracked_pref_path_count_);
- } else {
- // ... or does it match the old style hash?
- std::string old_style_hash =
- GetHashedPrefValue(tracked_pref_paths_[i], value,
- HASHED_PREF_STYLE_DEPRECATED);
- if (old_style_hash == last_hash) {
- UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceMigrated",
- i, tracked_pref_path_count_);
- } else {
- // ... or was it simply changed to something else?
- UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceChanged",
- i, tracked_pref_path_count_);
- }
- }
-
- // ... either way update the expected hash to match the new value.
- UpdateTrackedPreference(tracked_pref_paths_[i]);
- }
- } else {
- // Record that we haven't tracked this preference yet, or the hash in
- // local state was removed.
- UMA_HISTOGRAM_ENUMERATION("Settings.TrackedPreferenceInitialized",
- i, tracked_pref_path_count_);
- UpdateTrackedPreference(tracked_pref_paths_[i]);
- }
- }
-
- // Now that we've checked the incoming preferences, register for change
- // notifications, unless this is test code.
- // TODO(bbudge) Fix failing browser_tests and so we can remove this test.
- // Several tests fail when they shutdown before they can write local state.
- if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType) &&
- !CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeFrame)) {
- InitializePrefObservers();
- }
-}
-
-void PrefMetricsService::UpdateTrackedPreference(const char* path) {
- const base::Value* value = prefs_->GetUserPrefValue(path);
- DictionaryPrefUpdate update(local_state_, prefs::kProfilePreferenceHashes);
- DictionaryValue* child_dictionary = NULL;
-
- // Get the dictionary corresponding to the profile name,
- // which may have a '.'
- if (!update->GetDictionaryWithoutPathExpansion(profile_name_,
- &child_dictionary)) {
- child_dictionary = new DictionaryValue;
- update->SetWithoutPathExpansion(profile_name_, child_dictionary);
- }
-
- child_dictionary->SetString(path,
- GetHashedPrefValue(path, value,
- HASHED_PREF_STYLE_NEW));
-}
-
-std::string PrefMetricsService::GetHashedPrefValue(
- const char* path,
- const base::Value* value,
- HashedPrefStyle desired_style) {
- // Dictionary values may contain empty lists and sub-dictionaries. Make a
- // deep copy with those removed to make the hash more stable.
- const DictionaryValue* dict_value;
- scoped_ptr<DictionaryValue> canonical_dict_value;
- if (value &&
- value->GetAsDictionary(&dict_value)) {
- canonical_dict_value.reset(dict_value->DeepCopyWithoutEmptyChildren());
- value = canonical_dict_value.get();
- }
-
- std::string value_as_string;
- // If |value| is NULL, we will still build a unique hash based on |device_id_|
- // and |path| below.
- if (value) {
- JSONStringValueSerializer serializer(&value_as_string);
- serializer.Serialize(*value);
- }
-
- std::string string_to_hash;
- // TODO(gab): Remove this as the old style is phased out.
- if (desired_style == HASHED_PREF_STYLE_NEW) {
- string_to_hash.append(device_id_);
- string_to_hash.append(path);
- }
- string_to_hash.append(value_as_string);
-
- crypto::HMAC hmac(crypto::HMAC::SHA256);
- unsigned char digest[kSHA256DigestSize];
- if (!hmac.Init(pref_hash_seed_) ||
- !hmac.Sign(string_to_hash, digest, kSHA256DigestSize)) {
- NOTREACHED();
- return std::string();
- }
-
- return base::HexEncode(digest, kSHA256DigestSize);
-}
-
-void PrefMetricsService::InitializePrefObservers() {
- pref_registrar_.Init(prefs_);
- for (int i = 0; i < tracked_pref_path_count_; ++i) {
- if (!prefs_->FindPreference(tracked_pref_paths_[i])) {
- // All tracked preferences need to have been registered already.
- DCHECK_EQ(kUnregisteredPreference, tracked_pref_paths_[i]);
- continue;
- }
-
- pref_registrar_.Add(
- tracked_pref_paths_[i],
- base::Bind(&PrefMetricsService::UpdateTrackedPreference,
- weak_factory_.GetWeakPtr(),
- tracked_pref_paths_[i]));
- }
-}
-
// static
PrefMetricsService::Factory* PrefMetricsService::Factory::GetInstance() {
return Singleton<PrefMetricsService::Factory>::get();
diff --git a/chrome/browser/prefs/pref_metrics_service.h b/chrome/browser/prefs/pref_metrics_service.h
index d38a006..c975a43 100644
--- a/chrome/browser/prefs/pref_metrics_service.h
+++ b/chrome/browser/prefs/pref_metrics_service.h
@@ -22,11 +22,6 @@ class PrefRegistrySimple;
// PrefMetricsService is responsible for recording prefs-related UMA stats.
class PrefMetricsService : public BrowserContextKeyedService {
public:
- enum HashedPrefStyle {
- HASHED_PREF_STYLE_NEW,
- HASHED_PREF_STYLE_DEPRECATED,
- };
-
explicit PrefMetricsService(Profile* profile);
virtual ~PrefMetricsService();
@@ -49,22 +44,15 @@ class PrefMetricsService : public BrowserContextKeyedService {
content::BrowserContext* context) const OVERRIDE;
};
- // Registers preferences in local state.
- static void RegisterPrefs(PrefRegistrySimple* registry);
-
private:
friend class PrefMetricsServiceTest;
// Function to log a Value to a histogram
- typedef base::Callback<void(const std::string&, const Value*)>
+ typedef base::Callback<void(const std::string&, const base::Value*)>
LogHistogramValueCallback;
// For unit testing only.
- PrefMetricsService(Profile* profile,
- PrefService* local_settings,
- const std::string& device_id,
- const char** tracked_pref_paths,
- int tracked_pref_path_count);
+ PrefMetricsService(Profile* profile, PrefService* local_settings);
// Record prefs state on browser context creation.
void RecordLaunchPrefs();
@@ -85,46 +73,16 @@ class PrefMetricsService : public BrowserContextKeyedService {
// Callback for a boolean pref change histogram.
void LogBooleanPrefChange(const std::string& histogram_name,
- const Value* value);
+ const base::Value* value);
// Callback for an integer pref change histogram.
void LogIntegerPrefChange(int boundary_value,
const std::string& histogram_name,
- const Value* value);
-
- // Callback to receive a unique device_id.
- void GetDeviceIdCallback(const std::string& device_id);
-
- // Checks the tracked preferences against their last known values and reports
- // any discrepancies. This must be called after |device_id| has been set.
- void CheckTrackedPreferences();
-
- // Updates the hash of the tracked preference in local state. This must be
- // called after |device_id| has been set.
- void UpdateTrackedPreference(const char* path);
-
- // Computes an MD5 hash for the given preference value. |value| can be
- // NULL which will result in the unique hash representing NULL for the pref
- // at |path|.
- std::string GetHashedPrefValue(
- const char* path,
- const base::Value* value,
- HashedPrefStyle desired_style);
-
- void InitializePrefObservers();
+ const base::Value* value);
Profile* profile_;
PrefService* prefs_;
PrefService* local_state_;
- std::string profile_name_;
- std::string pref_hash_seed_;
- std::string device_id_;
- const char** tracked_pref_paths_;
- const int tracked_pref_path_count_;
-
- // TODO(gab): preprocessor define this member out on builds that don't use
- // DCHECKs (http://crbug.com/322713).
- bool checked_tracked_prefs_;
PrefChangeRegistrar pref_registrar_;
scoped_ptr<SyncedPrefChangeRegistrar> synced_pref_change_registrar_;
diff --git a/chrome/browser/prefs/pref_metrics_service_unittest.cc b/chrome/browser/prefs/pref_metrics_service_unittest.cc
deleted file mode 100644
index e80d408..0000000
--- a/chrome/browser/prefs/pref_metrics_service_unittest.cc
+++ /dev/null
@@ -1,478 +0,0 @@
-// 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 "base/memory/scoped_ptr.h"
-#include "base/metrics/histogram.h"
-#include "base/metrics/statistics_recorder.h"
-#include "base/prefs/scoped_user_pref_update.h"
-#include "base/prefs/testing_pref_service.h"
-#include "base/values.h"
-#include "chrome/browser/prefs/pref_metrics_service.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_pref_service_syncable.h"
-#include "chrome/test/base/testing_profile.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "components/user_prefs/pref_registry_syncable.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-// TestingProfile may register some real preferences; to avoid interference,
-// define fake preferences for testing.
-const char* kTrackedPrefs[] = {
- "pref_metrics_service_test.pref1",
- "pref_metrics_service_test.pref2",
-};
-
-const int kTrackedPrefCount = arraysize(kTrackedPrefs);
-
-const char kTestDeviceId[] = "test_device_id1";
-const char kOtherTestDeviceId[] = "test_device_id2";
-
-} // namespace
-
-class PrefMetricsServiceTest : public testing::Test {
- protected:
- virtual void SetUp() {
- pref1_changed_ = 0;
- pref2_changed_ = 0;
- pref1_cleared_ = 0;
- pref2_cleared_ = 0;
- pref1_initialized_ = 0;
- pref2_initialized_ = 0;
- pref1_migrated_ = 0;
- pref2_migrated_ = 0;
- pref1_unchanged_ = 0;
- pref2_unchanged_ = 0;
-
- base::StatisticsRecorder::Initialize();
-
- // Reset and set up the profile manager.
- profile_manager_.reset(new TestingProfileManager(
- TestingBrowserProcess::GetGlobal()));
- ASSERT_TRUE(profile_manager_->SetUp());
-
- // Check that PrefMetricsService behaves with a '.' in the profile name.
- profile_ = profile_manager_->CreateTestingProfile("test@example.com");
-
- profile_name_ = profile_->GetPath().AsUTF8Unsafe();
-
- prefs_ = profile_->GetTestingPrefService();
-
- // Register our test-only tracked prefs as string values.
- for (int i = 0; i < kTrackedPrefCount; ++i) {
- prefs_->registry()->RegisterStringPref(
- kTrackedPrefs[i],
- "test_default_value",
- user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
- }
-
- // Initialize pref in local state that holds hashed values.
- PrefMetricsService::RegisterPrefs(local_state_.registry());
-
- // Update global counts in case another test left stray samples.
- UpdateHistogramSamples();
- }
-
- scoped_ptr<PrefMetricsService> CreatePrefMetricsService(
- const std::string& device_id) {
- return scoped_ptr<PrefMetricsService>(
- new PrefMetricsService(profile_,
- &local_state_,
- device_id,
- kTrackedPrefs,
- kTrackedPrefCount));
- }
-
- std::string GetHashedPrefValue(PrefMetricsService* service,
- const char* path,
- const base::Value* value) {
- return service->GetHashedPrefValue(
- path, value, PrefMetricsService::HASHED_PREF_STYLE_NEW);
- }
-
- std::string GetOldStyleHashedPrefValue(PrefMetricsService* service,
- const char* path,
- const base::Value* value) {
- return service->GetHashedPrefValue(
- path, value, PrefMetricsService::HASHED_PREF_STYLE_DEPRECATED);
- }
-
- void GetSamples(const char* histogram_name, int* bucket1, int* bucket2) {
- base::HistogramBase* histogram =
- base::StatisticsRecorder::FindHistogram(histogram_name);
- if (!histogram) {
- *bucket1 = 0;
- *bucket2 = 0;
- } else {
- scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples());
- *bucket1 = samples->GetCount(0);
- *bucket2 = samples->GetCount(1);
- }
- }
-
- void UpdateHistogramSamples() {
- int changed1, changed2;
- GetSamples("Settings.TrackedPreferenceChanged", &changed1, &changed2);
- pref1_changed_ = changed1 - pref1_changed_total;
- pref2_changed_ = changed2 - pref2_changed_total;
- pref1_changed_total = changed1;
- pref2_changed_total = changed2;
-
- int cleared1, cleared2;
- GetSamples("Settings.TrackedPreferenceCleared", &cleared1, &cleared2);
- pref1_cleared_ = cleared1 - pref1_cleared_total;
- pref2_cleared_ = cleared2 - pref2_cleared_total;
- pref1_cleared_total = cleared1;
- pref2_cleared_total = cleared2;
-
- int inited1, inited2;
- GetSamples("Settings.TrackedPreferenceInitialized", &inited1, &inited2);
- pref1_initialized_ = inited1 - pref1_initialized_total;
- pref2_initialized_ = inited2 - pref2_initialized_total;
- pref1_initialized_total = inited1;
- pref2_initialized_total = inited2;
-
- int migrated1, migrated2;
- GetSamples("Settings.TrackedPreferenceMigrated", &migrated1, &migrated2);
- pref1_migrated_ = migrated1 - pref1_migrated_total;
- pref2_migrated_ = migrated2 - pref2_migrated_total;
- pref1_migrated_total = migrated1;
- pref2_migrated_total = migrated2;
-
- int unchanged1, unchanged2;
- GetSamples("Settings.TrackedPreferenceUnchanged", &unchanged1, &unchanged2);
- pref1_unchanged_ = unchanged1 - pref1_unchanged_total;
- pref2_unchanged_ = unchanged2 - pref2_unchanged_total;
- pref1_unchanged_total = unchanged1;
- pref2_unchanged_total = unchanged2;
- }
-
- TestingProfile* profile_;
- std::string profile_name_;
- scoped_ptr<TestingProfileManager> profile_manager_;
- TestingPrefServiceSyncable* prefs_;
- TestingPrefServiceSimple local_state_;
-
- // Since histogram samples are recorded by a global StatisticsRecorder, we
- // need to maintain total counts so we can compute deltas for individual
- // tests.
- static int pref1_changed_total;
- static int pref2_changed_total;
- static int pref1_cleared_total;
- static int pref2_cleared_total;
- static int pref1_initialized_total;
- static int pref2_initialized_total;
- static int pref1_migrated_total;
- static int pref2_migrated_total;
- static int pref1_unchanged_total;
- static int pref2_unchanged_total;
-
- // Counts of samples recorded since UpdateHistogramSamples was last called.
- int pref1_changed_;
- int pref2_changed_;
- int pref1_cleared_;
- int pref2_cleared_;
- int pref1_initialized_;
- int pref2_initialized_;
- int pref1_migrated_;
- int pref2_migrated_;
- int pref1_unchanged_;
- int pref2_unchanged_;
-};
-
-int PrefMetricsServiceTest::pref1_changed_total;
-int PrefMetricsServiceTest::pref2_changed_total;
-int PrefMetricsServiceTest::pref1_cleared_total;
-int PrefMetricsServiceTest::pref2_cleared_total;
-int PrefMetricsServiceTest::pref1_initialized_total;
-int PrefMetricsServiceTest::pref2_initialized_total;
-int PrefMetricsServiceTest::pref1_migrated_total;
-int PrefMetricsServiceTest::pref2_migrated_total;
-int PrefMetricsServiceTest::pref1_unchanged_total;
-int PrefMetricsServiceTest::pref2_unchanged_total;
-
-TEST_F(PrefMetricsServiceTest, StartupNoUserPref) {
- // Local state is empty and no user prefs are set. We should still have
- // initialized all preferences once.
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
- UpdateHistogramSamples();
- EXPECT_EQ(0, pref1_changed_);
- EXPECT_EQ(0, pref2_changed_);
- EXPECT_EQ(0, pref1_cleared_);
- EXPECT_EQ(0, pref2_cleared_);
- EXPECT_EQ(1, pref1_initialized_);
- EXPECT_EQ(1, pref2_initialized_);
- EXPECT_EQ(0, pref1_migrated_);
- EXPECT_EQ(0, pref2_migrated_);
- EXPECT_EQ(0, pref1_unchanged_);
- EXPECT_EQ(0, pref2_unchanged_);
-
- // Ensure that each pref got a hash even though their value is NULL (i.e.,
- // empty).
- const DictionaryValue* root_dictionary =
- local_state_.GetDictionary(prefs::kProfilePreferenceHashes);
- ASSERT_TRUE(root_dictionary != NULL);
-
- const DictionaryValue* child_dictionary = NULL;
- ASSERT_TRUE(root_dictionary->GetDictionaryWithoutPathExpansion(
- profile_name_, &child_dictionary));
-
- std::string pref1_hash;
- std::string pref2_hash;
- ASSERT_TRUE(child_dictionary->GetString(kTrackedPrefs[0], &pref1_hash));
- ASSERT_TRUE(child_dictionary->GetString(kTrackedPrefs[1], &pref2_hash));
-
- // These two hashes are expected to be different as the paths on which they're
- // based differ.
- EXPECT_EQ("2A38C5000E1EDC2D5FA3B6A8E1D3B54068E32D329324D0D8C1AADA65BBDB20B3",
- pref1_hash);
- EXPECT_EQ("C4FEB38BDADD16CC642815B9798FEA70BF92C6CA9250BACD6993701196D72067",
- pref2_hash);
-}
-
-TEST_F(PrefMetricsServiceTest, StartupUserPref) {
- // Local state is empty. Set a value for one tracked pref. We should record
- // that we checked preferences once and initialized a hash for the pref.
- prefs_->SetString(kTrackedPrefs[0], "foo");
- {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
- UpdateHistogramSamples();
- EXPECT_EQ(0, pref1_changed_);
- EXPECT_EQ(0, pref2_changed_);
- EXPECT_EQ(0, pref1_cleared_);
- EXPECT_EQ(0, pref2_cleared_);
- EXPECT_EQ(1, pref1_initialized_);
- EXPECT_EQ(1, pref2_initialized_);
- EXPECT_EQ(0, pref1_migrated_);
- EXPECT_EQ(0, pref2_migrated_);
- EXPECT_EQ(0, pref1_unchanged_);
- EXPECT_EQ(0, pref2_unchanged_);
-
- // Change the pref. This should be observed by the PrefMetricsService, which
- // will update the hash in local_state_ to stay in sync.
- prefs_->SetString(kTrackedPrefs[0], "bar");
- }
- // The next startup should record no changes.
- {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
- UpdateHistogramSamples();
- EXPECT_EQ(0, pref1_changed_);
- EXPECT_EQ(0, pref2_changed_);
- EXPECT_EQ(0, pref1_cleared_);
- EXPECT_EQ(0, pref2_cleared_);
- EXPECT_EQ(0, pref1_initialized_);
- EXPECT_EQ(0, pref2_initialized_);
- EXPECT_EQ(0, pref1_migrated_);
- EXPECT_EQ(0, pref2_migrated_);
- EXPECT_EQ(1, pref1_unchanged_);
- EXPECT_EQ(1, pref2_unchanged_);
- }
-}
-
-TEST_F(PrefMetricsServiceTest, ChangedUserPref) {
- // Local state is empty. Set a value for the tracked pref. We should record
- // that we checked preferences once and initialized a hash for the pref.
- prefs_->SetString(kTrackedPrefs[0], "foo");
- {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
- UpdateHistogramSamples();
- EXPECT_EQ(0, pref1_changed_);
- EXPECT_EQ(0, pref2_changed_);
- EXPECT_EQ(0, pref1_cleared_);
- EXPECT_EQ(0, pref2_cleared_);
- EXPECT_EQ(1, pref1_initialized_);
- EXPECT_EQ(1, pref2_initialized_);
- EXPECT_EQ(0, pref1_migrated_);
- EXPECT_EQ(0, pref2_migrated_);
- EXPECT_EQ(0, pref1_unchanged_);
- EXPECT_EQ(0, pref2_unchanged_);
- // Hashed prefs should now be stored in local state.
- }
- // Change the value of the tracked pref while there is no PrefMetricsService
- // to update the hash. We should observe a pref value change.
- prefs_->SetString(kTrackedPrefs[0], "bar");
- {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
- UpdateHistogramSamples();
- EXPECT_EQ(1, pref1_changed_);
- EXPECT_EQ(0, pref2_changed_);
- EXPECT_EQ(0, pref1_cleared_);
- EXPECT_EQ(0, pref2_cleared_);
- EXPECT_EQ(0, pref1_initialized_);
- EXPECT_EQ(0, pref2_initialized_);
- EXPECT_EQ(0, pref1_migrated_);
- EXPECT_EQ(0, pref2_migrated_);
- EXPECT_EQ(0, pref1_unchanged_);
- EXPECT_EQ(1, pref2_unchanged_);
- }
- // Clear the value of the tracked pref while there is no PrefMetricsService
- // to update the hash. We should observe a pref value removal.
- prefs_->ClearPref(kTrackedPrefs[0]);
- {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
- UpdateHistogramSamples();
- EXPECT_EQ(0, pref1_changed_);
- EXPECT_EQ(0, pref2_changed_);
- EXPECT_EQ(1, pref1_cleared_);
- EXPECT_EQ(0, pref2_cleared_);
- EXPECT_EQ(0, pref1_initialized_);
- EXPECT_EQ(0, pref2_initialized_);
- EXPECT_EQ(0, pref1_migrated_);
- EXPECT_EQ(0, pref2_migrated_);
- EXPECT_EQ(0, pref1_unchanged_);
- EXPECT_EQ(1, pref2_unchanged_);
- }
-}
-
-TEST_F(PrefMetricsServiceTest, MigratedUserPref) {
- // Initialize both preferences and get the old style hash for the first pref
- // from the PrefMetricsService before shutting it down.
- prefs_->SetString(kTrackedPrefs[0], "foo");
- prefs_->SetString(kTrackedPrefs[1], "bar");
- std::string old_style_hash;
- {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
- UpdateHistogramSamples();
- EXPECT_EQ(0, pref1_changed_);
- EXPECT_EQ(0, pref2_changed_);
- EXPECT_EQ(0, pref1_cleared_);
- EXPECT_EQ(0, pref2_cleared_);
- EXPECT_EQ(1, pref1_initialized_);
- EXPECT_EQ(1, pref2_initialized_);
- EXPECT_EQ(0, pref1_migrated_);
- EXPECT_EQ(0, pref2_migrated_);
- EXPECT_EQ(0, pref1_unchanged_);
- EXPECT_EQ(0, pref2_unchanged_);
-
- old_style_hash =
- GetOldStyleHashedPrefValue(service.get(), kTrackedPrefs[0],
- prefs_->GetUserPrefValue(kTrackedPrefs[0]));
- }
-
- // Update the pref's hash to use the old style while the PrefMetricsService
- // isn't running.
- {
- DictionaryPrefUpdate update(&local_state_, prefs::kProfilePreferenceHashes);
- DictionaryValue* child_dictionary = NULL;
- // Get the dictionary corresponding to the profile name,
- // which may have a '.'
- ASSERT_TRUE(update->GetDictionaryWithoutPathExpansion(profile_name_,
- &child_dictionary));
- child_dictionary->SetString(kTrackedPrefs[0], old_style_hash);
- }
-
- // Relaunch the service and make sure the first preference got migrated.
- {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
- UpdateHistogramSamples();
- EXPECT_EQ(0, pref1_changed_);
- EXPECT_EQ(0, pref2_changed_);
- EXPECT_EQ(0, pref1_cleared_);
- EXPECT_EQ(0, pref2_cleared_);
- EXPECT_EQ(0, pref1_initialized_);
- EXPECT_EQ(0, pref2_initialized_);
- EXPECT_EQ(1, pref1_migrated_);
- EXPECT_EQ(0, pref2_migrated_);
- EXPECT_EQ(0, pref1_unchanged_);
- EXPECT_EQ(1, pref2_unchanged_);
- }
- // Make sure the migration happens only once.
- {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
- UpdateHistogramSamples();
- EXPECT_EQ(0, pref1_changed_);
- EXPECT_EQ(0, pref2_changed_);
- EXPECT_EQ(0, pref1_cleared_);
- EXPECT_EQ(0, pref2_cleared_);
- EXPECT_EQ(0, pref1_initialized_);
- EXPECT_EQ(0, pref2_initialized_);
- EXPECT_EQ(0, pref1_migrated_);
- EXPECT_EQ(0, pref2_migrated_);
- EXPECT_EQ(1, pref1_unchanged_);
- EXPECT_EQ(1, pref2_unchanged_);
- }
-}
-
-// Make sure that the new algorithm is still able to generate old style hashes
-// as they were before this change.
-TEST_F(PrefMetricsServiceTest, OldStyleHashAsExpected) {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
-
- // Verify the hashes match the values previously used in the
- // "PrefHashStability" test below.
- DictionaryValue dict;
- dict.Set("a", new StringValue("foo"));
- dict.Set("d", new StringValue("bad"));
- dict.Set("b", new StringValue("bar"));
- dict.Set("c", new StringValue("baz"));
- EXPECT_EQ("C503FB7C65EEFD5C07185F616A0AA67923C069909933F362022B1F187E73E9A2",
- GetOldStyleHashedPrefValue(service.get(), "pref.path1", &dict));
- ListValue list;
- list.Set(0, new base::FundamentalValue(true));
- list.Set(1, new base::FundamentalValue(100));
- list.Set(2, new base::FundamentalValue(1.0));
- EXPECT_EQ("3163EC3C96263143AF83EA5C9860DFB960EE2263413C7D7D8A9973FCC00E7692",
- GetOldStyleHashedPrefValue(service.get(), "pref.path2", &list));
-}
-
-// Tests that serialization of dictionary values is stable. If the order of
-// the entries or any whitespace changes, it would cause a spike in pref change
-// UMA events as every hash would change.
-TEST_F(PrefMetricsServiceTest, PrefHashStability) {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
-
- DictionaryValue dict;
- dict.Set("a", new StringValue("foo"));
- dict.Set("d", new StringValue("bad"));
- dict.Set("b", new StringValue("bar"));
- dict.Set("c", new StringValue("baz"));
- EXPECT_EQ("A50FE7EB31BFBC32B8A27E71730AF15421178A9B5815644ACE174B18966735B9",
- GetHashedPrefValue(service.get(), "pref.path1", &dict));
-
- ListValue list;
- list.Set(0, new base::FundamentalValue(true));
- list.Set(1, new base::FundamentalValue(100));
- list.Set(2, new base::FundamentalValue(1.0));
- EXPECT_EQ("5CE37D7EBCBC9BE510F0F5E7C326CA92C1673713C3717839610AEA1A217D8BB8",
- GetHashedPrefValue(service.get(), "pref.path2", &list));
-}
-
-// Tests that different hashes are generated for different device IDs.
-TEST_F(PrefMetricsServiceTest, HashIsBasedOnDeviceId) {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
- scoped_ptr<PrefMetricsService> other_service =
- CreatePrefMetricsService(kOtherTestDeviceId);
-
- StringValue test_value("test value");
- EXPECT_EQ("49CA276F9F2AEDCF6BFA1CD9FC4747476E1315BBBBC27DD33548B23CD36E2EEE",
- GetHashedPrefValue(service.get(), "pref.path", &test_value));
- EXPECT_EQ("13EEDA99C38777ADA8B87C23A3C5CD1FD31ADE1491823E255D3520E5B56C4BC7",
- GetHashedPrefValue(other_service.get(), "pref.path", &test_value));
-}
-
-// Tests that different hashes are generated for different paths.
-TEST_F(PrefMetricsServiceTest, HashIsBasedOnPath) {
- scoped_ptr<PrefMetricsService> service =
- CreatePrefMetricsService(kTestDeviceId);
-
- StringValue test_value("test value");
- EXPECT_EQ("2A5DCB1294F212DB26DF9C08C46F11C272D80136AAD3B4AAE5B7D008DF5F3F22",
- GetHashedPrefValue(service.get(), "pref.path1", &test_value));
- EXPECT_EQ("455EC2A7E192E9F1C06294BBB3B66BBD81B8D1A8550D518EA5D5C8F70FCF6EF3",
- GetHashedPrefValue(service.get(), "pref.path2", &test_value));
-}
diff --git a/chrome/browser/prefs/pref_service_browsertest.cc b/chrome/browser/prefs/pref_service_browsertest.cc
index f524981..26f8820 100644
--- a/chrome/browser/prefs/pref_service_browsertest.cc
+++ b/chrome/browser/prefs/pref_service_browsertest.cc
@@ -65,9 +65,8 @@ class PreferenceServiceTest : public InProcessBrowserTest {
base::FilePath user_data_directory;
PathService::Get(chrome::DIR_USER_DATA, &user_data_directory);
- base::FilePath reference_pref_file;
if (new_profile_) {
- reference_pref_file = ui_test_utils::GetTestFilePath(
+ original_pref_file_ = ui_test_utils::GetTestFilePath(
base::FilePath().AppendASCII("profiles").
AppendASCII("window_placement").
AppendASCII("Default"),
@@ -77,17 +76,17 @@ class PreferenceServiceTest : public InProcessBrowserTest {
CHECK(base::CreateDirectory(tmp_pref_file_));
tmp_pref_file_ = tmp_pref_file_.Append(chrome::kPreferencesFilename);
} else {
- reference_pref_file = ui_test_utils::GetTestFilePath(
+ original_pref_file_ = ui_test_utils::GetTestFilePath(
base::FilePath().AppendASCII("profiles").
AppendASCII("window_placement"),
base::FilePath().Append(chrome::kLocalStateFilename));
tmp_pref_file_ = user_data_directory.Append(chrome::kLocalStateFilename);
}
- CHECK(base::PathExists(reference_pref_file));
+ CHECK(base::PathExists(original_pref_file_));
// Copy only the Preferences file if |new_profile_|, or Local State if not,
// and the rest will be automatically created.
- CHECK(base::CopyFile(reference_pref_file, tmp_pref_file_));
+ CHECK(base::CopyFile(original_pref_file_, tmp_pref_file_));
#if defined(OS_WIN)
// Make the copy writable. On POSIX we assume the umask allows files
@@ -99,6 +98,7 @@ class PreferenceServiceTest : public InProcessBrowserTest {
}
protected:
+ base::FilePath original_pref_file_;
base::FilePath tmp_pref_file_;
private:
@@ -127,7 +127,7 @@ IN_PROC_BROWSER_TEST_F(PreservedWindowPlacementIsLoaded, Test) {
// The window should open with the new reference profile, with window
// placement values stored in the user data directory.
- JSONFileValueSerializer deserializer(tmp_pref_file_);
+ JSONFileValueSerializer deserializer(original_pref_file_);
scoped_ptr<base::Value> root(deserializer.Deserialize(NULL, NULL));
ASSERT_TRUE(root.get());
@@ -188,7 +188,7 @@ IN_PROC_BROWSER_TEST_F(PreservedWindowPlacementIsMigrated, Test) {
// The window should open with the old reference profile, with window
// placement values stored in Local State.
- JSONFileValueSerializer deserializer(tmp_pref_file_);
+ JSONFileValueSerializer deserializer(original_pref_file_);
scoped_ptr<base::Value> root(deserializer.Deserialize(NULL, NULL));
ASSERT_TRUE(root.get());
diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc
index d8c3ce3..41dea64 100644
--- a/chrome/browser/profiles/profile_impl.cc
+++ b/chrome/browser/profiles/profile_impl.cc
@@ -60,6 +60,7 @@
#include "chrome/browser/policy/profile_policy_connector_factory.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/prefs/chrome_pref_service_factory.h"
+#include "chrome/browser/prefs/pref_hash_store_impl.h"
#include "chrome/browser/prefs/pref_service_syncable.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/profiles/bookmark_model_loaded_observer.h"
@@ -93,6 +94,7 @@
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_constants.h"
+#include "grit/browser_resources.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
@@ -118,6 +120,9 @@
#if defined(OS_WIN)
#include "chrome/browser/profiles/file_path_verifier_win.h"
#include "chrome/installer/util/install_util.h"
+#if defined(ENABLE_RLZ)
+#include "rlz/lib/machine_id.h"
+#endif
#endif
#if defined(OS_CHROMEOS)
@@ -257,6 +262,33 @@ void SchedulePrefsFileVerification(const base::FilePath& prefs_file) {
#endif
}
+scoped_ptr<PrefHashStore> GetPrefHashStore(Profile* profile) {
+ // TODO(erikwright): Enable this on Android when race condition is sorted out.
+#if defined(OS_ANDROID)
+ return scoped_ptr<PrefHashStore>();
+#else
+ std::string seed = ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_PREF_HASH_SEED_BIN).as_string();
+ std::string device_id;
+
+#if defined(OS_WIN) && defined(ENABLE_RLZ)
+ // This is used by
+ // chrome/browser/extensions/api/music_manager_private/device_id_win.cc
+ // but that API is private (http://crbug.com/276485) and other platforms are
+ // not available synchronously.
+ // As part of improving pref metrics on other platforms we may want to find
+ // ways to defer preference loading until the device ID can be used.
+ rlz_lib::GetMachineId(&device_id);
+#endif
+
+ return scoped_ptr<PrefHashStore>(new PrefHashStoreImpl(
+ profile->GetPath().AsUTF8Unsafe(),
+ seed,
+ device_id,
+ g_browser_process->local_state()));
+#endif
+}
+
} // namespace
// static
@@ -460,6 +492,7 @@ ProfileImpl::ProfileImpl(
sequenced_task_runner,
profile_policy_connector_->policy_service(),
managed_user_settings,
+ GetPrefHashStore(this),
new ExtensionPrefStore(
ExtensionPrefValueMapFactory::GetForBrowserContext(this), false),
pref_registry_,
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 0f0ac46..ae609f1 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1552,6 +1552,13 @@
'browser/prefs/command_line_pref_store.h',
'browser/prefs/incognito_mode_prefs.cc',
'browser/prefs/incognito_mode_prefs.h',
+ 'browser/prefs/pref_hash_calculator.cc',
+ 'browser/prefs/pref_hash_calculator.h',
+ 'browser/prefs/pref_hash_filter.cc',
+ 'browser/prefs/pref_hash_filter.h',
+ 'browser/prefs/pref_hash_store.h',
+ 'browser/prefs/pref_hash_store_impl.cc',
+ 'browser/prefs/pref_hash_store_impl.h',
'browser/prefs/pref_model_associator.cc',
'browser/prefs/pref_model_associator.h',
'browser/prefs/pref_metrics_service.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 7f3393b..a182db3 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -1121,7 +1121,9 @@
'browser/prefs/chrome_pref_service_unittest.cc',
'browser/prefs/command_line_pref_store_unittest.cc',
'browser/prefs/incognito_mode_prefs_unittest.cc',
- 'browser/prefs/pref_metrics_service_unittest.cc',
+ 'browser/prefs/pref_hash_calculator_unittest.cc',
+ 'browser/prefs/pref_hash_filter_unittest.cc',
+ 'browser/prefs/pref_hash_store_impl_unittest.cc',
'browser/prefs/pref_model_associator_unittest.cc',
'browser/prefs/proxy_config_dictionary_unittest.cc',
'browser/prefs/proxy_policy_unittest.cc',
diff --git a/chrome/service/service_process_prefs.cc b/chrome/service/service_process_prefs.cc
index c628a0b..d923185 100644
--- a/chrome/service/service_process_prefs.cc
+++ b/chrome/service/service_process_prefs.cc
@@ -5,12 +5,15 @@
#include "chrome/service/service_process_prefs.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/prefs/pref_filter.h"
#include "base/values.h"
ServiceProcessPrefs::ServiceProcessPrefs(
const base::FilePath& pref_filename,
base::SequencedTaskRunner* task_runner)
- : prefs_(new JsonPrefStore(pref_filename, task_runner)) {
+ : prefs_(new JsonPrefStore(pref_filename,
+ task_runner,
+ scoped_ptr<PrefFilter>())) {
}
ServiceProcessPrefs::~ServiceProcessPrefs() {}