summaryrefslogtreecommitdiff
path: root/chromium/components/prefs
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2016-05-09 14:22:11 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2016-05-09 15:11:45 +0000
commit2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c (patch)
treee75f511546c5fd1a173e87c1f9fb11d7ac8d1af3 /chromium/components/prefs
parenta4f3d46271c57e8155ba912df46a05559d14726e (diff)
downloadqtwebengine-chromium-2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c.tar.gz
BASELINE: Update Chromium to 51.0.2704.41
Also adds in all smaller components by reversing logic for exclusion. Change-Id: Ibf90b506e7da088ea2f65dcf23f2b0992c504422 Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Diffstat (limited to 'chromium/components/prefs')
-rw-r--r--chromium/components/prefs/BUILD.gn106
-rw-r--r--chromium/components/prefs/OWNERS4
-rw-r--r--chromium/components/prefs/base_prefs_export.h29
-rw-r--r--chromium/components/prefs/default_pref_store.cc56
-rw-r--r--chromium/components/prefs/default_pref_store.h53
-rw-r--r--chromium/components/prefs/default_pref_store_unittest.cc70
-rw-r--r--chromium/components/prefs/json_pref_store.cc541
-rw-r--r--chromium/components/prefs/json_pref_store.h231
-rw-r--r--chromium/components/prefs/json_pref_store_unittest.cc962
-rw-r--r--chromium/components/prefs/mock_pref_change_callback.cc24
-rw-r--r--chromium/components/prefs/mock_pref_change_callback.h51
-rw-r--r--chromium/components/prefs/overlay_user_pref_store.cc190
-rw-r--r--chromium/components/prefs/overlay_user_pref_store.h91
-rw-r--r--chromium/components/prefs/overlay_user_pref_store_unittest.cc311
-rw-r--r--chromium/components/prefs/persistent_pref_store.h80
-rw-r--r--chromium/components/prefs/pref_change_registrar.cc95
-rw-r--r--chromium/components/prefs/pref_change_registrar.h81
-rw-r--r--chromium/components/prefs/pref_change_registrar_unittest.cc198
-rw-r--r--chromium/components/prefs/pref_filter.h55
-rw-r--r--chromium/components/prefs/pref_member.cc223
-rw-r--r--chromium/components/prefs/pref_member.h357
-rw-r--r--chromium/components/prefs/pref_member_unittest.cc325
-rw-r--r--chromium/components/prefs/pref_notifier.h26
-rw-r--r--chromium/components/prefs/pref_notifier_impl.cc117
-rw-r--r--chromium/components/prefs/pref_notifier_impl.h74
-rw-r--r--chromium/components/prefs/pref_notifier_impl_unittest.cc222
-rw-r--r--chromium/components/prefs/pref_observer.h21
-rw-r--r--chromium/components/prefs/pref_registry.cc67
-rw-r--r--chromium/components/prefs/pref_registry.h90
-rw-r--r--chromium/components/prefs/pref_registry_simple.cc160
-rw-r--r--chromium/components/prefs/pref_registry_simple.h91
-rw-r--r--chromium/components/prefs/pref_service.cc620
-rw-r--r--chromium/components/prefs/pref_service.h388
-rw-r--r--chromium/components/prefs/pref_service_factory.cc61
-rw-r--r--chromium/components/prefs/pref_service_factory.h89
-rw-r--r--chromium/components/prefs/pref_service_unittest.cc430
-rw-r--r--chromium/components/prefs/pref_store.cc13
-rw-r--r--chromium/components/prefs/pref_store.h63
-rw-r--r--chromium/components/prefs/pref_store_observer_mock.cc29
-rw-r--r--chromium/components/prefs/pref_store_observer_mock.h35
-rw-r--r--chromium/components/prefs/pref_value_map.cc144
-rw-r--r--chromium/components/prefs/pref_value_map.h91
-rw-r--r--chromium/components/prefs/pref_value_map_unittest.cc135
-rw-r--r--chromium/components/prefs/pref_value_store.cc290
-rw-r--r--chromium/components/prefs/pref_value_store.h260
-rw-r--r--chromium/components/prefs/pref_value_store_unittest.cc670
-rw-r--r--chromium/components/prefs/prefs.gyp87
-rw-r--r--chromium/components/prefs/scoped_user_pref_update.cc37
-rw-r--r--chromium/components/prefs/scoped_user_pref_update.h109
-rw-r--r--chromium/components/prefs/scoped_user_pref_update_unittest.cc81
-rw-r--r--chromium/components/prefs/testing_pref_service.cc54
-rw-r--r--chromium/components/prefs/testing_pref_service.h196
-rw-r--r--chromium/components/prefs/testing_pref_store.cc185
-rw-r--r--chromium/components/prefs/testing_pref_store.h116
-rw-r--r--chromium/components/prefs/value_map_pref_store.cc64
-rw-r--r--chromium/components/prefs/value_map_pref_store.h57
-rw-r--r--chromium/components/prefs/writeable_pref_store.h72
57 files changed, 9377 insertions, 0 deletions
diff --git a/chromium/components/prefs/BUILD.gn b/chromium/components/prefs/BUILD.gn
new file mode 100644
index 00000000000..fdbb5cf5d5e
--- /dev/null
+++ b/chromium/components/prefs/BUILD.gn
@@ -0,0 +1,106 @@
+# Copyright 2015 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.
+
+component("prefs") {
+ sources = [
+ "default_pref_store.cc",
+ "default_pref_store.h",
+ "json_pref_store.cc",
+ "json_pref_store.h",
+ "overlay_user_pref_store.cc",
+ "overlay_user_pref_store.h",
+ "pref_change_registrar.cc",
+ "pref_change_registrar.h",
+ "pref_member.cc",
+ "pref_member.h",
+ "pref_notifier_impl.cc",
+ "pref_notifier_impl.h",
+ "pref_registry.cc",
+ "pref_registry.h",
+ "pref_registry_simple.cc",
+ "pref_registry_simple.h",
+ "pref_service.cc",
+ "pref_service.h",
+ "pref_service_factory.cc",
+ "pref_service_factory.h",
+ "pref_store.cc",
+ "pref_store.h",
+ "pref_value_map.cc",
+ "pref_value_map.h",
+ "pref_value_store.cc",
+ "pref_value_store.h",
+ "scoped_user_pref_update.cc",
+ "scoped_user_pref_update.h",
+ "value_map_pref_store.cc",
+ "value_map_pref_store.h",
+ ]
+ if (!is_ios) {
+ sources += [
+ "base_prefs_export.h",
+ "persistent_pref_store.h",
+ "pref_filter.h",
+ "pref_notifier.h",
+ "pref_observer.h",
+ "writeable_pref_store.h",
+ ]
+ }
+
+ defines = [ "COMPONENTS_PREFS_IMPLEMENTATION" ]
+
+ deps = [
+ "//base",
+ ]
+
+ if (!is_debug) {
+ configs -= [ "//build/config/compiler:default_optimization" ]
+ configs += [ "//build/config/compiler:optimize_max" ]
+ }
+}
+
+source_set("test_support") {
+ testonly = true
+ sources = [
+ "mock_pref_change_callback.cc",
+ "mock_pref_change_callback.h",
+ "pref_store_observer_mock.cc",
+ "pref_store_observer_mock.h",
+ "testing_pref_service.cc",
+ "testing_pref_service.h",
+ "testing_pref_store.cc",
+ "testing_pref_store.h",
+ ]
+
+ public_deps = [
+ ":prefs",
+ ]
+ deps = [
+ "//base",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "default_pref_store_unittest.cc",
+ "json_pref_store_unittest.cc",
+ "overlay_user_pref_store_unittest.cc",
+ "pref_change_registrar_unittest.cc",
+ "pref_member_unittest.cc",
+ "pref_notifier_impl_unittest.cc",
+ "pref_service_unittest.cc",
+ "pref_value_map_unittest.cc",
+ "pref_value_store_unittest.cc",
+ "scoped_user_pref_update_unittest.cc",
+ ]
+
+ deps = [
+ ":test_support",
+ "//base",
+ "//base/test:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/prefs/OWNERS b/chromium/components/prefs/OWNERS
new file mode 100644
index 00000000000..2d870381cb7
--- /dev/null
+++ b/chromium/components/prefs/OWNERS
@@ -0,0 +1,4 @@
+battre@chromium.org
+bauerb@chromium.org
+gab@chromium.org
+pam@chromium.org
diff --git a/chromium/components/prefs/base_prefs_export.h b/chromium/components/prefs/base_prefs_export.h
new file mode 100644
index 00000000000..e047ad99ab2
--- /dev/null
+++ b/chromium/components/prefs/base_prefs_export.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_
+#define COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(COMPONENTS_PREFS_IMPLEMENTATION)
+#define COMPONENTS_PREFS_EXPORT __declspec(dllexport)
+#else
+#define COMPONENTS_PREFS_EXPORT __declspec(dllimport)
+#endif // defined(COMPONENTS_PREFS_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(COMPONENTS_PREFS_IMPLEMENTATION)
+#define COMPONENTS_PREFS_EXPORT __attribute__((visibility("default")))
+#else
+#define COMPONENTS_PREFS_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define COMPONENTS_PREFS_EXPORT
+#endif
+
+#endif // COMPONENTS_PREFS_COMPONENTS_PREFS_EXPORT_H_
diff --git a/chromium/components/prefs/default_pref_store.cc b/chromium/components/prefs/default_pref_store.cc
new file mode 100644
index 00000000000..79867e95774
--- /dev/null
+++ b/chromium/components/prefs/default_pref_store.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 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 "components/prefs/default_pref_store.h"
+
+#include <utility>
+
+#include "base/logging.h"
+
+using base::Value;
+
+DefaultPrefStore::DefaultPrefStore() {}
+
+bool DefaultPrefStore::GetValue(const std::string& key,
+ const Value** result) const {
+ return prefs_.GetValue(key, result);
+}
+
+void DefaultPrefStore::AddObserver(PrefStore::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void DefaultPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool DefaultPrefStore::HasObservers() const {
+ return observers_.might_have_observers();
+}
+
+void DefaultPrefStore::SetDefaultValue(const std::string& key,
+ scoped_ptr<Value> value) {
+ DCHECK(!GetValue(key, NULL));
+ prefs_.SetValue(key, std::move(value));
+}
+
+void DefaultPrefStore::ReplaceDefaultValue(const std::string& key,
+ scoped_ptr<Value> value) {
+ const Value* old_value = NULL;
+ GetValue(key, &old_value);
+ bool notify = !old_value->Equals(value.get());
+ prefs_.SetValue(key, std::move(value));
+ if (notify)
+ FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
+}
+
+DefaultPrefStore::const_iterator DefaultPrefStore::begin() const {
+ return prefs_.begin();
+}
+
+DefaultPrefStore::const_iterator DefaultPrefStore::end() const {
+ return prefs_.end();
+}
+
+DefaultPrefStore::~DefaultPrefStore() {}
diff --git a/chromium/components/prefs/default_pref_store.h b/chromium/components/prefs/default_pref_store.h
new file mode 100644
index 00000000000..2e338899bea
--- /dev/null
+++ b/chromium/components/prefs/default_pref_store.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_
+#define COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/values.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/pref_store.h"
+#include "components/prefs/pref_value_map.h"
+
+// Used within a PrefRegistry to keep track of default preference values.
+class COMPONENTS_PREFS_EXPORT DefaultPrefStore : public PrefStore {
+ public:
+ typedef PrefValueMap::const_iterator const_iterator;
+
+ DefaultPrefStore();
+
+ // PrefStore implementation:
+ bool GetValue(const std::string& key,
+ const base::Value** result) const override;
+ void AddObserver(PrefStore::Observer* observer) override;
+ void RemoveObserver(PrefStore::Observer* observer) override;
+ bool HasObservers() const override;
+
+ // Sets a |value| for |key|. Should only be called if a value has not been
+ // set yet; otherwise call ReplaceDefaultValue().
+ void SetDefaultValue(const std::string& key, scoped_ptr<base::Value> value);
+
+ // Replaces the the value for |key| with a new value. Should only be called
+ // if a value has alreday been set; otherwise call SetDefaultValue().
+ void ReplaceDefaultValue(const std::string& key,
+ scoped_ptr<base::Value> value);
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ private:
+ ~DefaultPrefStore() override;
+
+ PrefValueMap prefs_;
+
+ base::ObserverList<PrefStore::Observer, true> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(DefaultPrefStore);
+};
+
+#endif // COMPONENTS_PREFS_DEFAULT_PREF_STORE_H_
diff --git a/chromium/components/prefs/default_pref_store_unittest.cc b/chromium/components/prefs/default_pref_store_unittest.cc
new file mode 100644
index 00000000000..06cabc01b14
--- /dev/null
+++ b/chromium/components/prefs/default_pref_store_unittest.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2012 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/macros.h"
+#include "components/prefs/default_pref_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringValue;
+using base::Value;
+
+namespace {
+
+class MockPrefStoreObserver : public PrefStore::Observer {
+ public:
+ explicit MockPrefStoreObserver(DefaultPrefStore* pref_store);
+ ~MockPrefStoreObserver() override;
+
+ int change_count() {
+ return change_count_;
+ }
+
+ // PrefStore::Observer implementation:
+ void OnPrefValueChanged(const std::string& key) override;
+ void OnInitializationCompleted(bool succeeded) override {}
+
+ private:
+ DefaultPrefStore* pref_store_;
+
+ int change_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockPrefStoreObserver);
+};
+
+MockPrefStoreObserver::MockPrefStoreObserver(DefaultPrefStore* pref_store)
+ : pref_store_(pref_store), change_count_(0) {
+ pref_store_->AddObserver(this);
+}
+
+MockPrefStoreObserver::~MockPrefStoreObserver() {
+ pref_store_->RemoveObserver(this);
+}
+
+void MockPrefStoreObserver::OnPrefValueChanged(const std::string& key) {
+ change_count_++;
+}
+
+} // namespace
+
+TEST(DefaultPrefStoreTest, NotifyPrefValueChanged) {
+ scoped_refptr<DefaultPrefStore> pref_store(new DefaultPrefStore);
+ MockPrefStoreObserver observer(pref_store.get());
+ std::string kPrefKey("pref_key");
+
+ // Setting a default value shouldn't send a change notification.
+ pref_store->SetDefaultValue(kPrefKey,
+ scoped_ptr<Value>(new StringValue("foo")));
+ EXPECT_EQ(0, observer.change_count());
+
+ // Replacing the default value should send a change notification...
+ pref_store->ReplaceDefaultValue(kPrefKey,
+ scoped_ptr<Value>(new StringValue("bar")));
+ EXPECT_EQ(1, observer.change_count());
+
+ // But only if the value actually changed.
+ pref_store->ReplaceDefaultValue(kPrefKey,
+ scoped_ptr<Value>(new StringValue("bar")));
+ EXPECT_EQ(1, observer.change_count());
+}
+
diff --git a/chromium/components/prefs/json_pref_store.cc b/chromium/components/prefs/json_pref_store.cc
new file mode 100644
index 00000000000..ba7cb913ebd
--- /dev/null
+++ b/chromium/components/prefs/json_pref_store.cc
@@ -0,0 +1,541 @@
+// Copyright (c) 2012 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 "components/prefs/json_pref_store.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/json/json_file_value_serializer.h"
+#include "base/json/json_string_value_serializer.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/metrics/histogram.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/task_runner_util.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/time/default_clock.h"
+#include "base/values.h"
+#include "components/prefs/pref_filter.h"
+
+// Result returned from internal read tasks.
+struct JsonPrefStore::ReadResult {
+ public:
+ ReadResult();
+ ~ReadResult();
+
+ scoped_ptr<base::Value> value;
+ PrefReadError error;
+ bool no_dir;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReadResult);
+};
+
+JsonPrefStore::ReadResult::ReadResult()
+ : error(PersistentPrefStore::PREF_READ_ERROR_NONE), no_dir(false) {
+}
+
+JsonPrefStore::ReadResult::~ReadResult() {
+}
+
+namespace {
+
+// Some extensions we'll tack on to copies of the Preferences files.
+const base::FilePath::CharType kBadExtension[] = FILE_PATH_LITERAL("bad");
+
+PersistentPrefStore::PrefReadError HandleReadErrors(
+ const base::Value* value,
+ const base::FilePath& path,
+ int error_code,
+ const std::string& error_msg) {
+ if (!value) {
+ DVLOG(1) << "Error while loading JSON file: " << error_msg
+ << ", file: " << path.value();
+ switch (error_code) {
+ case JSONFileValueDeserializer::JSON_ACCESS_DENIED:
+ return PersistentPrefStore::PREF_READ_ERROR_ACCESS_DENIED;
+ case JSONFileValueDeserializer::JSON_CANNOT_READ_FILE:
+ return PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
+ case JSONFileValueDeserializer::JSON_FILE_LOCKED:
+ return PersistentPrefStore::PREF_READ_ERROR_FILE_LOCKED;
+ case JSONFileValueDeserializer::JSON_NO_SUCH_FILE:
+ return PersistentPrefStore::PREF_READ_ERROR_NO_FILE;
+ default:
+ // JSON errors indicate file corruption of some sort.
+ // Since the file is corrupt, move it to the side and continue with
+ // empty preferences. This will result in them losing their settings.
+ // We keep the old file for possible support and debugging assistance
+ // as well as to detect if they're seeing these errors repeatedly.
+ // TODO(erikkay) Instead, use the last known good file.
+ base::FilePath bad = path.ReplaceExtension(kBadExtension);
+
+ // If they've ever had a parse error before, put them in another bucket.
+ // TODO(erikkay) if we keep this error checking for very long, we may
+ // want to differentiate between recent and long ago errors.
+ bool bad_existed = base::PathExists(bad);
+ base::Move(path, bad);
+ return bad_existed ? PersistentPrefStore::PREF_READ_ERROR_JSON_REPEAT
+ : PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE;
+ }
+ }
+ if (!value->IsType(base::Value::TYPE_DICTIONARY))
+ return PersistentPrefStore::PREF_READ_ERROR_JSON_TYPE;
+ return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+// Records a sample for |size| in the Settings.JsonDataReadSizeKilobytes
+// histogram suffixed with the base name of the JSON file under |path|.
+void RecordJsonDataSizeHistogram(const base::FilePath& path, size_t size) {
+ std::string spaceless_basename;
+ base::ReplaceChars(path.BaseName().MaybeAsASCII(), " ", "_",
+ &spaceless_basename);
+
+ // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
+ // macro adapted to allow for a dynamically suffixed histogram name.
+ // Note: The factory creates and owns the histogram.
+ base::HistogramBase* histogram = base::Histogram::FactoryGet(
+ "Settings.JsonDataReadSizeKilobytes." + spaceless_basename, 1, 10000, 50,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ histogram->Add(static_cast<int>(size) / 1024);
+}
+
+scoped_ptr<JsonPrefStore::ReadResult> ReadPrefsFromDisk(
+ const base::FilePath& path,
+ const base::FilePath& alternate_path) {
+ if (!base::PathExists(path) && !alternate_path.empty() &&
+ base::PathExists(alternate_path)) {
+ base::Move(alternate_path, path);
+ }
+
+ int error_code;
+ std::string error_msg;
+ scoped_ptr<JsonPrefStore::ReadResult> read_result(
+ new JsonPrefStore::ReadResult);
+ JSONFileValueDeserializer deserializer(path);
+ read_result->value = deserializer.Deserialize(&error_code, &error_msg);
+ read_result->error =
+ HandleReadErrors(read_result->value.get(), path, error_code, error_msg);
+ read_result->no_dir = !base::PathExists(path.DirName());
+
+ if (read_result->error == PersistentPrefStore::PREF_READ_ERROR_NONE)
+ RecordJsonDataSizeHistogram(path, deserializer.get_last_read_size());
+
+ return read_result;
+}
+
+} // namespace
+
+// static
+scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile(
+ const base::FilePath& filename,
+ base::SequencedWorkerPool* worker_pool) {
+ std::string token("json_pref_store-");
+ token.append(filename.AsUTF8Unsafe());
+ return worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
+ worker_pool->GetNamedSequenceToken(token),
+ base::SequencedWorkerPool::BLOCK_SHUTDOWN);
+}
+
+JsonPrefStore::JsonPrefStore(
+ const base::FilePath& pref_filename,
+ const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter)
+ : JsonPrefStore(pref_filename,
+ base::FilePath(),
+ sequenced_task_runner,
+ std::move(pref_filter)) {}
+
+JsonPrefStore::JsonPrefStore(
+ const base::FilePath& pref_filename,
+ const base::FilePath& pref_alternate_filename,
+ const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter)
+ : path_(pref_filename),
+ alternate_path_(pref_alternate_filename),
+ sequenced_task_runner_(sequenced_task_runner),
+ prefs_(new base::DictionaryValue()),
+ read_only_(false),
+ writer_(pref_filename, sequenced_task_runner),
+ pref_filter_(std::move(pref_filter)),
+ initialized_(false),
+ filtering_in_progress_(false),
+ pending_lossy_write_(false),
+ read_error_(PREF_READ_ERROR_NONE),
+ write_count_histogram_(writer_.commit_interval(), path_) {
+ DCHECK(!path_.empty());
+}
+
+bool JsonPrefStore::GetValue(const std::string& key,
+ const base::Value** result) const {
+ DCHECK(CalledOnValidThread());
+
+ base::Value* tmp = nullptr;
+ if (!prefs_->Get(key, &tmp))
+ return false;
+
+ if (result)
+ *result = tmp;
+ return true;
+}
+
+void JsonPrefStore::AddObserver(PrefStore::Observer* observer) {
+ DCHECK(CalledOnValidThread());
+
+ observers_.AddObserver(observer);
+}
+
+void JsonPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+ DCHECK(CalledOnValidThread());
+
+ observers_.RemoveObserver(observer);
+}
+
+bool JsonPrefStore::HasObservers() const {
+ DCHECK(CalledOnValidThread());
+
+ return observers_.might_have_observers();
+}
+
+bool JsonPrefStore::IsInitializationComplete() const {
+ DCHECK(CalledOnValidThread());
+
+ return initialized_;
+}
+
+bool JsonPrefStore::GetMutableValue(const std::string& key,
+ base::Value** result) {
+ DCHECK(CalledOnValidThread());
+
+ return prefs_->Get(key, result);
+}
+
+void JsonPrefStore::SetValue(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) {
+ DCHECK(CalledOnValidThread());
+
+ DCHECK(value);
+ base::Value* old_value = nullptr;
+ prefs_->Get(key, &old_value);
+ if (!old_value || !value->Equals(old_value)) {
+ prefs_->Set(key, std::move(value));
+ ReportValueChanged(key, flags);
+ }
+}
+
+void JsonPrefStore::SetValueSilently(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) {
+ DCHECK(CalledOnValidThread());
+
+ DCHECK(value);
+ base::Value* old_value = nullptr;
+ prefs_->Get(key, &old_value);
+ if (!old_value || !value->Equals(old_value)) {
+ prefs_->Set(key, std::move(value));
+ ScheduleWrite(flags);
+ }
+}
+
+void JsonPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
+ DCHECK(CalledOnValidThread());
+
+ if (prefs_->RemovePath(key, nullptr))
+ ReportValueChanged(key, flags);
+}
+
+void JsonPrefStore::RemoveValueSilently(const std::string& key,
+ uint32_t flags) {
+ DCHECK(CalledOnValidThread());
+
+ prefs_->RemovePath(key, nullptr);
+ ScheduleWrite(flags);
+}
+
+bool JsonPrefStore::ReadOnly() const {
+ DCHECK(CalledOnValidThread());
+
+ return read_only_;
+}
+
+PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const {
+ DCHECK(CalledOnValidThread());
+
+ return read_error_;
+}
+
+PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
+ DCHECK(CalledOnValidThread());
+
+ OnFileRead(ReadPrefsFromDisk(path_, alternate_path_));
+ return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE
+ : read_error_;
+}
+
+void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
+ DCHECK(CalledOnValidThread());
+
+ initialized_ = false;
+ error_delegate_.reset(error_delegate);
+
+ // Weakly binds the read task so that it doesn't kick in during shutdown.
+ base::PostTaskAndReplyWithResult(
+ sequenced_task_runner_.get(),
+ FROM_HERE,
+ base::Bind(&ReadPrefsFromDisk, path_, alternate_path_),
+ base::Bind(&JsonPrefStore::OnFileRead, AsWeakPtr()));
+}
+
+void JsonPrefStore::CommitPendingWrite() {
+ DCHECK(CalledOnValidThread());
+
+ // Schedule a write for any lossy writes that are outstanding to ensure that
+ // they get flushed when this function is called.
+ SchedulePendingLossyWrites();
+
+ if (writer_.HasPendingWrite() && !read_only_)
+ writer_.DoScheduledWrite();
+}
+
+void JsonPrefStore::SchedulePendingLossyWrites() {
+ if (pending_lossy_write_)
+ writer_.ScheduleWrite(this);
+}
+
+void JsonPrefStore::ReportValueChanged(const std::string& key, uint32_t flags) {
+ DCHECK(CalledOnValidThread());
+
+ if (pref_filter_)
+ pref_filter_->FilterUpdate(key);
+
+ FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
+
+ ScheduleWrite(flags);
+}
+
+void JsonPrefStore::RegisterOnNextSuccessfulWriteCallback(
+ const base::Closure& on_next_successful_write) {
+ DCHECK(CalledOnValidThread());
+
+ writer_.RegisterOnNextSuccessfulWriteCallback(on_next_successful_write);
+}
+
+void JsonPrefStore::ClearMutableValues() {
+ NOTIMPLEMENTED();
+}
+
+void JsonPrefStore::OnFileRead(scoped_ptr<ReadResult> read_result) {
+ DCHECK(CalledOnValidThread());
+
+ DCHECK(read_result);
+
+ scoped_ptr<base::DictionaryValue> unfiltered_prefs(new base::DictionaryValue);
+
+ read_error_ = read_result->error;
+
+ bool initialization_successful = !read_result->no_dir;
+
+ if (initialization_successful) {
+ switch (read_error_) {
+ case PREF_READ_ERROR_ACCESS_DENIED:
+ case PREF_READ_ERROR_FILE_OTHER:
+ case PREF_READ_ERROR_FILE_LOCKED:
+ case PREF_READ_ERROR_JSON_TYPE:
+ case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
+ read_only_ = true;
+ break;
+ case PREF_READ_ERROR_NONE:
+ DCHECK(read_result->value.get());
+ unfiltered_prefs.reset(
+ static_cast<base::DictionaryValue*>(read_result->value.release()));
+ break;
+ case PREF_READ_ERROR_NO_FILE:
+ // If the file just doesn't exist, maybe this is first run. In any case
+ // there's no harm in writing out default prefs in this case.
+ case PREF_READ_ERROR_JSON_PARSE:
+ case PREF_READ_ERROR_JSON_REPEAT:
+ break;
+ case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
+ // This is a special error code to be returned by ReadPrefs when it
+ // can't complete synchronously, it should never be returned by the read
+ // operation itself.
+ case PREF_READ_ERROR_MAX_ENUM:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ if (pref_filter_) {
+ filtering_in_progress_ = true;
+ const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback(
+ base::Bind(
+ &JsonPrefStore::FinalizeFileRead, AsWeakPtr(),
+ initialization_successful));
+ pref_filter_->FilterOnLoad(post_filter_on_load_callback,
+ std::move(unfiltered_prefs));
+ } else {
+ FinalizeFileRead(initialization_successful, std::move(unfiltered_prefs),
+ false);
+ }
+}
+
+JsonPrefStore::~JsonPrefStore() {
+ CommitPendingWrite();
+}
+
+bool JsonPrefStore::SerializeData(std::string* output) {
+ DCHECK(CalledOnValidThread());
+
+ pending_lossy_write_ = false;
+
+ write_count_histogram_.RecordWriteOccured();
+
+ if (pref_filter_)
+ pref_filter_->FilterSerializeData(prefs_.get());
+
+ JSONStringValueSerializer serializer(output);
+ // Not pretty-printing prefs shrinks pref file size by ~30%. To obtain
+ // readable prefs for debugging purposes, you can dump your prefs into any
+ // command-line or online JSON pretty printing tool.
+ serializer.set_pretty_print(false);
+ return serializer.Serialize(*prefs_);
+}
+
+void JsonPrefStore::FinalizeFileRead(bool initialization_successful,
+ scoped_ptr<base::DictionaryValue> prefs,
+ bool schedule_write) {
+ DCHECK(CalledOnValidThread());
+
+ filtering_in_progress_ = false;
+
+ if (!initialization_successful) {
+ FOR_EACH_OBSERVER(PrefStore::Observer,
+ observers_,
+ OnInitializationCompleted(false));
+ return;
+ }
+
+ prefs_ = std::move(prefs);
+
+ initialized_ = true;
+
+ if (schedule_write)
+ ScheduleWrite(DEFAULT_PREF_WRITE_FLAGS);
+
+ if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE)
+ error_delegate_->OnError(read_error_);
+
+ FOR_EACH_OBSERVER(PrefStore::Observer,
+ observers_,
+ OnInitializationCompleted(true));
+
+ return;
+}
+
+void JsonPrefStore::ScheduleWrite(uint32_t flags) {
+ if (read_only_)
+ return;
+
+ if (flags & LOSSY_PREF_WRITE_FLAG)
+ pending_lossy_write_ = true;
+ else
+ writer_.ScheduleWrite(this);
+}
+
+// NOTE: This value should NOT be changed without renaming the histogram
+// otherwise it will create incompatible buckets.
+const int32_t
+ JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins = 5;
+
+JsonPrefStore::WriteCountHistogram::WriteCountHistogram(
+ const base::TimeDelta& commit_interval,
+ const base::FilePath& path)
+ : WriteCountHistogram(commit_interval,
+ path,
+ scoped_ptr<base::Clock>(new base::DefaultClock)) {
+}
+
+JsonPrefStore::WriteCountHistogram::WriteCountHistogram(
+ const base::TimeDelta& commit_interval,
+ const base::FilePath& path,
+ scoped_ptr<base::Clock> clock)
+ : commit_interval_(commit_interval),
+ path_(path),
+ clock_(clock.release()),
+ report_interval_(
+ base::TimeDelta::FromMinutes(kHistogramWriteReportIntervalMins)),
+ last_report_time_(clock_->Now()),
+ writes_since_last_report_(0) {
+}
+
+JsonPrefStore::WriteCountHistogram::~WriteCountHistogram() {
+ ReportOutstandingWrites();
+}
+
+void JsonPrefStore::WriteCountHistogram::RecordWriteOccured() {
+ ReportOutstandingWrites();
+
+ ++writes_since_last_report_;
+}
+
+void JsonPrefStore::WriteCountHistogram::ReportOutstandingWrites() {
+ base::Time current_time = clock_->Now();
+ base::TimeDelta time_since_last_report = current_time - last_report_time_;
+
+ if (time_since_last_report <= report_interval_)
+ return;
+
+ // If the time since the last report exceeds the report interval, report all
+ // the writes since the last report. They must have all occurred in the same
+ // report interval.
+ base::HistogramBase* histogram = GetHistogram();
+ histogram->Add(writes_since_last_report_);
+
+ // There may be several report intervals that elapsed that don't have any
+ // writes in them. Report these too.
+ int64_t total_num_intervals_elapsed =
+ (time_since_last_report / report_interval_);
+ for (int64_t i = 0; i < total_num_intervals_elapsed - 1; ++i)
+ histogram->Add(0);
+
+ writes_since_last_report_ = 0;
+ last_report_time_ += total_num_intervals_elapsed * report_interval_;
+}
+
+base::HistogramBase* JsonPrefStore::WriteCountHistogram::GetHistogram() {
+ std::string spaceless_basename;
+ base::ReplaceChars(path_.BaseName().MaybeAsASCII(), " ", "_",
+ &spaceless_basename);
+ std::string histogram_name =
+ "Settings.JsonDataWriteCount." + spaceless_basename;
+
+ // The min value for a histogram is 1. The max value is the maximum number of
+ // writes that can occur in the window being recorded. The number of buckets
+ // used is the max value (plus the underflow/overflow buckets).
+ int32_t min_value = 1;
+ int32_t max_value = report_interval_ / commit_interval_;
+ int32_t num_buckets = max_value + 1;
+
+ // NOTE: These values should NOT be changed without renaming the histogram
+ // otherwise it will create incompatible buckets.
+ DCHECK_EQ(30, max_value);
+ DCHECK_EQ(31, num_buckets);
+
+ // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_COUNTS
+ // macro adapted to allow for a dynamically suffixed histogram name.
+ // Note: The factory creates and owns the histogram.
+ base::HistogramBase* histogram = base::Histogram::FactoryGet(
+ histogram_name, min_value, max_value, num_buckets,
+ base::HistogramBase::kUmaTargetedHistogramFlag);
+ return histogram;
+}
diff --git a/chromium/components/prefs/json_pref_store.h b/chromium/components/prefs/json_pref_store.h
new file mode 100644
index 00000000000..ff6e1f22000
--- /dev/null
+++ b/chromium/components/prefs/json_pref_store.h
@@ -0,0 +1,231 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_JSON_PREF_STORE_H_
+#define COMPONENTS_PREFS_JSON_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <set>
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/files/file_path.h"
+#include "base/files/important_file_writer.h"
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/threading/non_thread_safe.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/persistent_pref_store.h"
+
+class PrefFilter;
+
+namespace base {
+class Clock;
+class DictionaryValue;
+class FilePath;
+class HistogramBase;
+class JsonPrefStoreLossyWriteTest;
+class SequencedTaskRunner;
+class SequencedWorkerPool;
+class Value;
+FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestBasic);
+FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod);
+FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods);
+FORWARD_DECLARE_TEST(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps);
+}
+
+// A writable PrefStore implementation that is used for user preferences.
+class COMPONENTS_PREFS_EXPORT JsonPrefStore
+ : public PersistentPrefStore,
+ public base::ImportantFileWriter::DataSerializer,
+ public base::SupportsWeakPtr<JsonPrefStore>,
+ public base::NonThreadSafe {
+ public:
+ struct ReadResult;
+
+ // Returns instance of SequencedTaskRunner which guarantees that file
+ // operations on the same file will be executed in sequenced order.
+ static scoped_refptr<base::SequencedTaskRunner> GetTaskRunnerForFile(
+ const base::FilePath& pref_filename,
+ base::SequencedWorkerPool* worker_pool);
+
+ // Same as the constructor below with no alternate filename.
+ JsonPrefStore(
+ const base::FilePath& pref_filename,
+ const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter);
+
+ // |sequenced_task_runner| must be a shutdown-blocking task runner, ideally
+ // created by the GetTaskRunnerForFile() method above.
+ // |pref_filename| is the path to the file to read prefs from.
+ // |pref_alternate_filename| is the path to an alternate file which the
+ // desired prefs may have previously been written to. If |pref_filename|
+ // doesn't exist and |pref_alternate_filename| does, |pref_alternate_filename|
+ // will be moved to |pref_filename| before the read occurs.
+ JsonPrefStore(
+ const base::FilePath& pref_filename,
+ const base::FilePath& pref_alternate_filename,
+ const scoped_refptr<base::SequencedTaskRunner>& sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter);
+
+ // PrefStore overrides:
+ bool GetValue(const std::string& key,
+ const base::Value** result) const override;
+ void AddObserver(PrefStore::Observer* observer) override;
+ void RemoveObserver(PrefStore::Observer* observer) override;
+ bool HasObservers() const override;
+ bool IsInitializationComplete() const override;
+
+ // PersistentPrefStore overrides:
+ bool GetMutableValue(const std::string& key, base::Value** result) override;
+ void SetValue(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) override;
+ void SetValueSilently(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) override;
+ void RemoveValue(const std::string& key, uint32_t flags) override;
+ bool ReadOnly() const override;
+ PrefReadError GetReadError() const override;
+ // Note this method may be asynchronous if this instance has a |pref_filter_|
+ // in which case it will return PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE.
+ // See details in pref_filter.h.
+ PrefReadError ReadPrefs() override;
+ void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
+ void CommitPendingWrite() override;
+ void SchedulePendingLossyWrites() override;
+ void ReportValueChanged(const std::string& key, uint32_t flags) override;
+
+ // Just like RemoveValue(), but doesn't notify observers. Used when doing some
+ // cleanup that shouldn't otherwise alert observers.
+ void RemoveValueSilently(const std::string& key, uint32_t flags);
+
+ // Registers |on_next_successful_write| to be called once, on the next
+ // successful write event of |writer_|.
+ void RegisterOnNextSuccessfulWriteCallback(
+ const base::Closure& on_next_successful_write);
+
+ void ClearMutableValues() override;
+
+ private:
+ // Represents a histogram for recording the number of writes to the pref file
+ // that occur every kHistogramWriteReportIntervalInMins minutes.
+ class COMPONENTS_PREFS_EXPORT WriteCountHistogram {
+ public:
+ static const int32_t kHistogramWriteReportIntervalMins;
+
+ WriteCountHistogram(const base::TimeDelta& commit_interval,
+ const base::FilePath& path);
+ // Constructor for testing. |clock| is a test Clock that is used to retrieve
+ // the time.
+ WriteCountHistogram(const base::TimeDelta& commit_interval,
+ const base::FilePath& path,
+ scoped_ptr<base::Clock> clock);
+ ~WriteCountHistogram();
+
+ // Record that a write has occured.
+ void RecordWriteOccured();
+
+ // Reports writes (that have not yet been reported) in all of the recorded
+ // intervals that have elapsed up until current time.
+ void ReportOutstandingWrites();
+
+ base::HistogramBase* GetHistogram();
+
+ private:
+ // The minimum interval at which writes can occur.
+ const base::TimeDelta commit_interval_;
+
+ // The path to the file.
+ const base::FilePath path_;
+
+ // Clock which is used to retrieve the current time.
+ scoped_ptr<base::Clock> clock_;
+
+ // The interval at which to report write counts.
+ const base::TimeDelta report_interval_;
+
+ // The time at which the last histogram value was reported for the number
+ // of write counts.
+ base::Time last_report_time_;
+
+ // The number of writes that have occured since the last write count was
+ // reported.
+ uint32_t writes_since_last_report_;
+
+ DISALLOW_COPY_AND_ASSIGN(WriteCountHistogram);
+ };
+
+ FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest,
+ WriteCountHistogramTestBasic);
+ FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest,
+ WriteCountHistogramTestSinglePeriod);
+ FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest,
+ WriteCountHistogramTestMultiplePeriods);
+ FRIEND_TEST_ALL_PREFIXES(base::JsonPrefStoreTest,
+ WriteCountHistogramTestPeriodWithGaps);
+ friend class base::JsonPrefStoreLossyWriteTest;
+
+ ~JsonPrefStore() override;
+
+ // This method is called after the JSON file has been read. It then hands
+ // |value| (or an empty dictionary in some read error cases) to the
+ // |pref_filter| if one is set. It also gives a callback pointing at
+ // FinalizeFileRead() to that |pref_filter_| which is then responsible for
+ // invoking it when done. If there is no |pref_filter_|, FinalizeFileRead()
+ // is invoked directly.
+ void OnFileRead(scoped_ptr<ReadResult> read_result);
+
+ // ImportantFileWriter::DataSerializer overrides:
+ bool SerializeData(std::string* output) override;
+
+ // This method is called after the JSON file has been read and the result has
+ // potentially been intercepted and modified by |pref_filter_|.
+ // |initialization_successful| is pre-determined by OnFileRead() and should
+ // be used when reporting OnInitializationCompleted().
+ // |schedule_write| indicates whether a write should be immediately scheduled
+ // (typically because the |pref_filter_| has already altered the |prefs|) --
+ // this will be ignored if this store is read-only.
+ void FinalizeFileRead(bool initialization_successful,
+ scoped_ptr<base::DictionaryValue> prefs,
+ bool schedule_write);
+
+ // Schedule a write with the file writer as long as |flags| doesn't contain
+ // WriteablePrefStore::LOSSY_PREF_WRITE_FLAG.
+ void ScheduleWrite(uint32_t flags);
+
+ const base::FilePath path_;
+ const base::FilePath alternate_path_;
+ const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
+
+ scoped_ptr<base::DictionaryValue> prefs_;
+
+ bool read_only_;
+
+ // Helper for safely writing pref data.
+ base::ImportantFileWriter writer_;
+
+ scoped_ptr<PrefFilter> pref_filter_;
+ base::ObserverList<PrefStore::Observer, true> observers_;
+
+ scoped_ptr<ReadErrorDelegate> error_delegate_;
+
+ bool initialized_;
+ bool filtering_in_progress_;
+ bool pending_lossy_write_;
+ PrefReadError read_error_;
+
+ std::set<std::string> keys_need_empty_value_;
+
+ WriteCountHistogram write_count_histogram_;
+
+ DISALLOW_COPY_AND_ASSIGN(JsonPrefStore);
+};
+
+#endif // COMPONENTS_PREFS_JSON_PREF_STORE_H_
diff --git a/chromium/components/prefs/json_pref_store_unittest.cc b/chromium/components/prefs/json_pref_store_unittest.cc
new file mode 100644
index 00000000000..621b895a0e2
--- /dev/null
+++ b/chromium/components/prefs/json_pref_store_unittest.cc
@@ -0,0 +1,962 @@
+// Copyright (c) 2012 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 "components/prefs/json_pref_store.h"
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram_samples.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/single_thread_task_runner.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/simple_test_clock.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "base/threading/thread.h"
+#include "base/values.h"
+#include "components/prefs/pref_filter.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+const char kHomePage[] = "homepage";
+
+const char kReadJson[] =
+ "{\n"
+ " \"homepage\": \"http://www.cnn.com\",\n"
+ " \"some_directory\": \"/usr/local/\",\n"
+ " \"tabs\": {\n"
+ " \"new_windows_in_tabs\": true,\n"
+ " \"max_tabs\": 20\n"
+ " }\n"
+ "}";
+
+const char kInvalidJson[] = "!@#$%^&";
+
+// Expected output for tests using RunBasicJsonPrefStoreTest().
+const char kWriteGolden[] =
+ "{\"homepage\":\"http://www.cnn.com\","
+ "\"long_int\":{\"pref\":\"214748364842\"},"
+ "\"some_directory\":\"/usr/sbin/\","
+ "\"tabs\":{\"max_tabs\":10,\"new_windows_in_tabs\":false}}";
+
+// Set the time on the given SimpleTestClock to the given time in minutes.
+void SetCurrentTimeInMinutes(double minutes, base::SimpleTestClock* clock) {
+ const int32_t kBaseTimeMins = 100;
+ clock->SetNow(base::Time::FromDoubleT((kBaseTimeMins + minutes) * 60));
+}
+
+// A PrefFilter that will intercept all calls to FilterOnLoad() and hold on
+// to the |prefs| until explicitly asked to release them.
+class InterceptingPrefFilter : public PrefFilter {
+ public:
+ InterceptingPrefFilter();
+ ~InterceptingPrefFilter() override;
+
+ // PrefFilter implementation:
+ void FilterOnLoad(
+ const PostFilterOnLoadCallback& post_filter_on_load_callback,
+ scoped_ptr<base::DictionaryValue> pref_store_contents) override;
+ void FilterUpdate(const std::string& path) override {}
+ void FilterSerializeData(
+ base::DictionaryValue* pref_store_contents) override {}
+
+ bool has_intercepted_prefs() const { return intercepted_prefs_ != NULL; }
+
+ // Finalize an intercepted read, handing |intercepted_prefs_| back to its
+ // JsonPrefStore.
+ void ReleasePrefs();
+
+ private:
+ PostFilterOnLoadCallback post_filter_on_load_callback_;
+ scoped_ptr<base::DictionaryValue> intercepted_prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterceptingPrefFilter);
+};
+
+InterceptingPrefFilter::InterceptingPrefFilter() {}
+InterceptingPrefFilter::~InterceptingPrefFilter() {}
+
+void InterceptingPrefFilter::FilterOnLoad(
+ const PostFilterOnLoadCallback& post_filter_on_load_callback,
+ scoped_ptr<base::DictionaryValue> pref_store_contents) {
+ post_filter_on_load_callback_ = post_filter_on_load_callback;
+ intercepted_prefs_ = std::move(pref_store_contents);
+}
+
+void InterceptingPrefFilter::ReleasePrefs() {
+ EXPECT_FALSE(post_filter_on_load_callback_.is_null());
+ post_filter_on_load_callback_.Run(std::move(intercepted_prefs_), false);
+ post_filter_on_load_callback_.Reset();
+}
+
+class MockPrefStoreObserver : public PrefStore::Observer {
+ public:
+ MOCK_METHOD1(OnPrefValueChanged, void (const std::string&));
+ MOCK_METHOD1(OnInitializationCompleted, void (bool));
+};
+
+class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate {
+ public:
+ MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError));
+};
+
+} // namespace
+
+class JsonPrefStoreTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+ }
+
+ void TearDown() override {
+ // Make sure all pending tasks have been processed (e.g., deleting the
+ // JsonPrefStore may post write tasks).
+ RunLoop().RunUntilIdle();
+ }
+
+ // The path to temporary directory used to contain the test operations.
+ base::ScopedTempDir temp_dir_;
+ // A message loop that we can use as the file thread message loop.
+ MessageLoop message_loop_;
+};
+
+// Test fallback behavior for a nonexistent file.
+TEST_F(JsonPrefStoreTest, NonExistentFile) {
+ base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt");
+ ASSERT_FALSE(PathExists(bogus_input_file));
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
+ pref_store->ReadPrefs());
+ EXPECT_FALSE(pref_store->ReadOnly());
+}
+
+// Test fallback behavior for a nonexistent file and alternate file.
+TEST_F(JsonPrefStoreTest, NonExistentFileAndAlternateFile) {
+ base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt");
+ base::FilePath bogus_alternate_input_file =
+ temp_dir_.path().AppendASCII("read_alternate.txt");
+ ASSERT_FALSE(PathExists(bogus_input_file));
+ ASSERT_FALSE(PathExists(bogus_alternate_input_file));
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(bogus_input_file, bogus_alternate_input_file,
+ message_loop_.task_runner(), scoped_ptr<PrefFilter>());
+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
+ pref_store->ReadPrefs());
+ EXPECT_FALSE(pref_store->ReadOnly());
+}
+
+// Test fallback behavior for an invalid file.
+TEST_F(JsonPrefStoreTest, InvalidFile) {
+ base::FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json");
+ ASSERT_LT(0, base::WriteFile(invalid_file,
+ kInvalidJson, arraysize(kInvalidJson) - 1));
+
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ invalid_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
+ pref_store->ReadPrefs());
+ EXPECT_FALSE(pref_store->ReadOnly());
+
+ // The file should have been moved aside.
+ EXPECT_FALSE(PathExists(invalid_file));
+ base::FilePath moved_aside = temp_dir_.path().AppendASCII("invalid.bad");
+ EXPECT_TRUE(PathExists(moved_aside));
+
+ std::string moved_aside_contents;
+ ASSERT_TRUE(base::ReadFileToString(moved_aside, &moved_aside_contents));
+ EXPECT_EQ(kInvalidJson, moved_aside_contents);
+}
+
+// This function is used to avoid code duplication while testing synchronous
+// and asynchronous version of the JsonPrefStore loading. It validates that the
+// given output file's contents matches kWriteGolden.
+void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
+ const base::FilePath& output_file) {
+ const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs";
+ const char kMaxTabs[] = "tabs.max_tabs";
+ const char kLongIntPref[] = "long_int.pref";
+
+ std::string cnn("http://www.cnn.com");
+
+ const Value* actual;
+ EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual));
+ std::string string_value;
+ EXPECT_TRUE(actual->GetAsString(&string_value));
+ EXPECT_EQ(cnn, string_value);
+
+ const char kSomeDirectory[] = "some_directory";
+
+ EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
+ base::FilePath::StringType path;
+ EXPECT_TRUE(actual->GetAsString(&path));
+ EXPECT_EQ(base::FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path);
+ base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/"));
+
+ pref_store->SetValue(kSomeDirectory,
+ make_scoped_ptr(new StringValue(some_path.value())),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual));
+ EXPECT_TRUE(actual->GetAsString(&path));
+ EXPECT_EQ(some_path.value(), path);
+
+ // Test reading some other data types from sub-dictionaries.
+ EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
+ bool boolean = false;
+ EXPECT_TRUE(actual->GetAsBoolean(&boolean));
+ EXPECT_TRUE(boolean);
+
+ pref_store->SetValue(kNewWindowsInTabs,
+ make_scoped_ptr(new FundamentalValue(false)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual));
+ EXPECT_TRUE(actual->GetAsBoolean(&boolean));
+ EXPECT_FALSE(boolean);
+
+ EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
+ int integer = 0;
+ EXPECT_TRUE(actual->GetAsInteger(&integer));
+ EXPECT_EQ(20, integer);
+ pref_store->SetValue(kMaxTabs, make_scoped_ptr(new FundamentalValue(10)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual));
+ EXPECT_TRUE(actual->GetAsInteger(&integer));
+ EXPECT_EQ(10, integer);
+
+ pref_store->SetValue(
+ kLongIntPref,
+ make_scoped_ptr(new StringValue(base::Int64ToString(214748364842LL))),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual));
+ EXPECT_TRUE(actual->GetAsString(&string_value));
+ int64_t value;
+ base::StringToInt64(string_value, &value);
+ EXPECT_EQ(214748364842LL, value);
+
+ // Serialize and compare to expected output.
+ pref_store->CommitPendingWrite();
+ RunLoop().RunUntilIdle();
+
+ std::string output_contents;
+ ASSERT_TRUE(base::ReadFileToString(output_file, &output_contents));
+ EXPECT_EQ(kWriteGolden, output_contents);
+ ASSERT_TRUE(base::DeleteFile(output_file, false));
+}
+
+TEST_F(JsonPrefStoreTest, Basic) {
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ ASSERT_LT(0, base::WriteFile(input_file,
+ kReadJson, arraysize(kReadJson) - 1));
+
+ // Test that the persistent value can be loaded.
+ ASSERT_TRUE(PathExists(input_file));
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+}
+
+TEST_F(JsonPrefStoreTest, BasicAsync) {
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ ASSERT_LT(0, base::WriteFile(input_file,
+ kReadJson, arraysize(kReadJson) - 1));
+
+ // Test that the persistent value can be loaded.
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
+
+ {
+ MockPrefStoreObserver mock_observer;
+ pref_store->AddObserver(&mock_observer);
+
+ MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
+ pref_store->ReadPrefsAsync(mock_error_delegate);
+
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
+ EXPECT_CALL(*mock_error_delegate,
+ OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
+ RunLoop().RunUntilIdle();
+ pref_store->RemoveObserver(&mock_observer);
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+ }
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+}
+
+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_.task_runner(), scoped_ptr<PrefFilter>());
+
+ // Set some keys with empty values.
+ pref_store->SetValue("list", make_scoped_ptr(new base::ListValue),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ pref_store->SetValue("dict", make_scoped_ptr(new base::DictionaryValue),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+ // Write to file.
+ pref_store->CommitPendingWrite();
+ RunLoop().RunUntilIdle();
+
+ // Reload.
+ pref_store = new JsonPrefStore(pref_file, message_loop_.task_runner(),
+ scoped_ptr<PrefFilter>());
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+ ASSERT_FALSE(pref_store->ReadOnly());
+
+ // Check values.
+ const Value* result = NULL;
+ EXPECT_TRUE(pref_store->GetValue("list", &result));
+ EXPECT_TRUE(ListValue().Equals(result));
+ EXPECT_TRUE(pref_store->GetValue("dict", &result));
+ EXPECT_TRUE(DictionaryValue().Equals(result));
+}
+
+// This test is just documenting some potentially non-obvious behavior. It
+// shouldn't be taken as normative.
+TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) {
+ FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json");
+
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ pref_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
+
+ scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
+ dict->SetString("key", "value");
+ pref_store->SetValue("dict", std::move(dict),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+ pref_store->RemoveValue("dict.key",
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+ const base::Value* retrieved_dict = NULL;
+ bool has_dict = pref_store->GetValue("dict", &retrieved_dict);
+ EXPECT_FALSE(has_dict);
+}
+
+// Tests asynchronous reading of the file when there is no file.
+TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
+ base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt");
+ ASSERT_FALSE(PathExists(bogus_input_file));
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>());
+ MockPrefStoreObserver mock_observer;
+ pref_store->AddObserver(&mock_observer);
+
+ MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate;
+ pref_store->ReadPrefsAsync(mock_error_delegate);
+
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
+ EXPECT_CALL(*mock_error_delegate,
+ OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1);
+ RunLoop().RunUntilIdle();
+ pref_store->RemoveObserver(&mock_observer);
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+}
+
+TEST_F(JsonPrefStoreTest, ReadWithInterceptor) {
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ ASSERT_LT(0, base::WriteFile(input_file,
+ kReadJson, arraysize(kReadJson) - 1));
+
+ scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter(
+ new InterceptingPrefFilter());
+ InterceptingPrefFilter* raw_intercepting_pref_filter_ =
+ intercepting_pref_filter.get();
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(input_file, message_loop_.task_runner(),
+ std::move(intercepting_pref_filter));
+
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE,
+ pref_store->ReadPrefs());
+ EXPECT_FALSE(pref_store->ReadOnly());
+
+ // The store shouldn't be considered initialized until the interceptor
+ // returns.
+ EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+ EXPECT_FALSE(pref_store->IsInitializationComplete());
+ EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL));
+
+ raw_intercepting_pref_filter_->ReleasePrefs();
+
+ EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+ EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL));
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+}
+
+TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ ASSERT_LT(0, base::WriteFile(input_file,
+ kReadJson, arraysize(kReadJson) - 1));
+
+ scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter(
+ new InterceptingPrefFilter());
+ InterceptingPrefFilter* raw_intercepting_pref_filter_ =
+ intercepting_pref_filter.get();
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(input_file, message_loop_.task_runner(),
+ std::move(intercepting_pref_filter));
+
+ MockPrefStoreObserver mock_observer;
+ pref_store->AddObserver(&mock_observer);
+
+ // Ownership of the |mock_error_delegate| is handed to the |pref_store| below.
+ MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
+
+ {
+ pref_store->ReadPrefsAsync(mock_error_delegate);
+
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0);
+ // EXPECT_CALL(*mock_error_delegate,
+ // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
+ RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+ EXPECT_FALSE(pref_store->IsInitializationComplete());
+ EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL));
+ }
+
+ {
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
+ // EXPECT_CALL(*mock_error_delegate,
+ // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
+
+ raw_intercepting_pref_filter_->ReleasePrefs();
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+ EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL));
+ }
+
+ pref_store->RemoveObserver(&mock_observer);
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+}
+
+TEST_F(JsonPrefStoreTest, AlternateFile) {
+ base::FilePath alternate_input_file =
+ temp_dir_.path().AppendASCII("alternate.json");
+ ASSERT_LT(0, base::WriteFile(alternate_input_file,
+ kReadJson, arraysize(kReadJson) - 1));
+
+ // Test that the alternate file is moved to the main file and read as-is from
+ // there.
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ ASSERT_FALSE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(input_file, alternate_input_file,
+ message_loop_.task_runner(), scoped_ptr<PrefFilter>());
+
+ ASSERT_FALSE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_FALSE(PathExists(alternate_input_file));
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+}
+
+TEST_F(JsonPrefStoreTest, AlternateFileIgnoredWhenMainFileExists) {
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ ASSERT_LT(0, base::WriteFile(input_file,
+ kReadJson, arraysize(kReadJson) - 1));
+
+ base::FilePath alternate_input_file =
+ temp_dir_.path().AppendASCII("alternate.json");
+ ASSERT_LT(0, base::WriteFile(alternate_input_file,
+ kInvalidJson, arraysize(kInvalidJson) - 1));
+
+ // Test that the alternate file is ignored and that the read occurs from the
+ // existing main file. There is no attempt at even deleting the alternate
+ // file as this scenario should never happen in normal user-data-dirs.
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(input_file, alternate_input_file,
+ message_loop_.task_runner(), scoped_ptr<PrefFilter>());
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+}
+
+TEST_F(JsonPrefStoreTest, AlternateFileDNE) {
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ ASSERT_LT(0, base::WriteFile(input_file,
+ kReadJson, arraysize(kReadJson) - 1));
+
+ // Test that the basic read works fine when an alternate file is specified but
+ // does not exist.
+ base::FilePath alternate_input_file =
+ temp_dir_.path().AppendASCII("alternate.json");
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_FALSE(PathExists(alternate_input_file));
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(input_file, alternate_input_file,
+ message_loop_.task_runner(), scoped_ptr<PrefFilter>());
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_FALSE(PathExists(alternate_input_file));
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_FALSE(PathExists(alternate_input_file));
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+}
+
+TEST_F(JsonPrefStoreTest, BasicAsyncWithAlternateFile) {
+ base::FilePath alternate_input_file =
+ temp_dir_.path().AppendASCII("alternate.json");
+ ASSERT_LT(0, base::WriteFile(alternate_input_file,
+ kReadJson, arraysize(kReadJson) - 1));
+
+ // Test that the alternate file is moved to the main file and read as-is from
+ // there even when the read is made asynchronously.
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(input_file, alternate_input_file,
+ message_loop_.task_runner(), scoped_ptr<PrefFilter>());
+
+ ASSERT_FALSE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+
+ {
+ MockPrefStoreObserver mock_observer;
+ pref_store->AddObserver(&mock_observer);
+
+ MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
+ pref_store->ReadPrefsAsync(mock_error_delegate);
+
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
+ EXPECT_CALL(*mock_error_delegate,
+ OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
+ RunLoop().RunUntilIdle();
+ pref_store->RemoveObserver(&mock_observer);
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+ }
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_FALSE(PathExists(alternate_input_file));
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(pref_store.get(), input_file);
+}
+
+TEST_F(JsonPrefStoreTest, WriteCountHistogramTestBasic) {
+ base::HistogramTester histogram_tester;
+
+ SimpleTestClock* test_clock = new SimpleTestClock;
+ SetCurrentTimeInMinutes(0, test_clock);
+ JsonPrefStore::WriteCountHistogram histogram(
+ base::TimeDelta::FromSeconds(10),
+ base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
+ scoped_ptr<base::Clock>(test_clock));
+ int32_t report_interval =
+ JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins;
+
+ histogram.RecordWriteOccured();
+
+ SetCurrentTimeInMinutes(1.5 * report_interval, test_clock);
+ histogram.ReportOutstandingWrites();
+ scoped_ptr<HistogramSamples> samples =
+ histogram.GetHistogram()->SnapshotSamples();
+
+ std::string histogram_name = histogram.GetHistogram()->histogram_name();
+ histogram_tester.ExpectBucketCount(histogram_name, 1, 1);
+ histogram_tester.ExpectTotalCount(histogram_name, 1);
+
+ ASSERT_EQ("Settings.JsonDataWriteCount.Local_State",
+ histogram.GetHistogram()->histogram_name());
+ ASSERT_TRUE(histogram.GetHistogram()->HasConstructionArguments(1, 30, 31));
+}
+
+TEST_F(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod) {
+ base::HistogramTester histogram_tester;
+
+ SimpleTestClock* test_clock = new SimpleTestClock;
+ SetCurrentTimeInMinutes(0, test_clock);
+ JsonPrefStore::WriteCountHistogram histogram(
+ base::TimeDelta::FromSeconds(10),
+ base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
+ scoped_ptr<base::Clock>(test_clock));
+ int32_t report_interval =
+ JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins;
+
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(0.5 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(0.7 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+
+ // Nothing should be recorded until the report period has elapsed.
+ std::string histogram_name = histogram.GetHistogram()->histogram_name();
+ histogram_tester.ExpectTotalCount(histogram_name, 0);
+
+ SetCurrentTimeInMinutes(1.3 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+
+ // Now the report period has elapsed.
+ histogram_tester.ExpectBucketCount(histogram_name, 3, 1);
+ histogram_tester.ExpectTotalCount(histogram_name, 1);
+
+ // The last write won't be recorded because the second count period hasn't
+ // fully elapsed.
+ SetCurrentTimeInMinutes(1.5 * report_interval, test_clock);
+ histogram.ReportOutstandingWrites();
+
+ histogram_tester.ExpectBucketCount(histogram_name, 3, 1);
+ histogram_tester.ExpectTotalCount(histogram_name, 1);
+}
+
+TEST_F(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods) {
+ base::HistogramTester histogram_tester;
+
+ SimpleTestClock* test_clock = new SimpleTestClock;
+ SetCurrentTimeInMinutes(0, test_clock);
+ JsonPrefStore::WriteCountHistogram histogram(
+ base::TimeDelta::FromSeconds(10),
+ base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
+ scoped_ptr<base::Clock>(test_clock));
+ int32_t report_interval =
+ JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins;
+
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(0.5 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(0.7 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(1.3 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(1.5 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(2.1 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(2.5 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(2.7 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(3.3 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+
+ // The last write won't be recorded because the second count period hasn't
+ // fully elapsed
+ SetCurrentTimeInMinutes(3.5 * report_interval, test_clock);
+ histogram.ReportOutstandingWrites();
+ std::string histogram_name = histogram.GetHistogram()->histogram_name();
+ histogram_tester.ExpectBucketCount(histogram_name, 3, 2);
+ histogram_tester.ExpectBucketCount(histogram_name, 2, 1);
+ histogram_tester.ExpectTotalCount(histogram_name, 3);
+}
+
+TEST_F(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps) {
+ base::HistogramTester histogram_tester;
+
+ SimpleTestClock* test_clock = new SimpleTestClock;
+ SetCurrentTimeInMinutes(0, test_clock);
+ JsonPrefStore::WriteCountHistogram histogram(
+ base::TimeDelta::FromSeconds(10),
+ base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")),
+ scoped_ptr<base::Clock>(test_clock));
+ int32_t report_interval =
+ JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins;
+
+ // 1 write in the first period.
+ histogram.RecordWriteOccured();
+
+ // No writes in the second and third periods.
+
+ // 2 writes in the fourth period.
+ SetCurrentTimeInMinutes(3.1 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(3.3 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+
+ // No writes in the fifth period.
+
+ // 3 writes in the sixth period.
+ SetCurrentTimeInMinutes(5.1 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(5.3 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+ SetCurrentTimeInMinutes(5.5 * report_interval, test_clock);
+ histogram.RecordWriteOccured();
+
+ SetCurrentTimeInMinutes(6.1 * report_interval, test_clock);
+ histogram.ReportOutstandingWrites();
+ std::string histogram_name = histogram.GetHistogram()->histogram_name();
+ histogram_tester.ExpectBucketCount(histogram_name, 0, 3);
+ histogram_tester.ExpectBucketCount(histogram_name, 1, 1);
+ histogram_tester.ExpectBucketCount(histogram_name, 2, 1);
+ histogram_tester.ExpectBucketCount(histogram_name, 3, 1);
+ histogram_tester.ExpectTotalCount(histogram_name, 6);
+}
+
+class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest {
+ protected:
+ void SetUp() override {
+ JsonPrefStoreTest::SetUp();
+ test_file_ = temp_dir_.path().AppendASCII("test.json");
+ }
+
+ // Creates a JsonPrefStore with the given |file_writer|.
+ scoped_refptr<JsonPrefStore> CreatePrefStore() {
+ return new JsonPrefStore(test_file_, message_loop_.task_runner(),
+ scoped_ptr<PrefFilter>());
+ }
+
+ // Return the ImportantFileWriter for a given JsonPrefStore.
+ ImportantFileWriter* GetImportantFileWriter(
+ scoped_refptr<JsonPrefStore> pref_store) {
+ return &(pref_store->writer_);
+ }
+
+ // Get the contents of kTestFile. Pumps the message loop before returning the
+ // result.
+ std::string GetTestFileContents() {
+ RunLoop().RunUntilIdle();
+ std::string file_contents;
+ ReadFileToString(test_file_, &file_contents);
+ return file_contents;
+ }
+
+ private:
+ base::FilePath test_file_;
+};
+
+TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteBasic) {
+ scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
+ ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
+
+ // Set a normal pref and check that it gets scheduled to be written.
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+ pref_store->SetValue("normal",
+ make_scoped_ptr(new base::StringValue("normal")),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ ASSERT_TRUE(file_writer->HasPendingWrite());
+ file_writer->DoScheduledWrite();
+ ASSERT_EQ("{\"normal\":\"normal\"}", GetTestFileContents());
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+
+ // Set a lossy pref and check that it is not scheduled to be written.
+ // SetValue/RemoveValue.
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+ pref_store->RemoveValue("lossy", WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+
+ // SetValueSilently/RemoveValueSilently.
+ pref_store->SetValueSilently("lossy",
+ make_scoped_ptr(new base::StringValue("lossy")),
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+ pref_store->RemoveValueSilently("lossy",
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+
+ // ReportValueChanged.
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+ pref_store->ReportValueChanged("lossy",
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+
+ // Call CommitPendingWrite and check that the lossy pref and the normal pref
+ // are there with the last values set above.
+ pref_store->CommitPendingWrite();
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+ ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
+ GetTestFileContents());
+}
+
+TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) {
+ scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
+ ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
+
+ // Set a lossy pref and check that it is not scheduled to be written.
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+
+ // Set a normal pref and check that it is scheduled to be written.
+ pref_store->SetValue("normal",
+ make_scoped_ptr(new base::StringValue("normal")),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ ASSERT_TRUE(file_writer->HasPendingWrite());
+
+ // Call DoScheduledWrite and check both prefs get written.
+ file_writer->DoScheduledWrite();
+ ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
+ GetTestFileContents());
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+}
+
+TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) {
+ scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
+ ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
+
+ // Set a normal pref and check that it is scheduled to be written.
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+ pref_store->SetValue("normal",
+ make_scoped_ptr(new base::StringValue("normal")),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ ASSERT_TRUE(file_writer->HasPendingWrite());
+
+ // Set a lossy pref and check that the write is still scheduled.
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+ ASSERT_TRUE(file_writer->HasPendingWrite());
+
+ // Call DoScheduledWrite and check both prefs get written.
+ file_writer->DoScheduledWrite();
+ ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}",
+ GetTestFileContents());
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+}
+
+TEST_F(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) {
+ scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore();
+ ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store);
+
+ // Set a lossy pref and check that it is not scheduled to be written.
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")),
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG);
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+
+ // Schedule pending lossy writes and check that it is scheduled.
+ pref_store->SchedulePendingLossyWrites();
+ ASSERT_TRUE(file_writer->HasPendingWrite());
+
+ // Call CommitPendingWrite and check that the lossy pref is there with the
+ // last value set above.
+ pref_store->CommitPendingWrite();
+ ASSERT_FALSE(file_writer->HasPendingWrite());
+ ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents());
+}
+
+} // namespace base
diff --git a/chromium/components/prefs/mock_pref_change_callback.cc b/chromium/components/prefs/mock_pref_change_callback.cc
new file mode 100644
index 00000000000..7ac2cd78451
--- /dev/null
+++ b/chromium/components/prefs/mock_pref_change_callback.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 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 "components/prefs/mock_pref_change_callback.h"
+
+#include "base/bind.h"
+
+MockPrefChangeCallback::MockPrefChangeCallback(PrefService* prefs)
+ : prefs_(prefs) {
+}
+
+MockPrefChangeCallback::~MockPrefChangeCallback() {}
+
+PrefChangeRegistrar::NamedChangeCallback MockPrefChangeCallback::GetCallback() {
+ return base::Bind(&MockPrefChangeCallback::OnPreferenceChanged,
+ base::Unretained(this));
+}
+
+void MockPrefChangeCallback::Expect(const std::string& pref_name,
+ const base::Value* value) {
+ EXPECT_CALL(*this, OnPreferenceChanged(pref_name))
+ .With(PrefValueMatches(prefs_, pref_name, value));
+}
diff --git a/chromium/components/prefs/mock_pref_change_callback.h b/chromium/components/prefs/mock_pref_change_callback.h
new file mode 100644
index 00000000000..9c0aeecd9c3
--- /dev/null
+++ b/chromium/components/prefs/mock_pref_change_callback.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 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 COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_
+#define COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_
+
+#include <string>
+
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::Pointee;
+using testing::Property;
+using testing::Truly;
+
+// Matcher that checks whether the current value of the preference named
+// |pref_name| in |prefs| matches |value|. If |value| is NULL, the matcher
+// checks that the value is not set.
+MATCHER_P3(PrefValueMatches, prefs, pref_name, value, "") {
+ const PrefService::Preference* pref = prefs->FindPreference(pref_name);
+ if (!pref)
+ return false;
+
+ const base::Value* actual_value = pref->GetValue();
+ if (!actual_value)
+ return value == NULL;
+ if (!value)
+ return actual_value == NULL;
+ return value->Equals(actual_value);
+}
+
+// A mock for testing preference notifications and easy setup of expectations.
+class MockPrefChangeCallback {
+ public:
+ explicit MockPrefChangeCallback(PrefService* prefs);
+ virtual ~MockPrefChangeCallback();
+
+ PrefChangeRegistrar::NamedChangeCallback GetCallback();
+
+ MOCK_METHOD1(OnPreferenceChanged, void(const std::string&));
+
+ void Expect(const std::string& pref_name,
+ const base::Value* value);
+
+ private:
+ PrefService* prefs_;
+};
+
+#endif // COMPONENTS_PREFS_MOCK_PREF_CHANGE_CALLBACK_H_
diff --git a/chromium/components/prefs/overlay_user_pref_store.cc b/chromium/components/prefs/overlay_user_pref_store.cc
new file mode 100644
index 00000000000..4d0905326d6
--- /dev/null
+++ b/chromium/components/prefs/overlay_user_pref_store.cc
@@ -0,0 +1,190 @@
+// Copyright (c) 2012 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 "components/prefs/overlay_user_pref_store.h"
+
+#include <utility>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+OverlayUserPrefStore::OverlayUserPrefStore(
+ PersistentPrefStore* underlay)
+ : underlay_(underlay) {
+ underlay_->AddObserver(this);
+}
+
+bool OverlayUserPrefStore::IsSetInOverlay(const std::string& key) const {
+ return overlay_.GetValue(key, NULL);
+}
+
+void OverlayUserPrefStore::AddObserver(PrefStore::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void OverlayUserPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool OverlayUserPrefStore::HasObservers() const {
+ return observers_.might_have_observers();
+}
+
+bool OverlayUserPrefStore::IsInitializationComplete() const {
+ return underlay_->IsInitializationComplete();
+}
+
+bool OverlayUserPrefStore::GetValue(const std::string& key,
+ const base::Value** result) const {
+ // If the |key| shall NOT be stored in the overlay store, there must not
+ // be an entry.
+ DCHECK(ShallBeStoredInOverlay(key) || !overlay_.GetValue(key, NULL));
+
+ if (overlay_.GetValue(key, result))
+ return true;
+ return underlay_->GetValue(GetUnderlayKey(key), result);
+}
+
+bool OverlayUserPrefStore::GetMutableValue(const std::string& key,
+ base::Value** result) {
+ if (!ShallBeStoredInOverlay(key))
+ return underlay_->GetMutableValue(GetUnderlayKey(key), result);
+
+ if (overlay_.GetValue(key, result))
+ return true;
+
+ // Try to create copy of underlay if the overlay does not contain a value.
+ base::Value* underlay_value = NULL;
+ if (!underlay_->GetMutableValue(GetUnderlayKey(key), &underlay_value))
+ return false;
+
+ *result = underlay_value->DeepCopy();
+ overlay_.SetValue(key, make_scoped_ptr(*result));
+ return true;
+}
+
+void OverlayUserPrefStore::SetValue(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) {
+ if (!ShallBeStoredInOverlay(key)) {
+ underlay_->SetValue(GetUnderlayKey(key), std::move(value), flags);
+ return;
+ }
+
+ if (overlay_.SetValue(key, std::move(value)))
+ ReportValueChanged(key, flags);
+}
+
+void OverlayUserPrefStore::SetValueSilently(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) {
+ if (!ShallBeStoredInOverlay(key)) {
+ underlay_->SetValueSilently(GetUnderlayKey(key), std::move(value), flags);
+ return;
+ }
+
+ overlay_.SetValue(key, std::move(value));
+}
+
+void OverlayUserPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
+ if (!ShallBeStoredInOverlay(key)) {
+ underlay_->RemoveValue(GetUnderlayKey(key), flags);
+ return;
+ }
+
+ if (overlay_.RemoveValue(key))
+ ReportValueChanged(key, flags);
+}
+
+bool OverlayUserPrefStore::ReadOnly() const {
+ return false;
+}
+
+PersistentPrefStore::PrefReadError OverlayUserPrefStore::GetReadError() const {
+ return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+PersistentPrefStore::PrefReadError OverlayUserPrefStore::ReadPrefs() {
+ // We do not read intentionally.
+ OnInitializationCompleted(true);
+ return PersistentPrefStore::PREF_READ_ERROR_NONE;
+}
+
+void OverlayUserPrefStore::ReadPrefsAsync(
+ ReadErrorDelegate* error_delegate_raw) {
+ scoped_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw);
+ // We do not read intentionally.
+ OnInitializationCompleted(true);
+}
+
+void OverlayUserPrefStore::CommitPendingWrite() {
+ underlay_->CommitPendingWrite();
+ // We do not write our content intentionally.
+}
+
+void OverlayUserPrefStore::SchedulePendingLossyWrites() {
+ underlay_->SchedulePendingLossyWrites();
+}
+
+void OverlayUserPrefStore::ReportValueChanged(const std::string& key,
+ uint32_t flags) {
+ FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
+}
+
+void OverlayUserPrefStore::OnPrefValueChanged(const std::string& key) {
+ if (!overlay_.GetValue(GetOverlayKey(key), NULL))
+ ReportValueChanged(GetOverlayKey(key), DEFAULT_PREF_WRITE_FLAGS);
+}
+
+void OverlayUserPrefStore::OnInitializationCompleted(bool succeeded) {
+ FOR_EACH_OBSERVER(PrefStore::Observer, observers_,
+ OnInitializationCompleted(succeeded));
+}
+
+void OverlayUserPrefStore::RegisterOverlayPref(const std::string& key) {
+ RegisterOverlayPref(key, key);
+}
+
+void OverlayUserPrefStore::RegisterOverlayPref(
+ const std::string& overlay_key,
+ const std::string& underlay_key) {
+ DCHECK(!overlay_key.empty()) << "Overlay key is empty";
+ DCHECK(overlay_to_underlay_names_map_.find(overlay_key) ==
+ overlay_to_underlay_names_map_.end()) <<
+ "Overlay key already registered";
+ DCHECK(!underlay_key.empty()) << "Underlay key is empty";
+ DCHECK(underlay_to_overlay_names_map_.find(underlay_key) ==
+ underlay_to_overlay_names_map_.end()) <<
+ "Underlay key already registered";
+ overlay_to_underlay_names_map_[overlay_key] = underlay_key;
+ underlay_to_overlay_names_map_[underlay_key] = overlay_key;
+}
+
+void OverlayUserPrefStore::ClearMutableValues() {
+ overlay_.Clear();
+}
+
+OverlayUserPrefStore::~OverlayUserPrefStore() {
+ underlay_->RemoveObserver(this);
+}
+
+const std::string& OverlayUserPrefStore::GetOverlayKey(
+ const std::string& underlay_key) const {
+ NamesMap::const_iterator i =
+ underlay_to_overlay_names_map_.find(underlay_key);
+ return i != underlay_to_overlay_names_map_.end() ? i->second : underlay_key;
+}
+
+const std::string& OverlayUserPrefStore::GetUnderlayKey(
+ const std::string& overlay_key) const {
+ NamesMap::const_iterator i =
+ overlay_to_underlay_names_map_.find(overlay_key);
+ return i != overlay_to_underlay_names_map_.end() ? i->second : overlay_key;
+}
+
+bool OverlayUserPrefStore::ShallBeStoredInOverlay(
+ const std::string& key) const {
+ return overlay_to_underlay_names_map_.find(key) !=
+ overlay_to_underlay_names_map_.end();
+}
diff --git a/chromium/components/prefs/overlay_user_pref_store.h b/chromium/components/prefs/overlay_user_pref_store.h
new file mode 100644
index 00000000000..c3382c25d4e
--- /dev/null
+++ b/chromium/components/prefs/overlay_user_pref_store.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_
+#define COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/persistent_pref_store.h"
+#include "components/prefs/pref_value_map.h"
+
+// PersistentPrefStore that directs all write operations into an in-memory
+// PrefValueMap. Read operations are first answered by the PrefValueMap.
+// If the PrefValueMap does not contain a value for the requested key,
+// the look-up is passed on to an underlying PersistentPrefStore |underlay_|.
+class COMPONENTS_PREFS_EXPORT OverlayUserPrefStore : public PersistentPrefStore,
+ public PrefStore::Observer {
+ public:
+ explicit OverlayUserPrefStore(PersistentPrefStore* underlay);
+
+ // Returns true if a value has been set for the |key| in this
+ // OverlayUserPrefStore, i.e. if it potentially overrides a value
+ // from the |underlay_|.
+ virtual bool IsSetInOverlay(const std::string& key) const;
+
+ // Methods of PrefStore.
+ void AddObserver(PrefStore::Observer* observer) override;
+ void RemoveObserver(PrefStore::Observer* observer) override;
+ bool HasObservers() const override;
+ bool IsInitializationComplete() const override;
+ bool GetValue(const std::string& key,
+ const base::Value** result) const override;
+
+ // Methods of PersistentPrefStore.
+ bool GetMutableValue(const std::string& key, base::Value** result) override;
+ void SetValue(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) override;
+ void SetValueSilently(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) override;
+ void RemoveValue(const std::string& key, uint32_t flags) override;
+ bool ReadOnly() const override;
+ PrefReadError GetReadError() const override;
+ PrefReadError ReadPrefs() override;
+ void ReadPrefsAsync(ReadErrorDelegate* delegate) override;
+ void CommitPendingWrite() override;
+ void SchedulePendingLossyWrites() override;
+ void ReportValueChanged(const std::string& key, uint32_t flags) override;
+
+ // Methods of PrefStore::Observer.
+ void OnPrefValueChanged(const std::string& key) override;
+ void OnInitializationCompleted(bool succeeded) override;
+
+ void RegisterOverlayPref(const std::string& key);
+ void RegisterOverlayPref(const std::string& overlay_key,
+ const std::string& underlay_key);
+
+ void ClearMutableValues() override;
+
+ protected:
+ ~OverlayUserPrefStore() override;
+
+ private:
+ typedef std::map<std::string, std::string> NamesMap;
+
+ const std::string& GetOverlayKey(const std::string& underlay_key) const;
+ const std::string& GetUnderlayKey(const std::string& overlay_key) const;
+
+ // Returns true if |key| corresponds to a preference that shall be stored in
+ // an in-memory PrefStore that is not persisted to disk.
+ bool ShallBeStoredInOverlay(const std::string& key) const;
+
+ base::ObserverList<PrefStore::Observer, true> observers_;
+ PrefValueMap overlay_;
+ scoped_refptr<PersistentPrefStore> underlay_;
+ NamesMap overlay_to_underlay_names_map_;
+ NamesMap underlay_to_overlay_names_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(OverlayUserPrefStore);
+};
+
+#endif // COMPONENTS_PREFS_OVERLAY_USER_PREF_STORE_H_
diff --git a/chromium/components/prefs/overlay_user_pref_store_unittest.cc b/chromium/components/prefs/overlay_user_pref_store_unittest.cc
new file mode 100644
index 00000000000..1ac185e9301
--- /dev/null
+++ b/chromium/components/prefs/overlay_user_pref_store_unittest.cc
@@ -0,0 +1,311 @@
+// Copyright (c) 2012 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 "components/prefs/overlay_user_pref_store.h"
+
+#include "base/values.h"
+#include "components/prefs/pref_store_observer_mock.h"
+#include "components/prefs/testing_pref_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::Mock;
+using ::testing::StrEq;
+
+namespace base {
+namespace {
+
+const char kBrowserWindowPlacement[] = "browser.window_placement";
+const char kShowBookmarkBar[] = "bookmark_bar.show_on_all_tabs";
+
+const char* const overlay_key = kBrowserWindowPlacement;
+const char* const regular_key = kShowBookmarkBar;
+// With the removal of the kWebKitGlobalXXX prefs, we'll no longer have real
+// prefs using the overlay pref store, so make up keys here.
+const char mapped_overlay_key[] = "test.per_tab.javascript_enabled";
+const char mapped_underlay_key[] = "test.per_profile.javascript_enabled";
+
+} // namespace
+
+class OverlayUserPrefStoreTest : public testing::Test {
+ protected:
+ OverlayUserPrefStoreTest()
+ : underlay_(new TestingPrefStore()),
+ overlay_(new OverlayUserPrefStore(underlay_.get())) {
+ overlay_->RegisterOverlayPref(overlay_key);
+ overlay_->RegisterOverlayPref(mapped_overlay_key, mapped_underlay_key);
+ }
+
+ ~OverlayUserPrefStoreTest() override {}
+
+ scoped_refptr<TestingPrefStore> underlay_;
+ scoped_refptr<OverlayUserPrefStore> overlay_;
+};
+
+TEST_F(OverlayUserPrefStoreTest, Observer) {
+ PrefStoreObserverMock obs;
+ overlay_->AddObserver(&obs);
+
+ // Check that underlay first value is reported.
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(42)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(overlay_key);
+
+ // Check that underlay overwriting is reported.
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(43)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(overlay_key);
+
+ // Check that overwriting change in overlay is reported.
+ overlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(44)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(overlay_key);
+
+ // Check that hidden underlay change is not reported.
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(45)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(obs.changed_keys.empty());
+
+ // Check that overlay remove is reported.
+ overlay_->RemoveValue(overlay_key,
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(overlay_key);
+
+ // Check that underlay remove is reported.
+ underlay_->RemoveValue(overlay_key,
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(overlay_key);
+
+ // Check respecting of silence.
+ overlay_->SetValueSilently(overlay_key,
+ make_scoped_ptr(new FundamentalValue(46)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(obs.changed_keys.empty());
+
+ overlay_->RemoveObserver(&obs);
+
+ // Check successful unsubscription.
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(47)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ overlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(48)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(obs.changed_keys.empty());
+}
+
+TEST_F(OverlayUserPrefStoreTest, GetAndSet) {
+ const Value* value = NULL;
+ EXPECT_FALSE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_FALSE(underlay_->GetValue(overlay_key, &value));
+
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(42)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+ // Value shines through:
+ EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
+
+ EXPECT_TRUE(underlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
+
+ overlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(43)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+ EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+
+ EXPECT_TRUE(underlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
+
+ overlay_->RemoveValue(overlay_key,
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+ // Value shines through:
+ EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
+
+ EXPECT_TRUE(underlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
+}
+
+// Check that GetMutableValue does not return the dictionary of the underlay.
+TEST_F(OverlayUserPrefStoreTest, ModifyDictionaries) {
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new DictionaryValue),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+ Value* modify = NULL;
+ EXPECT_TRUE(overlay_->GetMutableValue(overlay_key, &modify));
+ ASSERT_TRUE(modify);
+ ASSERT_TRUE(modify->IsType(Value::TYPE_DICTIONARY));
+ static_cast<DictionaryValue*>(modify)->SetInteger(overlay_key, 42);
+
+ Value* original_in_underlay = NULL;
+ EXPECT_TRUE(underlay_->GetMutableValue(overlay_key, &original_in_underlay));
+ ASSERT_TRUE(original_in_underlay);
+ ASSERT_TRUE(original_in_underlay->IsType(Value::TYPE_DICTIONARY));
+ EXPECT_TRUE(static_cast<DictionaryValue*>(original_in_underlay)->empty());
+
+ Value* modified = NULL;
+ EXPECT_TRUE(overlay_->GetMutableValue(overlay_key, &modified));
+ ASSERT_TRUE(modified);
+ ASSERT_TRUE(modified->IsType(Value::TYPE_DICTIONARY));
+ EXPECT_TRUE(Value::Equals(modify, static_cast<DictionaryValue*>(modified)));
+}
+
+// Here we consider a global preference that is not overlayed.
+TEST_F(OverlayUserPrefStoreTest, GlobalPref) {
+ PrefStoreObserverMock obs;
+ overlay_->AddObserver(&obs);
+
+ const Value* value = NULL;
+
+ // Check that underlay first value is reported.
+ underlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(42)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(regular_key);
+
+ // Check that underlay overwriting is reported.
+ underlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(43)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(regular_key);
+
+ // Check that we get this value from the overlay
+ EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+
+ // Check that overwriting change in overlay is reported.
+ overlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(44)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(regular_key);
+
+ // Check that we get this value from the overlay and the underlay.
+ EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(44).Equals(value));
+ EXPECT_TRUE(underlay_->GetValue(regular_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(44).Equals(value));
+
+ // Check that overlay remove is reported.
+ overlay_->RemoveValue(regular_key,
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(regular_key);
+
+ // Check that value was removed from overlay and underlay
+ EXPECT_FALSE(overlay_->GetValue(regular_key, &value));
+ EXPECT_FALSE(underlay_->GetValue(regular_key, &value));
+
+ // Check respecting of silence.
+ overlay_->SetValueSilently(regular_key,
+ make_scoped_ptr(new FundamentalValue(46)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(obs.changed_keys.empty());
+
+ overlay_->RemoveObserver(&obs);
+
+ // Check successful unsubscription.
+ underlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(47)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ overlay_->SetValue(regular_key, make_scoped_ptr(new FundamentalValue(48)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(obs.changed_keys.empty());
+}
+
+// Check that names mapping works correctly.
+TEST_F(OverlayUserPrefStoreTest, NamesMapping) {
+ PrefStoreObserverMock obs;
+ overlay_->AddObserver(&obs);
+
+ const Value* value = NULL;
+
+ // Check that if there is no override in the overlay, changing underlay value
+ // is reported as changing an overlay value.
+ underlay_->SetValue(mapped_underlay_key,
+ make_scoped_ptr(new FundamentalValue(42)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(mapped_overlay_key);
+
+ // Check that underlay overwriting is reported.
+ underlay_->SetValue(mapped_underlay_key,
+ make_scoped_ptr(new FundamentalValue(43)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(mapped_overlay_key);
+
+ // Check that we get this value from the overlay with both keys
+ EXPECT_TRUE(overlay_->GetValue(mapped_overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+ // In this case, overlay reads directly from the underlay.
+ EXPECT_TRUE(overlay_->GetValue(mapped_underlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+
+ // Check that overwriting change in overlay is reported.
+ overlay_->SetValue(mapped_overlay_key,
+ make_scoped_ptr(new FundamentalValue(44)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(mapped_overlay_key);
+
+ // Check that we get an overriden value from overlay, while reading the
+ // value from underlay still holds an old value.
+ EXPECT_TRUE(overlay_->GetValue(mapped_overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(44).Equals(value));
+ EXPECT_TRUE(overlay_->GetValue(mapped_underlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+ EXPECT_TRUE(underlay_->GetValue(mapped_underlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+
+ // Check that hidden underlay change is not reported.
+ underlay_->SetValue(mapped_underlay_key,
+ make_scoped_ptr(new FundamentalValue(45)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(obs.changed_keys.empty());
+
+ // Check that overlay remove is reported.
+ overlay_->RemoveValue(mapped_overlay_key,
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(mapped_overlay_key);
+
+ // Check that underlay remove is reported.
+ underlay_->RemoveValue(mapped_underlay_key,
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ obs.VerifyAndResetChangedKey(mapped_overlay_key);
+
+ // Check that value was removed.
+ EXPECT_FALSE(overlay_->GetValue(mapped_overlay_key, &value));
+ EXPECT_FALSE(overlay_->GetValue(mapped_underlay_key, &value));
+
+ // Check respecting of silence.
+ overlay_->SetValueSilently(mapped_overlay_key,
+ make_scoped_ptr(new FundamentalValue(46)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(obs.changed_keys.empty());
+
+ overlay_->RemoveObserver(&obs);
+
+ // Check successful unsubscription.
+ underlay_->SetValue(mapped_underlay_key,
+ make_scoped_ptr(new FundamentalValue(47)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ overlay_->SetValue(mapped_overlay_key,
+ make_scoped_ptr(new FundamentalValue(48)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ EXPECT_TRUE(obs.changed_keys.empty());
+}
+
+// Check that mutable values are removed correctly.
+TEST_F(OverlayUserPrefStoreTest, ClearMutableValues) {
+ // Set in overlay and underlay the same preference.
+ underlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(42)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+ overlay_->SetValue(overlay_key, make_scoped_ptr(new FundamentalValue(43)),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+
+ const Value* value = nullptr;
+ // Check that an overlay preference is returned.
+ EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
+ overlay_->ClearMutableValues();
+
+ // Check that an underlay preference is returned.
+ EXPECT_TRUE(overlay_->GetValue(overlay_key, &value));
+ EXPECT_TRUE(base::FundamentalValue(42).Equals(value));
+}
+
+} // namespace base
diff --git a/chromium/components/prefs/persistent_pref_store.h b/chromium/components/prefs/persistent_pref_store.h
new file mode 100644
index 00000000000..7bc28278be6
--- /dev/null
+++ b/chromium/components/prefs/persistent_pref_store.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_
+#define COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_
+
+#include <string>
+
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/writeable_pref_store.h"
+
+// This interface is complementary to the PrefStore interface, declaring
+// additional functionality that adds support for setting values and persisting
+// the data to some backing store.
+class COMPONENTS_PREFS_EXPORT PersistentPrefStore : public WriteablePrefStore {
+ public:
+ // Unique integer code for each type of error so we can report them
+ // distinctly in a histogram.
+ // NOTE: Don't change the explicit values of the enums as it will change the
+ // server's meaning of the histogram.
+ enum PrefReadError {
+ PREF_READ_ERROR_NONE = 0,
+ PREF_READ_ERROR_JSON_PARSE = 1,
+ PREF_READ_ERROR_JSON_TYPE = 2,
+ PREF_READ_ERROR_ACCESS_DENIED = 3,
+ PREF_READ_ERROR_FILE_OTHER = 4,
+ PREF_READ_ERROR_FILE_LOCKED = 5,
+ PREF_READ_ERROR_NO_FILE = 6,
+ PREF_READ_ERROR_JSON_REPEAT = 7,
+ // PREF_READ_ERROR_OTHER = 8, // Deprecated.
+ PREF_READ_ERROR_FILE_NOT_SPECIFIED = 9,
+ // Indicates that ReadPrefs() couldn't complete synchronously and is waiting
+ // for an asynchronous task to complete first.
+ PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE = 10,
+ PREF_READ_ERROR_MAX_ENUM
+ };
+
+ class ReadErrorDelegate {
+ public:
+ virtual ~ReadErrorDelegate() {}
+
+ virtual void OnError(PrefReadError error) = 0;
+ };
+
+ // Whether the store is in a pseudo-read-only mode where changes are not
+ // actually persisted to disk. This happens in some cases when there are
+ // read errors during startup.
+ virtual bool ReadOnly() const = 0;
+
+ // Gets the read error. Only valid if IsInitializationComplete() returns true.
+ virtual PrefReadError GetReadError() const = 0;
+
+ // Reads the preferences from disk. Notifies observers via
+ // "PrefStore::OnInitializationCompleted" when done.
+ virtual PrefReadError ReadPrefs() = 0;
+
+ // Reads the preferences from disk asynchronously. Notifies observers via
+ // "PrefStore::OnInitializationCompleted" when done. Also it fires
+ // |error_delegate| if it is not NULL and reading error has occurred.
+ // Owns |error_delegate|.
+ virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) = 0;
+
+ // Lands any pending writes to disk.
+ virtual void CommitPendingWrite() = 0;
+
+ // Schedule a write if there is any lossy data pending. Unlike
+ // CommitPendingWrite() this does not immediately sync to disk, instead it
+ // triggers an eventual write if there is lossy data pending and if there
+ // isn't one scheduled already.
+ virtual void SchedulePendingLossyWrites() = 0;
+
+ // It should be called only for Incognito pref store.
+ virtual void ClearMutableValues() = 0;
+
+ protected:
+ ~PersistentPrefStore() override {}
+};
+
+#endif // COMPONENTS_PREFS_PERSISTENT_PREF_STORE_H_
diff --git a/chromium/components/prefs/pref_change_registrar.cc b/chromium/components/prefs/pref_change_registrar.cc
new file mode 100644
index 00000000000..72c1a4927cd
--- /dev/null
+++ b/chromium/components/prefs/pref_change_registrar.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2010 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 "components/prefs/pref_change_registrar.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "components/prefs/pref_service.h"
+
+PrefChangeRegistrar::PrefChangeRegistrar() : service_(NULL) {}
+
+PrefChangeRegistrar::~PrefChangeRegistrar() {
+ // If you see an invalid memory access in this destructor, this
+ // PrefChangeRegistrar might be subscribed to an OffTheRecordProfileImpl that
+ // has been destroyed. This should not happen any more but be warned.
+ // Feel free to contact battre@chromium.org in case this happens.
+ RemoveAll();
+}
+
+void PrefChangeRegistrar::Init(PrefService* service) {
+ DCHECK(IsEmpty() || service_ == service);
+ service_ = service;
+}
+
+void PrefChangeRegistrar::Add(const std::string& path,
+ const base::Closure& obs) {
+ Add(path, base::Bind(&PrefChangeRegistrar::InvokeUnnamedCallback, obs));
+}
+
+void PrefChangeRegistrar::Add(const std::string& path,
+ const NamedChangeCallback& obs) {
+ if (!service_) {
+ NOTREACHED();
+ return;
+ }
+ DCHECK(!IsObserved(path)) << "Already had pref, \"" << path
+ << "\", registered.";
+
+ service_->AddPrefObserver(path, this);
+ observers_[path] = obs;
+}
+
+void PrefChangeRegistrar::Remove(const std::string& path) {
+ DCHECK(IsObserved(path));
+
+ observers_.erase(path);
+ service_->RemovePrefObserver(path, this);
+}
+
+void PrefChangeRegistrar::RemoveAll() {
+ for (ObserverMap::const_iterator it = observers_.begin();
+ it != observers_.end(); ++it) {
+ service_->RemovePrefObserver(it->first, this);
+ }
+
+ observers_.clear();
+}
+
+bool PrefChangeRegistrar::IsEmpty() const {
+ return observers_.empty();
+}
+
+bool PrefChangeRegistrar::IsObserved(const std::string& pref) {
+ return observers_.find(pref) != observers_.end();
+}
+
+bool PrefChangeRegistrar::IsManaged() {
+ for (ObserverMap::const_iterator it = observers_.begin();
+ it != observers_.end(); ++it) {
+ const PrefService::Preference* pref = service_->FindPreference(it->first);
+ if (pref && pref->IsManaged())
+ return true;
+ }
+ return false;
+}
+
+void PrefChangeRegistrar::OnPreferenceChanged(PrefService* service,
+ const std::string& pref) {
+ if (IsObserved(pref))
+ observers_[pref].Run(pref);
+}
+
+void PrefChangeRegistrar::InvokeUnnamedCallback(const base::Closure& callback,
+ const std::string& pref_name) {
+ callback.Run();
+}
+
+PrefService* PrefChangeRegistrar::prefs() {
+ return service_;
+}
+
+const PrefService* PrefChangeRegistrar::prefs() const {
+ return service_;
+}
diff --git a/chromium/components/prefs/pref_change_registrar.h b/chromium/components/prefs/pref_change_registrar.h
new file mode 100644
index 00000000000..2ae3b195e30
--- /dev/null
+++ b/chromium/components/prefs/pref_change_registrar.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2010 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 COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_
+#define COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/pref_observer.h"
+
+class PrefService;
+
+// Automatically manages the registration of one or more pref change observers
+// with a PrefStore. Functions much like NotificationRegistrar, but specifically
+// manages observers of preference changes. When the Registrar is destroyed,
+// all registered observers are automatically unregistered with the PrefStore.
+class COMPONENTS_PREFS_EXPORT PrefChangeRegistrar : public PrefObserver {
+ public:
+ // You can register this type of callback if you need to know the
+ // path of the preference that is changing.
+ typedef base::Callback<void(const std::string&)> NamedChangeCallback;
+
+ PrefChangeRegistrar();
+ virtual ~PrefChangeRegistrar();
+
+ // Must be called before adding or removing observers. Can be called more
+ // than once as long as the value of |service| doesn't change.
+ void Init(PrefService* service);
+
+ // Adds a pref observer for the specified pref |path| and |obs| observer
+ // object. All registered observers will be automatically unregistered
+ // when the registrar's destructor is called.
+ //
+ // The second version binds a callback that will receive the path of
+ // the preference that is changing as its parameter.
+ //
+ // Only one observer may be registered per path.
+ void Add(const std::string& path, const base::Closure& obs);
+ void Add(const std::string& path, const NamedChangeCallback& obs);
+
+ // Removes the pref observer registered for |path|.
+ void Remove(const std::string& path);
+
+ // Removes all observers that have been previously added with a call to Add.
+ void RemoveAll();
+
+ // Returns true if no pref observers are registered.
+ bool IsEmpty() const;
+
+ // Check whether |pref| is in the set of preferences being observed.
+ bool IsObserved(const std::string& pref);
+
+ // Check whether any of the observed preferences has the managed bit set.
+ bool IsManaged();
+
+ // Return the PrefService for this registrar.
+ PrefService* prefs();
+ const PrefService* prefs() const;
+
+ private:
+ // PrefObserver:
+ void OnPreferenceChanged(PrefService* service,
+ const std::string& pref_name) override;
+
+ static void InvokeUnnamedCallback(const base::Closure& callback,
+ const std::string& pref_name);
+
+ typedef std::map<std::string, NamedChangeCallback> ObserverMap;
+
+ ObserverMap observers_;
+ PrefService* service_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefChangeRegistrar);
+};
+
+#endif // COMPONENTS_PREFS_PREF_CHANGE_REGISTRAR_H_
diff --git a/chromium/components/prefs/pref_change_registrar_unittest.cc b/chromium/components/prefs/pref_change_registrar_unittest.cc
new file mode 100644
index 00000000000..ab350922a4c
--- /dev/null
+++ b/chromium/components/prefs/pref_change_registrar_unittest.cc
@@ -0,0 +1,198 @@
+// Copyright (c) 2010 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/bind.h"
+#include "base/bind_helpers.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_observer.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Mock;
+using testing::Eq;
+
+namespace base {
+namespace {
+
+const char kHomePage[] = "homepage";
+const char kHomePageIsNewTabPage[] = "homepage_is_newtabpage";
+const char kApplicationLocale[] = "intl.app_locale";
+
+// A mock provider that allows us to capture pref observer changes.
+class MockPrefService : public TestingPrefServiceSimple {
+ public:
+ MockPrefService() {}
+ virtual ~MockPrefService() {}
+
+ MOCK_METHOD2(AddPrefObserver, void(const std::string&, PrefObserver*));
+ MOCK_METHOD2(RemovePrefObserver, void(const std::string&, PrefObserver*));
+};
+
+} // namespace
+
+class PrefChangeRegistrarTest : public testing::Test {
+ public:
+ PrefChangeRegistrarTest() {}
+ ~PrefChangeRegistrarTest() override {}
+
+ protected:
+ void SetUp() override;
+
+ base::Closure observer() const {
+ return base::Bind(&base::DoNothing);
+ }
+
+ MockPrefService* service() const { return service_.get(); }
+
+ private:
+ scoped_ptr<MockPrefService> service_;
+};
+
+void PrefChangeRegistrarTest::SetUp() {
+ service_.reset(new MockPrefService());
+}
+
+TEST_F(PrefChangeRegistrarTest, AddAndRemove) {
+ PrefChangeRegistrar registrar;
+ registrar.Init(service());
+
+ // Test adding.
+ EXPECT_CALL(*service(),
+ AddPrefObserver(Eq(std::string("test.pref.1")), &registrar));
+ EXPECT_CALL(*service(),
+ AddPrefObserver(Eq(std::string("test.pref.2")), &registrar));
+ registrar.Add("test.pref.1", observer());
+ registrar.Add("test.pref.2", observer());
+ EXPECT_FALSE(registrar.IsEmpty());
+
+ // Test removing.
+ Mock::VerifyAndClearExpectations(service());
+ EXPECT_CALL(*service(),
+ RemovePrefObserver(Eq(std::string("test.pref.1")), &registrar));
+ EXPECT_CALL(*service(),
+ RemovePrefObserver(Eq(std::string("test.pref.2")), &registrar));
+ registrar.Remove("test.pref.1");
+ registrar.Remove("test.pref.2");
+ EXPECT_TRUE(registrar.IsEmpty());
+
+ // Explicitly check the expectations now to make sure that the Removes
+ // worked (rather than the registrar destructor doing the work).
+ Mock::VerifyAndClearExpectations(service());
+}
+
+TEST_F(PrefChangeRegistrarTest, AutoRemove) {
+ PrefChangeRegistrar registrar;
+ registrar.Init(service());
+
+ // Setup of auto-remove.
+ EXPECT_CALL(*service(),
+ AddPrefObserver(Eq(std::string("test.pref.1")), &registrar));
+ registrar.Add("test.pref.1", observer());
+ Mock::VerifyAndClearExpectations(service());
+ EXPECT_FALSE(registrar.IsEmpty());
+
+ // Test auto-removing.
+ EXPECT_CALL(*service(),
+ RemovePrefObserver(Eq(std::string("test.pref.1")), &registrar));
+}
+
+TEST_F(PrefChangeRegistrarTest, RemoveAll) {
+ PrefChangeRegistrar registrar;
+ registrar.Init(service());
+
+ EXPECT_CALL(*service(),
+ AddPrefObserver(Eq(std::string("test.pref.1")), &registrar));
+ EXPECT_CALL(*service(),
+ AddPrefObserver(Eq(std::string("test.pref.2")), &registrar));
+ registrar.Add("test.pref.1", observer());
+ registrar.Add("test.pref.2", observer());
+ Mock::VerifyAndClearExpectations(service());
+
+ EXPECT_CALL(*service(),
+ RemovePrefObserver(Eq(std::string("test.pref.1")), &registrar));
+ EXPECT_CALL(*service(),
+ RemovePrefObserver(Eq(std::string("test.pref.2")), &registrar));
+ registrar.RemoveAll();
+ EXPECT_TRUE(registrar.IsEmpty());
+
+ // Explicitly check the expectations now to make sure that the RemoveAll
+ // worked (rather than the registrar destructor doing the work).
+ Mock::VerifyAndClearExpectations(service());
+}
+
+class ObserveSetOfPreferencesTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ pref_service_.reset(new TestingPrefServiceSimple);
+ PrefRegistrySimple* registry = pref_service_->registry();
+ registry->RegisterStringPref(kHomePage, "http://google.com");
+ registry->RegisterBooleanPref(kHomePageIsNewTabPage, false);
+ registry->RegisterStringPref(kApplicationLocale, std::string());
+ }
+
+ PrefChangeRegistrar* CreatePrefChangeRegistrar() {
+ PrefChangeRegistrar* pref_set = new PrefChangeRegistrar();
+ base::Closure callback = base::Bind(&base::DoNothing);
+ pref_set->Init(pref_service_.get());
+ pref_set->Add(kHomePage, callback);
+ pref_set->Add(kHomePageIsNewTabPage, callback);
+ return pref_set;
+ }
+
+ MOCK_METHOD1(OnPreferenceChanged, void(const std::string&));
+
+ scoped_ptr<TestingPrefServiceSimple> pref_service_;
+};
+
+TEST_F(ObserveSetOfPreferencesTest, IsObserved) {
+ scoped_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar());
+ EXPECT_TRUE(pref_set->IsObserved(kHomePage));
+ EXPECT_TRUE(pref_set->IsObserved(kHomePageIsNewTabPage));
+ EXPECT_FALSE(pref_set->IsObserved(kApplicationLocale));
+}
+
+TEST_F(ObserveSetOfPreferencesTest, IsManaged) {
+ scoped_ptr<PrefChangeRegistrar> pref_set(CreatePrefChangeRegistrar());
+ EXPECT_FALSE(pref_set->IsManaged());
+ pref_service_->SetManagedPref(kHomePage,
+ new StringValue("http://crbug.com"));
+ EXPECT_TRUE(pref_set->IsManaged());
+ pref_service_->SetManagedPref(kHomePageIsNewTabPage,
+ new FundamentalValue(true));
+ EXPECT_TRUE(pref_set->IsManaged());
+ pref_service_->RemoveManagedPref(kHomePage);
+ EXPECT_TRUE(pref_set->IsManaged());
+ pref_service_->RemoveManagedPref(kHomePageIsNewTabPage);
+ EXPECT_FALSE(pref_set->IsManaged());
+}
+
+TEST_F(ObserveSetOfPreferencesTest, Observe) {
+ using testing::_;
+ using testing::Mock;
+
+ PrefChangeRegistrar pref_set;
+ PrefChangeRegistrar::NamedChangeCallback callback = base::Bind(
+ &ObserveSetOfPreferencesTest::OnPreferenceChanged,
+ base::Unretained(this));
+ pref_set.Init(pref_service_.get());
+ pref_set.Add(kHomePage, callback);
+ pref_set.Add(kHomePageIsNewTabPage, callback);
+
+ EXPECT_CALL(*this, OnPreferenceChanged(kHomePage));
+ pref_service_->SetUserPref(kHomePage, new StringValue("http://crbug.com"));
+ Mock::VerifyAndClearExpectations(this);
+
+ EXPECT_CALL(*this, OnPreferenceChanged(kHomePageIsNewTabPage));
+ pref_service_->SetUserPref(kHomePageIsNewTabPage,
+ new FundamentalValue(true));
+ Mock::VerifyAndClearExpectations(this);
+
+ EXPECT_CALL(*this, OnPreferenceChanged(_)).Times(0);
+ pref_service_->SetUserPref(kApplicationLocale, new StringValue("en_US.utf8"));
+ Mock::VerifyAndClearExpectations(this);
+}
+
+} // namespace base
diff --git a/chromium/components/prefs/pref_filter.h b/chromium/components/prefs/pref_filter.h
new file mode 100644
index 00000000000..8fa66273e67
--- /dev/null
+++ b/chromium/components/prefs/pref_filter.h
@@ -0,0 +1,55 @@
+// 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 COMPONENTS_PREFS_PREF_FILTER_H_
+#define COMPONENTS_PREFS_PREF_FILTER_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/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 COMPONENTS_PREFS_EXPORT PrefFilter {
+ public:
+ // A callback to be invoked when |prefs| have been read (and possibly
+ // pre-modified) and are now ready to be handed back to this callback's
+ // builder. |schedule_write| indicates whether a write should be immediately
+ // scheduled (typically because the |prefs| were pre-modified).
+ typedef base::Callback<void(scoped_ptr<base::DictionaryValue> prefs,
+ bool schedule_write)> PostFilterOnLoadCallback;
+
+ virtual ~PrefFilter() {}
+
+ // This method is given ownership of the |pref_store_contents| read from disk
+ // before the underlying PersistentPrefStore gets to use them. It must hand
+ // them back via |post_filter_on_load_callback|, but may modify them first.
+ // Note: This method is asynchronous, which may make calls like
+ // PersistentPrefStore::ReadPrefs() asynchronous. The owner of filtered
+ // PersistentPrefStores should handle this to make the reads look synchronous
+ // to external users (see SegregatedPrefStore::ReadPrefs() for an example).
+ virtual void FilterOnLoad(
+ const PostFilterOnLoadCallback& post_filter_on_load_callback,
+ scoped_ptr<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) = 0;
+
+ // Receives notification when the pref store is about to serialize data
+ // contained in |pref_store_contents| to a string. Modifications to
+ // |pref_store_contents| will be persisted to disk and also affect the
+ // in-memory state.
+ virtual void FilterSerializeData(
+ base::DictionaryValue* pref_store_contents) = 0;
+};
+
+#endif // COMPONENTS_PREFS_PREF_FILTER_H_
diff --git a/chromium/components/prefs/pref_member.cc b/chromium/components/prefs/pref_member.cc
new file mode 100644
index 00000000000..b848d722d1c
--- /dev/null
+++ b/chromium/components/prefs/pref_member.cc
@@ -0,0 +1,223 @@
+// Copyright (c) 2012 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 "components/prefs/pref_member.h"
+
+#include <utility>
+
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/value_conversions.h"
+#include "components/prefs/pref_service.h"
+
+using base::SingleThreadTaskRunner;
+
+namespace subtle {
+
+PrefMemberBase::PrefMemberBase()
+ : prefs_(NULL),
+ setting_value_(false) {
+}
+
+PrefMemberBase::~PrefMemberBase() {
+ Destroy();
+}
+
+void PrefMemberBase::Init(const std::string& pref_name,
+ PrefService* prefs,
+ const NamedChangeCallback& observer) {
+ observer_ = observer;
+ Init(pref_name, prefs);
+}
+
+void PrefMemberBase::Init(const std::string& pref_name, PrefService* prefs) {
+ DCHECK(prefs);
+ DCHECK(pref_name_.empty()); // Check that Init is only called once.
+ prefs_ = prefs;
+ pref_name_ = pref_name;
+ // Check that the preference is registered.
+ DCHECK(prefs_->FindPreference(pref_name_)) << pref_name << " not registered.";
+
+ // Add ourselves as a pref observer so we can keep our local value in sync.
+ prefs_->AddPrefObserver(pref_name, this);
+}
+
+void PrefMemberBase::Destroy() {
+ if (prefs_ && !pref_name_.empty()) {
+ prefs_->RemovePrefObserver(pref_name_, this);
+ prefs_ = NULL;
+ }
+}
+
+void PrefMemberBase::MoveToThread(
+ scoped_refptr<SingleThreadTaskRunner> task_runner) {
+ VerifyValuePrefName();
+ // Load the value from preferences if it hasn't been loaded so far.
+ if (!internal())
+ UpdateValueFromPref(base::Closure());
+ internal()->MoveToThread(std::move(task_runner));
+}
+
+void PrefMemberBase::OnPreferenceChanged(PrefService* service,
+ const std::string& pref_name) {
+ VerifyValuePrefName();
+ UpdateValueFromPref((!setting_value_ && !observer_.is_null()) ?
+ base::Bind(observer_, pref_name) : base::Closure());
+}
+
+void PrefMemberBase::UpdateValueFromPref(const base::Closure& callback) const {
+ VerifyValuePrefName();
+ const PrefService::Preference* pref = prefs_->FindPreference(pref_name_);
+ DCHECK(pref);
+ if (!internal())
+ CreateInternal();
+ internal()->UpdateValue(pref->GetValue()->DeepCopy(),
+ pref->IsManaged(),
+ pref->IsUserModifiable(),
+ callback);
+}
+
+void PrefMemberBase::VerifyPref() const {
+ VerifyValuePrefName();
+ if (!internal())
+ UpdateValueFromPref(base::Closure());
+}
+
+void PrefMemberBase::InvokeUnnamedCallback(const base::Closure& callback,
+ const std::string& pref_name) {
+ callback.Run();
+}
+
+PrefMemberBase::Internal::Internal()
+ : thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ is_managed_(false),
+ is_user_modifiable_(false) {
+}
+PrefMemberBase::Internal::~Internal() { }
+
+bool PrefMemberBase::Internal::IsOnCorrectThread() const {
+ return thread_task_runner_->BelongsToCurrentThread();
+}
+
+void PrefMemberBase::Internal::UpdateValue(
+ base::Value* v,
+ bool is_managed,
+ bool is_user_modifiable,
+ const base::Closure& callback) const {
+ scoped_ptr<base::Value> value(v);
+ base::ScopedClosureRunner closure_runner(callback);
+ if (IsOnCorrectThread()) {
+ bool rv = UpdateValueInternal(*value);
+ DCHECK(rv);
+ is_managed_ = is_managed;
+ is_user_modifiable_ = is_user_modifiable;
+ } else {
+ bool may_run = thread_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&PrefMemberBase::Internal::UpdateValue, this,
+ value.release(), is_managed, is_user_modifiable,
+ closure_runner.Release()));
+ DCHECK(may_run);
+ }
+}
+
+void PrefMemberBase::Internal::MoveToThread(
+ scoped_refptr<SingleThreadTaskRunner> task_runner) {
+ CheckOnCorrectThread();
+ thread_task_runner_ = std::move(task_runner);
+}
+
+bool PrefMemberVectorStringUpdate(const base::Value& value,
+ std::vector<std::string>* string_vector) {
+ if (!value.IsType(base::Value::TYPE_LIST))
+ return false;
+ const base::ListValue* list = static_cast<const base::ListValue*>(&value);
+
+ std::vector<std::string> local_vector;
+ for (base::ListValue::const_iterator it = list->begin();
+ it != list->end(); ++it) {
+ std::string string_value;
+ if (!(*it)->GetAsString(&string_value))
+ return false;
+
+ local_vector.push_back(string_value);
+ }
+
+ string_vector->swap(local_vector);
+ return true;
+}
+
+} // namespace subtle
+
+template <>
+void PrefMember<bool>::UpdatePref(const bool& value) {
+ prefs()->SetBoolean(pref_name(), value);
+}
+
+template <>
+bool PrefMember<bool>::Internal::UpdateValueInternal(
+ const base::Value& value) const {
+ return value.GetAsBoolean(&value_);
+}
+
+template <>
+void PrefMember<int>::UpdatePref(const int& value) {
+ prefs()->SetInteger(pref_name(), value);
+}
+
+template <>
+bool PrefMember<int>::Internal::UpdateValueInternal(
+ const base::Value& value) const {
+ return value.GetAsInteger(&value_);
+}
+
+template <>
+void PrefMember<double>::UpdatePref(const double& value) {
+ prefs()->SetDouble(pref_name(), value);
+}
+
+template <>
+bool PrefMember<double>::Internal::UpdateValueInternal(const base::Value& value)
+ const {
+ return value.GetAsDouble(&value_);
+}
+
+template <>
+void PrefMember<std::string>::UpdatePref(const std::string& value) {
+ prefs()->SetString(pref_name(), value);
+}
+
+template <>
+bool PrefMember<std::string>::Internal::UpdateValueInternal(
+ const base::Value& value)
+ const {
+ return value.GetAsString(&value_);
+}
+
+template <>
+void PrefMember<base::FilePath>::UpdatePref(const base::FilePath& value) {
+ prefs()->SetFilePath(pref_name(), value);
+}
+
+template <>
+bool PrefMember<base::FilePath>::Internal::UpdateValueInternal(
+ const base::Value& value)
+ const {
+ return base::GetValueAsFilePath(value, &value_);
+}
+
+template <>
+void PrefMember<std::vector<std::string> >::UpdatePref(
+ const std::vector<std::string>& value) {
+ base::ListValue list_value;
+ list_value.AppendStrings(value);
+ prefs()->Set(pref_name(), list_value);
+}
+
+template <>
+bool PrefMember<std::vector<std::string> >::Internal::UpdateValueInternal(
+ const base::Value& value) const {
+ return subtle::PrefMemberVectorStringUpdate(value, &value_);
+}
diff --git a/chromium/components/prefs/pref_member.h b/chromium/components/prefs/pref_member.h
new file mode 100644
index 00000000000..f46e368603c
--- /dev/null
+++ b/chromium/components/prefs/pref_member.h
@@ -0,0 +1,357 @@
+// Copyright (c) 2012 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.
+//
+// A helper class that stays in sync with a preference (bool, int, real,
+// string or filepath). For example:
+//
+// class MyClass {
+// public:
+// MyClass(PrefService* prefs) {
+// my_string_.Init(prefs::kHomePage, prefs);
+// }
+// private:
+// StringPrefMember my_string_;
+// };
+//
+// my_string_ should stay in sync with the prefs::kHomePage pref and will
+// update if either the pref changes or if my_string_.SetValue is called.
+//
+// An optional observer can be passed into the Init method which can be used to
+// notify MyClass of changes. Note that if you use SetValue(), the observer
+// will not be notified.
+
+#ifndef COMPONENTS_PREFS_PREF_MEMBER_H_
+#define COMPONENTS_PREFS_PREF_MEMBER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/single_thread_task_runner.h"
+#include "base/values.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/pref_observer.h"
+
+class PrefService;
+
+namespace subtle {
+
+class COMPONENTS_PREFS_EXPORT PrefMemberBase : public PrefObserver {
+ public:
+ // Type of callback you can register if you need to know the name of
+ // the pref that is changing.
+ typedef base::Callback<void(const std::string&)> NamedChangeCallback;
+
+ PrefService* prefs() { return prefs_; }
+ const PrefService* prefs() const { return prefs_; }
+
+ protected:
+ class COMPONENTS_PREFS_EXPORT Internal
+ : public base::RefCountedThreadSafe<Internal> {
+ public:
+ Internal();
+
+ // Update the value, either by calling |UpdateValueInternal| directly
+ // or by dispatching to the right thread.
+ // Takes ownership of |value|.
+ void UpdateValue(base::Value* value,
+ bool is_managed,
+ bool is_user_modifiable,
+ const base::Closure& callback) const;
+
+ void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+ // See PrefMember<> for description.
+ bool IsManaged() const {
+ return is_managed_;
+ }
+
+ bool IsUserModifiable() const {
+ return is_user_modifiable_;
+ }
+
+ protected:
+ friend class base::RefCountedThreadSafe<Internal>;
+ virtual ~Internal();
+
+ void CheckOnCorrectThread() const {
+ DCHECK(IsOnCorrectThread());
+ }
+
+ private:
+ // This method actually updates the value. It should only be called from
+ // the thread the PrefMember is on.
+ virtual bool UpdateValueInternal(const base::Value& value) const = 0;
+
+ bool IsOnCorrectThread() const;
+
+ scoped_refptr<base::SingleThreadTaskRunner> thread_task_runner_;
+ mutable bool is_managed_;
+ mutable bool is_user_modifiable_;
+
+ DISALLOW_COPY_AND_ASSIGN(Internal);
+ };
+
+ PrefMemberBase();
+ virtual ~PrefMemberBase();
+
+ // See PrefMember<> for description.
+ void Init(const std::string& pref_name,
+ PrefService* prefs,
+ const NamedChangeCallback& observer);
+ void Init(const std::string& pref_name, PrefService* prefs);
+
+ virtual void CreateInternal() const = 0;
+
+ // See PrefMember<> for description.
+ void Destroy();
+
+ void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner);
+
+ // PrefObserver
+ void OnPreferenceChanged(PrefService* service,
+ const std::string& pref_name) override;
+
+ void VerifyValuePrefName() const {
+ DCHECK(!pref_name_.empty());
+ }
+
+ // This method is used to do the actual sync with the preference.
+ // Note: it is logically const, because it doesn't modify the state
+ // seen by the outside world. It is just doing a lazy load behind the scenes.
+ void UpdateValueFromPref(const base::Closure& callback) const;
+
+ // Verifies the preference name, and lazily loads the preference value if
+ // it hasn't been loaded yet.
+ void VerifyPref() const;
+
+ const std::string& pref_name() const { return pref_name_; }
+
+ virtual Internal* internal() const = 0;
+
+ // Used to allow registering plain base::Closure callbacks.
+ static void InvokeUnnamedCallback(const base::Closure& callback,
+ const std::string& pref_name);
+
+ private:
+ // Ordered the members to compact the class instance.
+ std::string pref_name_;
+ NamedChangeCallback observer_;
+ PrefService* prefs_;
+
+ protected:
+ bool setting_value_;
+};
+
+// This function implements StringListPrefMember::UpdateValue().
+// It is exposed here for testing purposes.
+bool COMPONENTS_PREFS_EXPORT PrefMemberVectorStringUpdate(
+ const base::Value& value,
+ std::vector<std::string>* string_vector);
+
+} // namespace subtle
+
+template <typename ValueType>
+class PrefMember : public subtle::PrefMemberBase {
+ public:
+ // Defer initialization to an Init method so it's easy to make this class be
+ // a member variable.
+ PrefMember() {}
+ virtual ~PrefMember() {}
+
+ // Do the actual initialization of the class. Use the two-parameter
+ // version if you don't want any notifications of changes. This
+ // method should only be called on the UI thread.
+ void Init(const std::string& pref_name,
+ PrefService* prefs,
+ const NamedChangeCallback& observer) {
+ subtle::PrefMemberBase::Init(pref_name, prefs, observer);
+ }
+ void Init(const std::string& pref_name,
+ PrefService* prefs,
+ const base::Closure& observer) {
+ subtle::PrefMemberBase::Init(
+ pref_name, prefs,
+ base::Bind(&PrefMemberBase::InvokeUnnamedCallback, observer));
+ }
+ void Init(const std::string& pref_name, PrefService* prefs) {
+ subtle::PrefMemberBase::Init(pref_name, prefs);
+ }
+
+ // Unsubscribes the PrefMember from the PrefService. After calling this
+ // function, the PrefMember may not be used any more on the UI thread.
+ // Assuming |MoveToThread| was previously called, |GetValue|, |IsManaged|,
+ // and |IsUserModifiable| can still be called from the other thread but
+ // the results will no longer update from the PrefService.
+ // This method should only be called on the UI thread.
+ void Destroy() {
+ subtle::PrefMemberBase::Destroy();
+ }
+
+ // Moves the PrefMember to another thread, allowing read accesses from there.
+ // Changes from the PrefService will be propagated asynchronously
+ // via PostTask.
+ // This method should only be used from the thread the PrefMember is currently
+ // on, which is the UI thread by default.
+ void MoveToThread(scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
+ subtle::PrefMemberBase::MoveToThread(task_runner);
+ }
+
+ // Check whether the pref is managed, i.e. controlled externally through
+ // enterprise configuration management (e.g. windows group policy). Returns
+ // false for unknown prefs.
+ // This method should only be used from the thread the PrefMember is currently
+ // on, which is the UI thread unless changed by |MoveToThread|.
+ bool IsManaged() const {
+ VerifyPref();
+ return internal_->IsManaged();
+ }
+
+ // Checks whether the pref can be modified by the user. This returns false
+ // when the pref is managed by a policy or an extension, and when a command
+ // line flag overrides the pref.
+ // This method should only be used from the thread the PrefMember is currently
+ // on, which is the UI thread unless changed by |MoveToThread|.
+ bool IsUserModifiable() const {
+ VerifyPref();
+ return internal_->IsUserModifiable();
+ }
+
+ // Retrieve the value of the member variable.
+ // This method should only be used from the thread the PrefMember is currently
+ // on, which is the UI thread unless changed by |MoveToThread|.
+ ValueType GetValue() const {
+ VerifyPref();
+ return internal_->value();
+ }
+
+ // Provided as a convenience.
+ ValueType operator*() const {
+ return GetValue();
+ }
+
+ // Set the value of the member variable.
+ // This method should only be called on the UI thread.
+ void SetValue(const ValueType& value) {
+ VerifyValuePrefName();
+ setting_value_ = true;
+ UpdatePref(value);
+ setting_value_ = false;
+ }
+
+ // Returns the pref name.
+ const std::string& GetPrefName() const {
+ return pref_name();
+ }
+
+ private:
+ class Internal : public subtle::PrefMemberBase::Internal {
+ public:
+ Internal() : value_(ValueType()) {}
+
+ ValueType value() {
+ CheckOnCorrectThread();
+ return value_;
+ }
+
+ protected:
+ ~Internal() override {}
+
+ COMPONENTS_PREFS_EXPORT bool UpdateValueInternal(
+ const base::Value& value) const override;
+
+ // We cache the value of the pref so we don't have to keep walking the pref
+ // tree.
+ mutable ValueType value_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Internal);
+ };
+
+ Internal* internal() const override { return internal_.get(); }
+ void CreateInternal() const override { internal_ = new Internal(); }
+
+ // This method is used to do the actual sync with pref of the specified type.
+ void COMPONENTS_PREFS_EXPORT UpdatePref(const ValueType& value);
+
+ mutable scoped_refptr<Internal> internal_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefMember);
+};
+
+// Declaration of template specialization need to be repeated here
+// specifically for each specialization (rather than just once above)
+// or at least one of our compilers won't be happy in all cases.
+// Specifically, it was failing on ChromeOS with a complaint about
+// PrefMember<FilePath>::UpdateValueInternal not being defined when
+// built in a chroot with the following parameters:
+//
+// FEATURES="noclean nostrip" USE="-chrome_debug -chrome_remoting
+// -chrome_internal -chrome_pdf component_build"
+// ~/trunk/goma/goma-wrapper cros_chrome_make --board=${BOARD}
+// --install --runhooks
+
+template <>
+COMPONENTS_PREFS_EXPORT void PrefMember<bool>::UpdatePref(const bool& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool PrefMember<bool>::Internal::UpdateValueInternal(
+ const base::Value& value) const;
+
+template <>
+COMPONENTS_PREFS_EXPORT void PrefMember<int>::UpdatePref(const int& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool PrefMember<int>::Internal::UpdateValueInternal(
+ const base::Value& value) const;
+
+template <>
+COMPONENTS_PREFS_EXPORT void
+PrefMember<double>::UpdatePref(const double& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool PrefMember<double>::Internal::UpdateValueInternal(
+ const base::Value& value) const;
+
+template <>
+COMPONENTS_PREFS_EXPORT void PrefMember<std::string>::UpdatePref(
+ const std::string& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool
+PrefMember<std::string>::Internal::UpdateValueInternal(
+ const base::Value& value) const;
+
+template <>
+COMPONENTS_PREFS_EXPORT void PrefMember<base::FilePath>::UpdatePref(
+ const base::FilePath& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool
+PrefMember<base::FilePath>::Internal::UpdateValueInternal(
+ const base::Value& value) const;
+
+template <>
+COMPONENTS_PREFS_EXPORT void PrefMember<std::vector<std::string>>::UpdatePref(
+ const std::vector<std::string>& value);
+
+template <>
+COMPONENTS_PREFS_EXPORT bool
+PrefMember<std::vector<std::string>>::Internal::UpdateValueInternal(
+ const base::Value& value) const;
+
+typedef PrefMember<bool> BooleanPrefMember;
+typedef PrefMember<int> IntegerPrefMember;
+typedef PrefMember<double> DoublePrefMember;
+typedef PrefMember<std::string> StringPrefMember;
+typedef PrefMember<base::FilePath> FilePathPrefMember;
+// This preference member is expensive for large string arrays.
+typedef PrefMember<std::vector<std::string>> StringListPrefMember;
+
+#endif // COMPONENTS_PREFS_PREF_MEMBER_H_
diff --git a/chromium/components/prefs/pref_member_unittest.cc b/chromium/components/prefs/pref_member_unittest.cc
new file mode 100644
index 00000000000..73b6ed00c86
--- /dev/null
+++ b/chromium/components/prefs/pref_member_unittest.cc
@@ -0,0 +1,325 @@
+// Copyright (c) 2011 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 "components/prefs/pref_member.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kBoolPref[] = "bool";
+const char kIntPref[] = "int";
+const char kDoublePref[] = "double";
+const char kStringPref[] = "string";
+const char kStringListPref[] = "string_list";
+
+void RegisterTestPrefs(PrefRegistrySimple* registry) {
+ registry->RegisterBooleanPref(kBoolPref, false);
+ registry->RegisterIntegerPref(kIntPref, 0);
+ registry->RegisterDoublePref(kDoublePref, 0.0);
+ registry->RegisterStringPref(kStringPref, "default");
+ registry->RegisterListPref(kStringListPref, new base::ListValue());
+}
+
+class GetPrefValueHelper
+ : public base::RefCountedThreadSafe<GetPrefValueHelper> {
+ public:
+ GetPrefValueHelper() : value_(false), pref_thread_("pref thread") {
+ pref_thread_.Start();
+ }
+
+ void Init(const std::string& pref_name, PrefService* prefs) {
+ pref_.Init(pref_name, prefs);
+ pref_.MoveToThread(pref_thread_.task_runner());
+ }
+
+ void Destroy() {
+ pref_.Destroy();
+ }
+
+ void FetchValue() {
+ base::WaitableEvent event(true, false);
+ ASSERT_TRUE(pref_thread_.task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&GetPrefValueHelper::GetPrefValue, this, &event)));
+ event.Wait();
+ }
+
+ // The thread must be stopped on the main thread. GetPrefValueHelper being
+ // ref-counted, the destructor can be called from any thread.
+ void StopThread() {
+ pref_thread_.Stop();
+ }
+
+ bool value() { return value_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<GetPrefValueHelper>;
+ ~GetPrefValueHelper() {}
+
+ void GetPrefValue(base::WaitableEvent* event) {
+ value_ = pref_.GetValue();
+ event->Signal();
+ }
+
+ BooleanPrefMember pref_;
+ bool value_;
+
+ base::Thread pref_thread_; // The thread |pref_| runs on.
+};
+
+class PrefMemberTestClass {
+ public:
+ explicit PrefMemberTestClass(PrefService* prefs)
+ : observe_cnt_(0), prefs_(prefs) {
+ str_.Init(kStringPref, prefs,
+ base::Bind(&PrefMemberTestClass::OnPreferenceChanged,
+ base::Unretained(this)));
+ }
+
+ void OnPreferenceChanged(const std::string& pref_name) {
+ EXPECT_EQ(pref_name, kStringPref);
+ EXPECT_EQ(str_.GetValue(), prefs_->GetString(kStringPref));
+ ++observe_cnt_;
+ }
+
+ StringPrefMember str_;
+ int observe_cnt_;
+
+ private:
+ PrefService* prefs_;
+};
+
+} // anonymous namespace
+
+class PrefMemberTest : public testing::Test {
+ base::MessageLoop message_loop_;
+};
+
+TEST_F(PrefMemberTest, BasicGetAndSet) {
+ TestingPrefServiceSimple prefs;
+ RegisterTestPrefs(prefs.registry());
+
+ // Test bool
+ BooleanPrefMember boolean;
+ boolean.Init(kBoolPref, &prefs);
+
+ // Check the defaults
+ EXPECT_FALSE(prefs.GetBoolean(kBoolPref));
+ EXPECT_FALSE(boolean.GetValue());
+ EXPECT_FALSE(*boolean);
+
+ // Try changing through the member variable.
+ boolean.SetValue(true);
+ EXPECT_TRUE(boolean.GetValue());
+ EXPECT_TRUE(prefs.GetBoolean(kBoolPref));
+ EXPECT_TRUE(*boolean);
+
+ // Try changing back through the pref.
+ prefs.SetBoolean(kBoolPref, false);
+ EXPECT_FALSE(prefs.GetBoolean(kBoolPref));
+ EXPECT_FALSE(boolean.GetValue());
+ EXPECT_FALSE(*boolean);
+
+ // Test int
+ IntegerPrefMember integer;
+ integer.Init(kIntPref, &prefs);
+
+ // Check the defaults
+ EXPECT_EQ(0, prefs.GetInteger(kIntPref));
+ EXPECT_EQ(0, integer.GetValue());
+ EXPECT_EQ(0, *integer);
+
+ // Try changing through the member variable.
+ integer.SetValue(5);
+ EXPECT_EQ(5, integer.GetValue());
+ EXPECT_EQ(5, prefs.GetInteger(kIntPref));
+ EXPECT_EQ(5, *integer);
+
+ // Try changing back through the pref.
+ prefs.SetInteger(kIntPref, 2);
+ EXPECT_EQ(2, prefs.GetInteger(kIntPref));
+ EXPECT_EQ(2, integer.GetValue());
+ EXPECT_EQ(2, *integer);
+
+ // Test double
+ DoublePrefMember double_member;
+ double_member.Init(kDoublePref, &prefs);
+
+ // Check the defaults
+ EXPECT_EQ(0.0, prefs.GetDouble(kDoublePref));
+ EXPECT_EQ(0.0, double_member.GetValue());
+ EXPECT_EQ(0.0, *double_member);
+
+ // Try changing through the member variable.
+ double_member.SetValue(1.0);
+ EXPECT_EQ(1.0, double_member.GetValue());
+ EXPECT_EQ(1.0, prefs.GetDouble(kDoublePref));
+ EXPECT_EQ(1.0, *double_member);
+
+ // Try changing back through the pref.
+ prefs.SetDouble(kDoublePref, 3.0);
+ EXPECT_EQ(3.0, prefs.GetDouble(kDoublePref));
+ EXPECT_EQ(3.0, double_member.GetValue());
+ EXPECT_EQ(3.0, *double_member);
+
+ // Test string
+ StringPrefMember string;
+ string.Init(kStringPref, &prefs);
+
+ // Check the defaults
+ EXPECT_EQ("default", prefs.GetString(kStringPref));
+ EXPECT_EQ("default", string.GetValue());
+ EXPECT_EQ("default", *string);
+
+ // Try changing through the member variable.
+ string.SetValue("foo");
+ EXPECT_EQ("foo", string.GetValue());
+ EXPECT_EQ("foo", prefs.GetString(kStringPref));
+ EXPECT_EQ("foo", *string);
+
+ // Try changing back through the pref.
+ prefs.SetString(kStringPref, "bar");
+ EXPECT_EQ("bar", prefs.GetString(kStringPref));
+ EXPECT_EQ("bar", string.GetValue());
+ EXPECT_EQ("bar", *string);
+
+ // Test string list
+ base::ListValue expected_list;
+ std::vector<std::string> expected_vector;
+ StringListPrefMember string_list;
+ string_list.Init(kStringListPref, &prefs);
+
+ // Check the defaults
+ EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+ EXPECT_EQ(expected_vector, string_list.GetValue());
+ EXPECT_EQ(expected_vector, *string_list);
+
+ // Try changing through the pref member.
+ expected_list.AppendString("foo");
+ expected_vector.push_back("foo");
+ string_list.SetValue(expected_vector);
+
+ EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+ EXPECT_EQ(expected_vector, string_list.GetValue());
+ EXPECT_EQ(expected_vector, *string_list);
+
+ // Try adding through the pref.
+ expected_list.AppendString("bar");
+ expected_vector.push_back("bar");
+ prefs.Set(kStringListPref, expected_list);
+
+ EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+ EXPECT_EQ(expected_vector, string_list.GetValue());
+ EXPECT_EQ(expected_vector, *string_list);
+
+ // Try removing through the pref.
+ expected_list.Remove(0, NULL);
+ expected_vector.erase(expected_vector.begin());
+ prefs.Set(kStringListPref, expected_list);
+
+ EXPECT_TRUE(expected_list.Equals(prefs.GetList(kStringListPref)));
+ EXPECT_EQ(expected_vector, string_list.GetValue());
+ EXPECT_EQ(expected_vector, *string_list);
+}
+
+TEST_F(PrefMemberTest, InvalidList) {
+ // Set the vector to an initial good value.
+ std::vector<std::string> expected_vector;
+ expected_vector.push_back("foo");
+
+ // Try to add a valid list first.
+ base::ListValue list;
+ list.AppendString("foo");
+ std::vector<std::string> vector;
+ EXPECT_TRUE(subtle::PrefMemberVectorStringUpdate(list, &vector));
+ EXPECT_EQ(expected_vector, vector);
+
+ // Now try to add an invalid list. |vector| should not be changed.
+ list.AppendInteger(0);
+ EXPECT_FALSE(subtle::PrefMemberVectorStringUpdate(list, &vector));
+ EXPECT_EQ(expected_vector, vector);
+}
+
+TEST_F(PrefMemberTest, TwoPrefs) {
+ // Make sure two DoublePrefMembers stay in sync.
+ TestingPrefServiceSimple prefs;
+ RegisterTestPrefs(prefs.registry());
+
+ DoublePrefMember pref1;
+ pref1.Init(kDoublePref, &prefs);
+ DoublePrefMember pref2;
+ pref2.Init(kDoublePref, &prefs);
+
+ pref1.SetValue(2.3);
+ EXPECT_EQ(2.3, *pref2);
+
+ pref2.SetValue(3.5);
+ EXPECT_EQ(3.5, *pref1);
+
+ prefs.SetDouble(kDoublePref, 4.2);
+ EXPECT_EQ(4.2, *pref1);
+ EXPECT_EQ(4.2, *pref2);
+}
+
+TEST_F(PrefMemberTest, Observer) {
+ TestingPrefServiceSimple prefs;
+ RegisterTestPrefs(prefs.registry());
+
+ PrefMemberTestClass test_obj(&prefs);
+ EXPECT_EQ("default", *test_obj.str_);
+
+ // Calling SetValue should not fire the observer.
+ test_obj.str_.SetValue("hello");
+ EXPECT_EQ(0, test_obj.observe_cnt_);
+ EXPECT_EQ("hello", prefs.GetString(kStringPref));
+
+ // Changing the pref does fire the observer.
+ prefs.SetString(kStringPref, "world");
+ EXPECT_EQ(1, test_obj.observe_cnt_);
+ EXPECT_EQ("world", *(test_obj.str_));
+
+ // Not changing the value should not fire the observer.
+ prefs.SetString(kStringPref, "world");
+ EXPECT_EQ(1, test_obj.observe_cnt_);
+ EXPECT_EQ("world", *(test_obj.str_));
+
+ prefs.SetString(kStringPref, "hello");
+ EXPECT_EQ(2, test_obj.observe_cnt_);
+ EXPECT_EQ("hello", prefs.GetString(kStringPref));
+}
+
+TEST_F(PrefMemberTest, NoInit) {
+ // Make sure not calling Init on a PrefMember doesn't cause problems.
+ IntegerPrefMember pref;
+}
+
+TEST_F(PrefMemberTest, MoveToThread) {
+ TestingPrefServiceSimple prefs;
+ scoped_refptr<GetPrefValueHelper> helper(new GetPrefValueHelper());
+ RegisterTestPrefs(prefs.registry());
+ helper->Init(kBoolPref, &prefs);
+
+ helper->FetchValue();
+ EXPECT_FALSE(helper->value());
+
+ prefs.SetBoolean(kBoolPref, true);
+
+ helper->FetchValue();
+ EXPECT_TRUE(helper->value());
+
+ helper->Destroy();
+
+ helper->FetchValue();
+ EXPECT_TRUE(helper->value());
+
+ helper->StopThread();
+}
diff --git a/chromium/components/prefs/pref_notifier.h b/chromium/components/prefs/pref_notifier.h
new file mode 100644
index 00000000000..2abc213cffa
--- /dev/null
+++ b/chromium/components/prefs/pref_notifier.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 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 COMPONENTS_PREFS_PREF_NOTIFIER_H_
+#define COMPONENTS_PREFS_PREF_NOTIFIER_H_
+
+#include <string>
+
+// Delegate interface used by PrefValueStore to notify its owner about changes
+// to the preference values.
+// TODO(mnissler, danno): Move this declaration to pref_value_store.h once we've
+// cleaned up all public uses of this interface.
+class PrefNotifier {
+ public:
+ virtual ~PrefNotifier() {}
+
+ // Sends out a change notification for the preference identified by
+ // |pref_name|.
+ virtual void OnPreferenceChanged(const std::string& pref_name) = 0;
+
+ // Broadcasts the intialization completed notification.
+ virtual void OnInitializationCompleted(bool succeeded) = 0;
+};
+
+#endif // COMPONENTS_PREFS_PREF_NOTIFIER_H_
diff --git a/chromium/components/prefs/pref_notifier_impl.cc b/chromium/components/prefs/pref_notifier_impl.cc
new file mode 100644
index 00000000000..b3d566003c6
--- /dev/null
+++ b/chromium/components/prefs/pref_notifier_impl.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2012 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 "components/prefs/pref_notifier_impl.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "components/prefs/pref_service.h"
+
+PrefNotifierImpl::PrefNotifierImpl()
+ : pref_service_(NULL) {
+}
+
+PrefNotifierImpl::PrefNotifierImpl(PrefService* service)
+ : pref_service_(service) {
+}
+
+PrefNotifierImpl::~PrefNotifierImpl() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Verify that there are no pref observers when we shut down.
+ for (PrefObserverMap::iterator it = pref_observers_.begin();
+ it != pref_observers_.end(); ++it) {
+ PrefObserverList::Iterator obs_iterator(it->second);
+ if (obs_iterator.GetNext()) {
+ LOG(WARNING) << "pref observer found at shutdown " << it->first;
+ }
+ }
+
+ // Same for initialization observers.
+ if (!init_observers_.empty())
+ LOG(WARNING) << "Init observer found at shutdown.";
+
+ STLDeleteContainerPairSecondPointers(pref_observers_.begin(),
+ pref_observers_.end());
+ pref_observers_.clear();
+ init_observers_.clear();
+}
+
+void PrefNotifierImpl::AddPrefObserver(const std::string& path,
+ PrefObserver* obs) {
+ // Get the pref observer list associated with the path.
+ PrefObserverList* observer_list = NULL;
+ const PrefObserverMap::iterator observer_iterator =
+ pref_observers_.find(path);
+ if (observer_iterator == pref_observers_.end()) {
+ observer_list = new PrefObserverList;
+ pref_observers_[path] = observer_list;
+ } else {
+ observer_list = observer_iterator->second;
+ }
+
+ // Add the pref observer. ObserverList will DCHECK if it already is
+ // in the list.
+ observer_list->AddObserver(obs);
+}
+
+void PrefNotifierImpl::RemovePrefObserver(const std::string& path,
+ PrefObserver* obs) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ const PrefObserverMap::iterator observer_iterator =
+ pref_observers_.find(path);
+ if (observer_iterator == pref_observers_.end()) {
+ return;
+ }
+
+ PrefObserverList* observer_list = observer_iterator->second;
+ observer_list->RemoveObserver(obs);
+}
+
+void PrefNotifierImpl::AddInitObserver(base::Callback<void(bool)> obs) {
+ init_observers_.push_back(obs);
+}
+
+void PrefNotifierImpl::OnPreferenceChanged(const std::string& path) {
+ FireObservers(path);
+}
+
+void PrefNotifierImpl::OnInitializationCompleted(bool succeeded) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // We must make a copy of init_observers_ and clear it before we run
+ // observers, or we can end up in this method re-entrantly before
+ // clearing the observers list.
+ PrefInitObserverList observers(init_observers_);
+ init_observers_.clear();
+
+ for (PrefInitObserverList::iterator it = observers.begin();
+ it != observers.end();
+ ++it) {
+ it->Run(succeeded);
+ }
+}
+
+void PrefNotifierImpl::FireObservers(const std::string& path) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // Only send notifications for registered preferences.
+ if (!pref_service_->FindPreference(path))
+ return;
+
+ const PrefObserverMap::iterator observer_iterator =
+ pref_observers_.find(path);
+ if (observer_iterator == pref_observers_.end())
+ return;
+
+ FOR_EACH_OBSERVER(PrefObserver,
+ *(observer_iterator->second),
+ OnPreferenceChanged(pref_service_, path));
+}
+
+void PrefNotifierImpl::SetPrefService(PrefService* pref_service) {
+ DCHECK(pref_service_ == NULL);
+ pref_service_ = pref_service;
+}
diff --git a/chromium/components/prefs/pref_notifier_impl.h b/chromium/components/prefs/pref_notifier_impl.h
new file mode 100644
index 00000000000..e0da264ac3a
--- /dev/null
+++ b/chromium/components/prefs/pref_notifier_impl.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2011 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 COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_
+#define COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_
+
+#include <list>
+#include <string>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/threading/thread_checker.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/pref_notifier.h"
+#include "components/prefs/pref_observer.h"
+
+class PrefService;
+
+// The PrefNotifier implementation used by the PrefService.
+class COMPONENTS_PREFS_EXPORT PrefNotifierImpl
+ : public NON_EXPORTED_BASE(PrefNotifier) {
+ public:
+ PrefNotifierImpl();
+ explicit PrefNotifierImpl(PrefService* pref_service);
+ ~PrefNotifierImpl() override;
+
+ // If the pref at the given path changes, we call the observer's
+ // OnPreferenceChanged method.
+ void AddPrefObserver(const std::string& path, PrefObserver* observer);
+ void RemovePrefObserver(const std::string& path, PrefObserver* observer);
+
+ // We run the callback once, when initialization completes. The bool
+ // parameter will be set to true for successful initialization,
+ // false for unsuccessful.
+ void AddInitObserver(base::Callback<void(bool)> observer);
+
+ void SetPrefService(PrefService* pref_service);
+
+ protected:
+ // PrefNotifier overrides.
+ void OnPreferenceChanged(const std::string& pref_name) override;
+ void OnInitializationCompleted(bool succeeded) override;
+
+ // A map from pref names to a list of observers. Observers get fired in the
+ // order they are added. These should only be accessed externally for unit
+ // testing.
+ typedef base::ObserverList<PrefObserver> PrefObserverList;
+ typedef base::hash_map<std::string, PrefObserverList*> PrefObserverMap;
+
+ typedef std::list<base::Callback<void(bool)>> PrefInitObserverList;
+
+ const PrefObserverMap* pref_observers() const { return &pref_observers_; }
+
+ private:
+ // For the given pref_name, fire any observer of the pref. Virtual so it can
+ // be mocked for unit testing.
+ virtual void FireObservers(const std::string& path);
+
+ // Weak reference; the notifier is owned by the PrefService.
+ PrefService* pref_service_;
+
+ PrefObserverMap pref_observers_;
+ PrefInitObserverList init_observers_;
+
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefNotifierImpl);
+};
+
+#endif // COMPONENTS_PREFS_PREF_NOTIFIER_IMPL_H_
diff --git a/chromium/components/prefs/pref_notifier_impl_unittest.cc b/chromium/components/prefs/pref_notifier_impl_unittest.cc
new file mode 100644
index 00000000000..a3f12349b9f
--- /dev/null
+++ b/chromium/components/prefs/pref_notifier_impl_unittest.cc
@@ -0,0 +1,222 @@
+// Copyright (c) 2011 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 <stddef.h>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "components/prefs/mock_pref_change_callback.h"
+#include "components/prefs/pref_notifier_impl.h"
+#include "components/prefs/pref_observer.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/pref_value_store.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Field;
+using testing::Invoke;
+using testing::Mock;
+using testing::Truly;
+
+namespace {
+
+const char kChangedPref[] = "changed_pref";
+const char kUnchangedPref[] = "unchanged_pref";
+
+class MockPrefInitObserver {
+ public:
+ MOCK_METHOD1(OnInitializationCompleted, void(bool));
+};
+
+// This is an unmodified PrefNotifierImpl, except we make
+// OnPreferenceChanged public for tests.
+class TestingPrefNotifierImpl : public PrefNotifierImpl {
+ public:
+ explicit TestingPrefNotifierImpl(PrefService* service)
+ : PrefNotifierImpl(service) {
+ }
+
+ // Make public for tests.
+ using PrefNotifierImpl::OnPreferenceChanged;
+};
+
+// Mock PrefNotifier that allows tracking of observers and notifications.
+class MockPrefNotifier : public PrefNotifierImpl {
+ public:
+ explicit MockPrefNotifier(PrefService* pref_service)
+ : PrefNotifierImpl(pref_service) {}
+ virtual ~MockPrefNotifier() {}
+
+ MOCK_METHOD1(FireObservers, void(const std::string& path));
+
+ size_t CountObserver(const std::string& path, PrefObserver* obs) {
+ PrefObserverMap::const_iterator observer_iterator =
+ pref_observers()->find(path);
+ if (observer_iterator == pref_observers()->end())
+ return false;
+
+ PrefObserverList* observer_list = observer_iterator->second;
+ PrefObserverList::Iterator it(observer_list);
+ PrefObserver* existing_obs;
+ size_t count = 0;
+ while ((existing_obs = it.GetNext()) != NULL) {
+ if (existing_obs == obs)
+ count++;
+ }
+
+ return count;
+ }
+
+ // Make public for tests below.
+ using PrefNotifierImpl::OnPreferenceChanged;
+ using PrefNotifierImpl::OnInitializationCompleted;
+};
+
+class PrefObserverMock : public PrefObserver {
+ public:
+ PrefObserverMock() {}
+ virtual ~PrefObserverMock() {}
+
+ MOCK_METHOD2(OnPreferenceChanged, void(PrefService*, const std::string&));
+};
+
+// Test fixture class.
+class PrefNotifierTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ pref_service_.registry()->RegisterBooleanPref(kChangedPref, true);
+ pref_service_.registry()->RegisterBooleanPref(kUnchangedPref, true);
+ }
+
+ TestingPrefServiceSimple pref_service_;
+
+ PrefObserverMock obs1_;
+ PrefObserverMock obs2_;
+};
+
+TEST_F(PrefNotifierTest, OnPreferenceChanged) {
+ MockPrefNotifier notifier(&pref_service_);
+ EXPECT_CALL(notifier, FireObservers(kChangedPref)).Times(1);
+ notifier.OnPreferenceChanged(kChangedPref);
+}
+
+TEST_F(PrefNotifierTest, OnInitializationCompleted) {
+ MockPrefNotifier notifier(&pref_service_);
+ MockPrefInitObserver observer;
+ notifier.AddInitObserver(
+ base::Bind(&MockPrefInitObserver::OnInitializationCompleted,
+ base::Unretained(&observer)));
+ EXPECT_CALL(observer, OnInitializationCompleted(true));
+ notifier.OnInitializationCompleted(true);
+}
+
+TEST_F(PrefNotifierTest, AddAndRemovePrefObservers) {
+ const char pref_name[] = "homepage";
+ const char pref_name2[] = "proxy";
+
+ MockPrefNotifier notifier(&pref_service_);
+ notifier.AddPrefObserver(pref_name, &obs1_);
+ ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+ // Re-adding the same observer for the same pref doesn't change anything.
+ // Skip this in debug mode, since it hits a DCHECK and death tests aren't
+ // thread-safe.
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+ notifier.AddPrefObserver(pref_name, &obs1_);
+ ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+#endif
+
+ // Ensure that we can add the same observer to a different pref.
+ notifier.AddPrefObserver(pref_name2, &obs1_);
+ ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
+ ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+ // Ensure that we can add another observer to the same pref.
+ notifier.AddPrefObserver(pref_name, &obs2_);
+ ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs1_));
+ ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
+ ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs2_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+ // Ensure that we can remove all observers, and that removing a non-existent
+ // observer is harmless.
+ notifier.RemovePrefObserver(pref_name, &obs1_);
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
+ ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
+ ASSERT_EQ(1u, notifier.CountObserver(pref_name, &obs2_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+ notifier.RemovePrefObserver(pref_name, &obs2_);
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
+ ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+ notifier.RemovePrefObserver(pref_name, &obs1_);
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
+ ASSERT_EQ(1u, notifier.CountObserver(pref_name2, &obs1_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+
+ notifier.RemovePrefObserver(pref_name2, &obs1_);
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs1_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs1_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name, &obs2_));
+ ASSERT_EQ(0u, notifier.CountObserver(pref_name2, &obs2_));
+}
+
+TEST_F(PrefNotifierTest, FireObservers) {
+ TestingPrefNotifierImpl notifier(&pref_service_);
+ notifier.AddPrefObserver(kChangedPref, &obs1_);
+ notifier.AddPrefObserver(kUnchangedPref, &obs1_);
+
+ EXPECT_CALL(obs1_, OnPreferenceChanged(&pref_service_, kChangedPref));
+ EXPECT_CALL(obs2_, OnPreferenceChanged(_, _)).Times(0);
+ notifier.OnPreferenceChanged(kChangedPref);
+ Mock::VerifyAndClearExpectations(&obs1_);
+ Mock::VerifyAndClearExpectations(&obs2_);
+
+ notifier.AddPrefObserver(kChangedPref, &obs2_);
+ notifier.AddPrefObserver(kUnchangedPref, &obs2_);
+
+ EXPECT_CALL(obs1_, OnPreferenceChanged(&pref_service_, kChangedPref));
+ EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref));
+ notifier.OnPreferenceChanged(kChangedPref);
+ Mock::VerifyAndClearExpectations(&obs1_);
+ Mock::VerifyAndClearExpectations(&obs2_);
+
+ // Make sure removing an observer from one pref doesn't affect anything else.
+ notifier.RemovePrefObserver(kChangedPref, &obs1_);
+
+ EXPECT_CALL(obs1_, OnPreferenceChanged(_, _)).Times(0);
+ EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref));
+ notifier.OnPreferenceChanged(kChangedPref);
+ Mock::VerifyAndClearExpectations(&obs1_);
+ Mock::VerifyAndClearExpectations(&obs2_);
+
+ // Make sure removing an observer entirely doesn't affect anything else.
+ notifier.RemovePrefObserver(kUnchangedPref, &obs1_);
+
+ EXPECT_CALL(obs1_, OnPreferenceChanged(_, _)).Times(0);
+ EXPECT_CALL(obs2_, OnPreferenceChanged(&pref_service_, kChangedPref));
+ notifier.OnPreferenceChanged(kChangedPref);
+ Mock::VerifyAndClearExpectations(&obs1_);
+ Mock::VerifyAndClearExpectations(&obs2_);
+
+ notifier.RemovePrefObserver(kChangedPref, &obs2_);
+ notifier.RemovePrefObserver(kUnchangedPref, &obs2_);
+}
+
+} // namespace
diff --git a/chromium/components/prefs/pref_observer.h b/chromium/components/prefs/pref_observer.h
new file mode 100644
index 00000000000..3f489318e9e
--- /dev/null
+++ b/chromium/components/prefs/pref_observer.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_PREF_OBSERVER_H_
+#define COMPONENTS_PREFS_PREF_OBSERVER_H_
+
+#include <string>
+
+class PrefService;
+
+// Used internally to the Prefs subsystem to pass preference change
+// notifications between PrefService, PrefNotifierImpl and
+// PrefChangeRegistrar.
+class PrefObserver {
+ public:
+ virtual void OnPreferenceChanged(PrefService* service,
+ const std::string& pref_name) = 0;
+};
+
+#endif // COMPONENTS_PREFS_PREF_OBSERVER_H_
diff --git a/chromium/components/prefs/pref_registry.cc b/chromium/components/prefs/pref_registry.cc
new file mode 100644
index 00000000000..b7c91de53c1
--- /dev/null
+++ b/chromium/components/prefs/pref_registry.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 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 "components/prefs/pref_registry.h"
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+#include "components/prefs/default_pref_store.h"
+#include "components/prefs/pref_store.h"
+
+PrefRegistry::PrefRegistry()
+ : defaults_(new DefaultPrefStore()) {
+}
+
+PrefRegistry::~PrefRegistry() {
+}
+
+uint32_t PrefRegistry::GetRegistrationFlags(
+ const std::string& pref_name) const {
+ const auto& it = registration_flags_.find(pref_name);
+ if (it == registration_flags_.end())
+ return NO_REGISTRATION_FLAGS;
+ return it->second;
+}
+
+scoped_refptr<PrefStore> PrefRegistry::defaults() {
+ return defaults_.get();
+}
+
+PrefRegistry::const_iterator PrefRegistry::begin() const {
+ return defaults_->begin();
+}
+
+PrefRegistry::const_iterator PrefRegistry::end() const {
+ return defaults_->end();
+}
+
+void PrefRegistry::SetDefaultPrefValue(const std::string& pref_name,
+ base::Value* value) {
+ DCHECK(value);
+ const base::Value* current_value = NULL;
+ DCHECK(defaults_->GetValue(pref_name, &current_value))
+ << "Setting default for unregistered pref: " << pref_name;
+ DCHECK(value->IsType(current_value->GetType()))
+ << "Wrong type for new default: " << pref_name;
+
+ defaults_->ReplaceDefaultValue(pref_name, make_scoped_ptr(value));
+}
+
+void PrefRegistry::RegisterPreference(const std::string& path,
+ base::Value* default_value,
+ uint32_t flags) {
+ base::Value::Type orig_type = default_value->GetType();
+ DCHECK(orig_type != base::Value::TYPE_NULL &&
+ orig_type != base::Value::TYPE_BINARY) <<
+ "invalid preference type: " << orig_type;
+ DCHECK(!defaults_->GetValue(path, NULL)) <<
+ "Trying to register a previously registered pref: " << path;
+ DCHECK(!ContainsKey(registration_flags_, path)) <<
+ "Trying to register a previously registered pref: " << path;
+
+ defaults_->SetDefaultValue(path, make_scoped_ptr(default_value));
+ if (flags != NO_REGISTRATION_FLAGS)
+ registration_flags_[path] = flags;
+}
diff --git a/chromium/components/prefs/pref_registry.h b/chromium/components/prefs/pref_registry.h
new file mode 100644
index 00000000000..31a4956f793
--- /dev/null
+++ b/chromium/components/prefs/pref_registry.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_PREF_REGISTRY_H_
+#define COMPONENTS_PREFS_PREF_REGISTRY_H_
+
+#include <stdint.h>
+
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/pref_value_map.h"
+
+namespace base {
+class Value;
+}
+
+class DefaultPrefStore;
+class PrefStore;
+
+// Preferences need to be registered with a type and default value
+// before they are used.
+//
+// The way you use a PrefRegistry is that you register all required
+// preferences on it (via one of its subclasses), then pass it as a
+// construction parameter to PrefService.
+//
+// Currently, registrations after constructing the PrefService will
+// also work, but this is being deprecated.
+class COMPONENTS_PREFS_EXPORT PrefRegistry
+ : public base::RefCounted<PrefRegistry> {
+ public:
+ // Registration flags that can be specified which impact how the pref will
+ // behave or be stored. This will be passed in a bitmask when the pref is
+ // registered. Subclasses of PrefRegistry can specify their own flags. Care
+ // must be taken to ensure none of these overlap with the flags below.
+ enum PrefRegistrationFlags : uint32_t {
+ // No flags are specified.
+ NO_REGISTRATION_FLAGS = 0,
+
+ // The first 8 bits are reserved for subclasses of PrefRegistry to use.
+
+ // This marks the pref as "lossy". There is no strict time guarantee on when
+ // a lossy pref will be persisted to permanent storage when it is modified.
+ LOSSY_PREF = 1 << 8,
+ };
+
+ typedef PrefValueMap::const_iterator const_iterator;
+ typedef base::hash_map<std::string, uint32_t> PrefRegistrationFlagsMap;
+
+ PrefRegistry();
+
+ // Retrieve the set of registration flags for the given preference. The return
+ // value is a bitmask of PrefRegistrationFlags.
+ uint32_t GetRegistrationFlags(const std::string& pref_name) const;
+
+ // Gets the registered defaults.
+ scoped_refptr<PrefStore> defaults();
+
+ // Allows iteration over defaults.
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ // Changes the default value for a preference. Takes ownership of |value|.
+ //
+ // |pref_name| must be a previously registered preference.
+ void SetDefaultPrefValue(const std::string& pref_name, base::Value* value);
+
+ protected:
+ friend class base::RefCounted<PrefRegistry>;
+ virtual ~PrefRegistry();
+
+ // Used by subclasses to register a default value and registration flags for
+ // a preference. |flags| is a bitmask of |PrefRegistrationFlags|.
+ void RegisterPreference(const std::string& path,
+ base::Value* default_value,
+ uint32_t flags);
+
+ scoped_refptr<DefaultPrefStore> defaults_;
+
+ // A map of pref name to a bitmask of PrefRegistrationFlags.
+ PrefRegistrationFlagsMap registration_flags_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrefRegistry);
+};
+
+#endif // COMPONENTS_PREFS_PREF_REGISTRY_H_
diff --git a/chromium/components/prefs/pref_registry_simple.cc b/chromium/components/prefs/pref_registry_simple.cc
new file mode 100644
index 00000000000..4874261babc
--- /dev/null
+++ b/chromium/components/prefs/pref_registry_simple.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2012 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 "components/prefs/pref_registry_simple.h"
+
+#include "base/files/file_path.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
+
+PrefRegistrySimple::PrefRegistrySimple() {
+}
+
+PrefRegistrySimple::~PrefRegistrySimple() {
+}
+
+void PrefRegistrySimple::RegisterBooleanPref(const std::string& path,
+ bool default_value) {
+ RegisterPrefAndNotify(path, new base::FundamentalValue(default_value),
+ NO_REGISTRATION_FLAGS);
+}
+
+void PrefRegistrySimple::RegisterIntegerPref(const std::string& path,
+ int default_value) {
+ RegisterPrefAndNotify(path, new base::FundamentalValue(default_value),
+ NO_REGISTRATION_FLAGS);
+}
+
+void PrefRegistrySimple::RegisterDoublePref(const std::string& path,
+ double default_value) {
+ RegisterPrefAndNotify(path, new base::FundamentalValue(default_value),
+ NO_REGISTRATION_FLAGS);
+}
+
+void PrefRegistrySimple::RegisterStringPref(const std::string& path,
+ const std::string& default_value) {
+ RegisterPrefAndNotify(path, new base::StringValue(default_value),
+ NO_REGISTRATION_FLAGS);
+}
+
+void PrefRegistrySimple::RegisterFilePathPref(
+ const std::string& path,
+ const base::FilePath& default_value) {
+ RegisterPrefAndNotify(path, new base::StringValue(default_value.value()),
+ NO_REGISTRATION_FLAGS);
+}
+
+void PrefRegistrySimple::RegisterListPref(const std::string& path) {
+ RegisterPrefAndNotify(path, new base::ListValue(), NO_REGISTRATION_FLAGS);
+}
+
+void PrefRegistrySimple::RegisterListPref(const std::string& path,
+ base::ListValue* default_value) {
+ RegisterPrefAndNotify(path, default_value, NO_REGISTRATION_FLAGS);
+}
+
+void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path) {
+ RegisterPrefAndNotify(path, new base::DictionaryValue(),
+ NO_REGISTRATION_FLAGS);
+}
+
+void PrefRegistrySimple::RegisterDictionaryPref(
+ const std::string& path,
+ base::DictionaryValue* default_value) {
+ RegisterPrefAndNotify(path, default_value, NO_REGISTRATION_FLAGS);
+}
+
+void PrefRegistrySimple::RegisterInt64Pref(const std::string& path,
+ int64_t default_value) {
+ RegisterPrefAndNotify(
+ path, new base::StringValue(base::Int64ToString(default_value)),
+ NO_REGISTRATION_FLAGS);
+}
+
+void PrefRegistrySimple::RegisterUint64Pref(const std::string& path,
+ uint64_t default_value) {
+ RegisterPrefAndNotify(
+ path, new base::StringValue(base::Uint64ToString(default_value)),
+ NO_REGISTRATION_FLAGS);
+}
+
+void PrefRegistrySimple::RegisterBooleanPref(const std::string& path,
+ bool default_value,
+ uint32_t flags) {
+ RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), flags);
+}
+
+void PrefRegistrySimple::RegisterIntegerPref(const std::string& path,
+ int default_value,
+ uint32_t flags) {
+ RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), flags);
+}
+
+void PrefRegistrySimple::RegisterDoublePref(const std::string& path,
+ double default_value,
+ uint32_t flags) {
+ RegisterPrefAndNotify(path, new base::FundamentalValue(default_value), flags);
+}
+
+void PrefRegistrySimple::RegisterStringPref(const std::string& path,
+ const std::string& default_value,
+ uint32_t flags) {
+ RegisterPrefAndNotify(path, new base::StringValue(default_value), flags);
+}
+
+void PrefRegistrySimple::RegisterFilePathPref(
+ const std::string& path,
+ const base::FilePath& default_value,
+ uint32_t flags) {
+ RegisterPrefAndNotify(path, new base::StringValue(default_value.value()),
+ flags);
+}
+
+void PrefRegistrySimple::RegisterListPref(const std::string& path,
+ uint32_t flags) {
+ RegisterPrefAndNotify(path, new base::ListValue(), flags);
+}
+
+void PrefRegistrySimple::RegisterListPref(const std::string& path,
+ base::ListValue* default_value,
+ uint32_t flags) {
+ RegisterPrefAndNotify(path, default_value, flags);
+}
+
+void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path,
+ uint32_t flags) {
+ RegisterPrefAndNotify(path, new base::DictionaryValue(), flags);
+}
+
+void PrefRegistrySimple::RegisterDictionaryPref(
+ const std::string& path,
+ base::DictionaryValue* default_value,
+ uint32_t flags) {
+ RegisterPrefAndNotify(path, default_value, flags);
+}
+
+void PrefRegistrySimple::RegisterInt64Pref(const std::string& path,
+ int64_t default_value,
+ uint32_t flags) {
+ RegisterPrefAndNotify(
+ path, new base::StringValue(base::Int64ToString(default_value)), flags);
+}
+
+void PrefRegistrySimple::RegisterUint64Pref(const std::string& path,
+ uint64_t default_value,
+ uint32_t flags) {
+ RegisterPrefAndNotify(
+ path, new base::StringValue(base::Uint64ToString(default_value)), flags);
+}
+
+void PrefRegistrySimple::OnPrefRegistered(const std::string& path,
+ base::Value* default_value,
+ uint32_t flags) {}
+
+void PrefRegistrySimple::RegisterPrefAndNotify(const std::string& path,
+ base::Value* default_value,
+ uint32_t flags) {
+ RegisterPreference(path, default_value, flags);
+ OnPrefRegistered(path, default_value, flags);
+}
diff --git a/chromium/components/prefs/pref_registry_simple.h b/chromium/components/prefs/pref_registry_simple.h
new file mode 100644
index 00000000000..e3267871590
--- /dev/null
+++ b/chromium/components/prefs/pref_registry_simple.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_
+#define COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/pref_registry.h"
+
+namespace base {
+class DictionaryValue;
+class FilePath;
+class ListValue;
+}
+
+// A simple implementation of PrefRegistry.
+class COMPONENTS_PREFS_EXPORT PrefRegistrySimple : public PrefRegistry {
+ public:
+ PrefRegistrySimple();
+
+ void RegisterBooleanPref(const std::string& path, bool default_value);
+ void RegisterIntegerPref(const std::string& path, int default_value);
+ void RegisterDoublePref(const std::string& path, double default_value);
+ void RegisterStringPref(const std::string& path,
+ const std::string& default_value);
+ void RegisterFilePathPref(const std::string& path,
+ const base::FilePath& default_value);
+ void RegisterListPref(const std::string& path);
+ void RegisterDictionaryPref(const std::string& path);
+ void RegisterListPref(const std::string& path,
+ base::ListValue* default_value);
+ void RegisterDictionaryPref(const std::string& path,
+ base::DictionaryValue* default_value);
+ void RegisterInt64Pref(const std::string& path, int64_t default_value);
+ void RegisterUint64Pref(const std::string&, uint64_t default_value);
+
+ // Versions of registration functions that accept PrefRegistrationFlags.
+ // |flags| is a bitmask of PrefRegistrationFlags.
+ void RegisterBooleanPref(const std::string&,
+ bool default_value,
+ uint32_t flags);
+ void RegisterIntegerPref(const std::string&,
+ int default_value,
+ uint32_t flags);
+ void RegisterDoublePref(const std::string&,
+ double default_value,
+ uint32_t flags);
+ void RegisterStringPref(const std::string&,
+ const std::string& default_value,
+ uint32_t flags);
+ void RegisterFilePathPref(const std::string&,
+ const base::FilePath& default_value,
+ uint32_t flags);
+ void RegisterListPref(const std::string&, uint32_t flags);
+ void RegisterDictionaryPref(const std::string&, uint32_t flags);
+ void RegisterListPref(const std::string&,
+ base::ListValue* default_value,
+ uint32_t flags);
+ void RegisterDictionaryPref(const std::string&,
+ base::DictionaryValue* default_value,
+ uint32_t flags);
+ void RegisterInt64Pref(const std::string&,
+ int64_t default_value,
+ uint32_t flags);
+ void RegisterUint64Pref(const std::string&,
+ uint64_t default_value,
+ uint32_t flags);
+
+ protected:
+ ~PrefRegistrySimple() override;
+
+ // Allows subclasses to hook into pref registration.
+ virtual void OnPrefRegistered(const std::string&,
+ base::Value* default_value,
+ uint32_t flags);
+
+ private:
+ void RegisterPrefAndNotify(const std::string&,
+ base::Value* default_value,
+ uint32_t flags);
+
+ DISALLOW_COPY_AND_ASSIGN(PrefRegistrySimple);
+};
+
+#endif // COMPONENTS_PREFS_PREF_REGISTRY_SIMPLE_H_
diff --git a/chromium/components/prefs/pref_service.cc b/chromium/components/prefs/pref_service.cc
new file mode 100644
index 00000000000..1ef2313b3fa
--- /dev/null
+++ b/chromium/components/prefs/pref_service.cc
@@ -0,0 +1,620 @@
+// Copyright (c) 2012 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 "components/prefs/pref_service.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/value_conversions.h"
+#include "build/build_config.h"
+#include "components/prefs/default_pref_store.h"
+#include "components/prefs/pref_notifier_impl.h"
+#include "components/prefs/pref_registry.h"
+#include "components/prefs/pref_value_store.h"
+
+namespace {
+
+class ReadErrorHandler : public PersistentPrefStore::ReadErrorDelegate {
+ public:
+ ReadErrorHandler(base::Callback<void(PersistentPrefStore::PrefReadError)> cb)
+ : callback_(cb) {}
+
+ void OnError(PersistentPrefStore::PrefReadError error) override {
+ callback_.Run(error);
+ }
+
+ private:
+ base::Callback<void(PersistentPrefStore::PrefReadError)> callback_;
+};
+
+// Returns the WriteablePrefStore::PrefWriteFlags for the pref with the given
+// |path|.
+uint32_t GetWriteFlags(const PrefService::Preference* pref) {
+ uint32_t write_flags = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS;
+
+ if (!pref)
+ return write_flags;
+
+ if (pref->registration_flags() & PrefRegistry::LOSSY_PREF)
+ write_flags |= WriteablePrefStore::LOSSY_PREF_WRITE_FLAG;
+ return write_flags;
+}
+
+} // namespace
+
+PrefService::PrefService(
+ PrefNotifierImpl* pref_notifier,
+ PrefValueStore* pref_value_store,
+ PersistentPrefStore* user_prefs,
+ PrefRegistry* pref_registry,
+ base::Callback<void(PersistentPrefStore::PrefReadError)>
+ read_error_callback,
+ bool async)
+ : pref_notifier_(pref_notifier),
+ pref_value_store_(pref_value_store),
+ pref_registry_(pref_registry),
+ user_pref_store_(user_prefs),
+ read_error_callback_(read_error_callback) {
+ pref_notifier_->SetPrefService(this);
+
+ // TODO(battre): This is a check for crbug.com/435208 to make sure that
+ // access violations are caused by a use-after-free bug and not by an
+ // initialization bug.
+ CHECK(pref_registry_);
+ CHECK(pref_value_store_);
+
+ InitFromStorage(async);
+}
+
+PrefService::~PrefService() {
+ DCHECK(CalledOnValidThread());
+
+ // Reset pointers so accesses after destruction reliably crash.
+ pref_value_store_.reset();
+ pref_registry_ = NULL;
+ user_pref_store_ = NULL;
+ pref_notifier_.reset();
+}
+
+void PrefService::InitFromStorage(bool async) {
+ if (user_pref_store_->IsInitializationComplete()) {
+ read_error_callback_.Run(user_pref_store_->GetReadError());
+ } else if (!async) {
+ read_error_callback_.Run(user_pref_store_->ReadPrefs());
+ } else {
+ // Guarantee that initialization happens after this function returned.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&PersistentPrefStore::ReadPrefsAsync, user_pref_store_.get(),
+ new ReadErrorHandler(read_error_callback_)));
+ }
+}
+
+void PrefService::CommitPendingWrite() {
+ DCHECK(CalledOnValidThread());
+ user_pref_store_->CommitPendingWrite();
+}
+
+void PrefService::SchedulePendingLossyWrites() {
+ DCHECK(CalledOnValidThread());
+ user_pref_store_->SchedulePendingLossyWrites();
+}
+
+bool PrefService::GetBoolean(const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+
+ bool result = false;
+
+ const base::Value* value = GetPreferenceValue(path);
+ if (!value) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return result;
+ }
+ bool rv = value->GetAsBoolean(&result);
+ DCHECK(rv);
+ return result;
+}
+
+int PrefService::GetInteger(const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+
+ int result = 0;
+
+ const base::Value* value = GetPreferenceValue(path);
+ if (!value) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return result;
+ }
+ bool rv = value->GetAsInteger(&result);
+ DCHECK(rv);
+ return result;
+}
+
+double PrefService::GetDouble(const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+
+ double result = 0.0;
+
+ const base::Value* value = GetPreferenceValue(path);
+ if (!value) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return result;
+ }
+ bool rv = value->GetAsDouble(&result);
+ DCHECK(rv);
+ return result;
+}
+
+std::string PrefService::GetString(const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+
+ std::string result;
+
+ const base::Value* value = GetPreferenceValue(path);
+ if (!value) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return result;
+ }
+ bool rv = value->GetAsString(&result);
+ DCHECK(rv);
+ return result;
+}
+
+base::FilePath PrefService::GetFilePath(const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+
+ base::FilePath result;
+
+ const base::Value* value = GetPreferenceValue(path);
+ if (!value) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return base::FilePath(result);
+ }
+ bool rv = base::GetValueAsFilePath(*value, &result);
+ DCHECK(rv);
+ return result;
+}
+
+bool PrefService::HasPrefPath(const std::string& path) const {
+ const Preference* pref = FindPreference(path);
+ return pref && !pref->IsDefaultValue();
+}
+
+scoped_ptr<base::DictionaryValue> PrefService::GetPreferenceValues() const {
+ DCHECK(CalledOnValidThread());
+ scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue);
+ for (const auto& it : *pref_registry_) {
+ out->Set(it.first, GetPreferenceValue(it.first)->CreateDeepCopy());
+ }
+ return out;
+}
+
+scoped_ptr<base::DictionaryValue> PrefService::GetPreferenceValuesOmitDefaults()
+ const {
+ DCHECK(CalledOnValidThread());
+ scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue);
+ for (const auto& it : *pref_registry_) {
+ const Preference* pref = FindPreference(it.first);
+ if (pref->IsDefaultValue())
+ continue;
+ out->Set(it.first, pref->GetValue()->CreateDeepCopy());
+ }
+ return out;
+}
+
+scoped_ptr<base::DictionaryValue>
+PrefService::GetPreferenceValuesWithoutPathExpansion() const {
+ DCHECK(CalledOnValidThread());
+ scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue);
+ for (const auto& it : *pref_registry_) {
+ const base::Value* value = GetPreferenceValue(it.first);
+ DCHECK(value);
+ out->SetWithoutPathExpansion(it.first, value->CreateDeepCopy());
+ }
+ return out;
+}
+
+const PrefService::Preference* PrefService::FindPreference(
+ const std::string& pref_name) const {
+ DCHECK(CalledOnValidThread());
+ PreferenceMap::iterator it = prefs_map_.find(pref_name);
+ if (it != prefs_map_.end())
+ return &(it->second);
+ const base::Value* default_value = NULL;
+ if (!pref_registry_->defaults()->GetValue(pref_name, &default_value))
+ return NULL;
+ it = prefs_map_.insert(
+ std::make_pair(pref_name, Preference(
+ this, pref_name, default_value->GetType()))).first;
+ return &(it->second);
+}
+
+bool PrefService::ReadOnly() const {
+ return user_pref_store_->ReadOnly();
+}
+
+PrefService::PrefInitializationStatus PrefService::GetInitializationStatus()
+ const {
+ if (!user_pref_store_->IsInitializationComplete())
+ return INITIALIZATION_STATUS_WAITING;
+
+ switch (user_pref_store_->GetReadError()) {
+ case PersistentPrefStore::PREF_READ_ERROR_NONE:
+ return INITIALIZATION_STATUS_SUCCESS;
+ case PersistentPrefStore::PREF_READ_ERROR_NO_FILE:
+ return INITIALIZATION_STATUS_CREATED_NEW_PREF_STORE;
+ default:
+ return INITIALIZATION_STATUS_ERROR;
+ }
+}
+
+bool PrefService::IsManagedPreference(const std::string& pref_name) const {
+ const Preference* pref = FindPreference(pref_name);
+ return pref && pref->IsManaged();
+}
+
+bool PrefService::IsPreferenceManagedByCustodian(
+ const std::string& pref_name) const {
+ const Preference* pref = FindPreference(pref_name);
+ return pref && pref->IsManagedByCustodian();
+}
+
+bool PrefService::IsUserModifiablePreference(
+ const std::string& pref_name) const {
+ const Preference* pref = FindPreference(pref_name);
+ return pref && pref->IsUserModifiable();
+}
+
+const base::DictionaryValue* PrefService::GetDictionary(
+ const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+
+ const base::Value* value = GetPreferenceValue(path);
+ if (!value) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return NULL;
+ }
+ if (value->GetType() != base::Value::TYPE_DICTIONARY) {
+ NOTREACHED();
+ return NULL;
+ }
+ return static_cast<const base::DictionaryValue*>(value);
+}
+
+const base::Value* PrefService::GetUserPrefValue(
+ const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to get an unregistered pref: " << path;
+ return NULL;
+ }
+
+ // Look for an existing preference in the user store. If it doesn't
+ // exist, return NULL.
+ base::Value* value = NULL;
+ if (!user_pref_store_->GetMutableValue(path, &value))
+ return NULL;
+
+ if (!value->IsType(pref->GetType())) {
+ NOTREACHED() << "Pref value type doesn't match registered type.";
+ return NULL;
+ }
+
+ return value;
+}
+
+void PrefService::SetDefaultPrefValue(const std::string& path,
+ base::Value* value) {
+ DCHECK(CalledOnValidThread());
+ pref_registry_->SetDefaultPrefValue(path, value);
+}
+
+const base::Value* PrefService::GetDefaultPrefValue(
+ const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+ // Lookup the preference in the default store.
+ const base::Value* value = NULL;
+ if (!pref_registry_->defaults()->GetValue(path, &value)) {
+ NOTREACHED() << "Default value missing for pref: " << path;
+ return NULL;
+ }
+ return value;
+}
+
+const base::ListValue* PrefService::GetList(const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+
+ const base::Value* value = GetPreferenceValue(path);
+ if (!value) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return NULL;
+ }
+ if (value->GetType() != base::Value::TYPE_LIST) {
+ NOTREACHED();
+ return NULL;
+ }
+ return static_cast<const base::ListValue*>(value);
+}
+
+void PrefService::AddPrefObserver(const std::string& path, PrefObserver* obs) {
+ pref_notifier_->AddPrefObserver(path, obs);
+}
+
+void PrefService::RemovePrefObserver(const std::string& path,
+ PrefObserver* obs) {
+ pref_notifier_->RemovePrefObserver(path, obs);
+}
+
+void PrefService::AddPrefInitObserver(base::Callback<void(bool)> obs) {
+ pref_notifier_->AddInitObserver(obs);
+}
+
+PrefRegistry* PrefService::DeprecatedGetPrefRegistry() {
+ return pref_registry_.get();
+}
+
+void PrefService::ClearPref(const std::string& path) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to clear an unregistered pref: " << path;
+ return;
+ }
+ user_pref_store_->RemoveValue(path, GetWriteFlags(pref));
+}
+
+void PrefService::ClearMutableValues() {
+ user_pref_store_->ClearMutableValues();
+}
+
+void PrefService::Set(const std::string& path, const base::Value& value) {
+ SetUserPrefValue(path, value.DeepCopy());
+}
+
+void PrefService::SetBoolean(const std::string& path, bool value) {
+ SetUserPrefValue(path, new base::FundamentalValue(value));
+}
+
+void PrefService::SetInteger(const std::string& path, int value) {
+ SetUserPrefValue(path, new base::FundamentalValue(value));
+}
+
+void PrefService::SetDouble(const std::string& path, double value) {
+ SetUserPrefValue(path, new base::FundamentalValue(value));
+}
+
+void PrefService::SetString(const std::string& path, const std::string& value) {
+ SetUserPrefValue(path, new base::StringValue(value));
+}
+
+void PrefService::SetFilePath(const std::string& path,
+ const base::FilePath& value) {
+ SetUserPrefValue(path, base::CreateFilePathValue(value));
+}
+
+void PrefService::SetInt64(const std::string& path, int64_t value) {
+ SetUserPrefValue(path, new base::StringValue(base::Int64ToString(value)));
+}
+
+int64_t PrefService::GetInt64(const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+
+ const base::Value* value = GetPreferenceValue(path);
+ if (!value) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return 0;
+ }
+ std::string result("0");
+ bool rv = value->GetAsString(&result);
+ DCHECK(rv);
+
+ int64_t val;
+ base::StringToInt64(result, &val);
+ return val;
+}
+
+void PrefService::SetUint64(const std::string& path, uint64_t value) {
+ SetUserPrefValue(path, new base::StringValue(base::Uint64ToString(value)));
+}
+
+uint64_t PrefService::GetUint64(const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+
+ const base::Value* value = GetPreferenceValue(path);
+ if (!value) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return 0;
+ }
+ std::string result("0");
+ bool rv = value->GetAsString(&result);
+ DCHECK(rv);
+
+ uint64_t val;
+ base::StringToUint64(result, &val);
+ return val;
+}
+
+base::Value* PrefService::GetMutableUserPref(const std::string& path,
+ base::Value::Type type) {
+ CHECK(type == base::Value::TYPE_DICTIONARY || type == base::Value::TYPE_LIST);
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to get an unregistered pref: " << path;
+ return NULL;
+ }
+ if (pref->GetType() != type) {
+ NOTREACHED() << "Wrong type for GetMutableValue: " << path;
+ return NULL;
+ }
+
+ // Look for an existing preference in the user store. If it doesn't
+ // exist or isn't the correct type, create a new user preference.
+ base::Value* value = NULL;
+ if (!user_pref_store_->GetMutableValue(path, &value) ||
+ !value->IsType(type)) {
+ if (type == base::Value::TYPE_DICTIONARY) {
+ value = new base::DictionaryValue;
+ } else if (type == base::Value::TYPE_LIST) {
+ value = new base::ListValue;
+ } else {
+ NOTREACHED();
+ }
+ user_pref_store_->SetValueSilently(path, make_scoped_ptr(value),
+ GetWriteFlags(pref));
+ }
+ return value;
+}
+
+void PrefService::ReportUserPrefChanged(const std::string& key) {
+ DCHECK(CalledOnValidThread());
+ user_pref_store_->ReportValueChanged(key, GetWriteFlags(FindPreference(key)));
+}
+
+void PrefService::SetUserPrefValue(const std::string& path,
+ base::Value* new_value) {
+ scoped_ptr<base::Value> owned_value(new_value);
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to write an unregistered pref: " << path;
+ return;
+ }
+ if (pref->GetType() != new_value->GetType()) {
+ NOTREACHED() << "Trying to set pref " << path
+ << " of type " << pref->GetType()
+ << " to value of type " << new_value->GetType();
+ return;
+ }
+
+ user_pref_store_->SetValue(path, std::move(owned_value), GetWriteFlags(pref));
+}
+
+void PrefService::UpdateCommandLinePrefStore(PrefStore* command_line_store) {
+ pref_value_store_->UpdateCommandLinePrefStore(command_line_store);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// PrefService::Preference
+
+PrefService::Preference::Preference(const PrefService* service,
+ const std::string& name,
+ base::Value::Type type)
+ : name_(name), type_(type), pref_service_(service) {
+ DCHECK(service);
+ // Cache the registration flags at creation time to avoid multiple map lookups
+ // later.
+ registration_flags_ = service->pref_registry_->GetRegistrationFlags(name_);
+}
+
+const std::string PrefService::Preference::name() const {
+ return name_;
+}
+
+base::Value::Type PrefService::Preference::GetType() const {
+ return type_;
+}
+
+const base::Value* PrefService::Preference::GetValue() const {
+ const base::Value* result= pref_service_->GetPreferenceValue(name_);
+ DCHECK(result) << "Must register pref before getting its value";
+ return result;
+}
+
+const base::Value* PrefService::Preference::GetRecommendedValue() const {
+ DCHECK(pref_service_->FindPreference(name_))
+ << "Must register pref before getting its value";
+
+ const base::Value* found_value = NULL;
+ if (pref_value_store()->GetRecommendedValue(name_, type_, &found_value)) {
+ DCHECK(found_value->IsType(type_));
+ return found_value;
+ }
+
+ // The pref has no recommended value.
+ return NULL;
+}
+
+bool PrefService::Preference::IsManaged() const {
+ return pref_value_store()->PrefValueInManagedStore(name_);
+}
+
+bool PrefService::Preference::IsManagedByCustodian() const {
+ return pref_value_store()->PrefValueInSupervisedStore(name_.c_str());
+}
+
+bool PrefService::Preference::IsRecommended() const {
+ return pref_value_store()->PrefValueFromRecommendedStore(name_);
+}
+
+bool PrefService::Preference::HasExtensionSetting() const {
+ return pref_value_store()->PrefValueInExtensionStore(name_);
+}
+
+bool PrefService::Preference::HasUserSetting() const {
+ return pref_value_store()->PrefValueInUserStore(name_);
+}
+
+bool PrefService::Preference::IsExtensionControlled() const {
+ return pref_value_store()->PrefValueFromExtensionStore(name_);
+}
+
+bool PrefService::Preference::IsUserControlled() const {
+ return pref_value_store()->PrefValueFromUserStore(name_);
+}
+
+bool PrefService::Preference::IsDefaultValue() const {
+ return pref_value_store()->PrefValueFromDefaultStore(name_);
+}
+
+bool PrefService::Preference::IsUserModifiable() const {
+ return pref_value_store()->PrefValueUserModifiable(name_);
+}
+
+bool PrefService::Preference::IsExtensionModifiable() const {
+ return pref_value_store()->PrefValueExtensionModifiable(name_);
+}
+
+const base::Value* PrefService::GetPreferenceValue(
+ const std::string& path) const {
+ DCHECK(CalledOnValidThread());
+
+ // TODO(battre): This is a check for crbug.com/435208. After analyzing some
+ // crash dumps it looks like the PrefService is accessed even though it has
+ // been cleared already.
+ CHECK(pref_registry_);
+ CHECK(pref_registry_->defaults());
+ CHECK(pref_value_store_);
+
+ const base::Value* default_value = NULL;
+ if (pref_registry_->defaults()->GetValue(path, &default_value)) {
+ const base::Value* found_value = NULL;
+ base::Value::Type default_type = default_value->GetType();
+ if (pref_value_store_->GetValue(path, default_type, &found_value)) {
+ DCHECK(found_value->IsType(default_type));
+ return found_value;
+ } else {
+ // Every registered preference has at least a default value.
+ NOTREACHED() << "no valid value found for registered pref " << path;
+ }
+ }
+
+ return NULL;
+}
diff --git a/chromium/components/prefs/pref_service.h b/chromium/components/prefs/pref_service.h
new file mode 100644
index 00000000000..6c95b64f653
--- /dev/null
+++ b/chromium/components/prefs/pref_service.h
@@ -0,0 +1,388 @@
+// Copyright (c) 2012 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.
+
+// This provides a way to access the application's current preferences.
+
+// Chromium settings and storage represent user-selected preferences and
+// information and MUST not be extracted, overwritten or modified except
+// through Chromium defined APIs.
+
+#ifndef COMPONENTS_PREFS_PREF_SERVICE_H_
+#define COMPONENTS_PREFS_PREF_SERVICE_H_
+
+#include <stdint.h>
+
+#include <set>
+#include <string>
+
+#include "base/callback.h"
+#include "base/compiler_specific.h"
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/values.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/persistent_pref_store.h"
+
+class PrefNotifier;
+class PrefNotifierImpl;
+class PrefObserver;
+class PrefRegistry;
+class PrefValueStore;
+class PrefStore;
+
+namespace base {
+class FilePath;
+}
+
+namespace subtle {
+class PrefMemberBase;
+class ScopedUserPrefUpdateBase;
+}
+
+// Base class for PrefServices. You can use the base class to read and
+// interact with preferences, but not to register new preferences; for
+// that see e.g. PrefRegistrySimple.
+//
+// Settings and storage accessed through this class represent
+// user-selected preferences and information and MUST not be
+// extracted, overwritten or modified except through the defined APIs.
+class COMPONENTS_PREFS_EXPORT PrefService : public base::NonThreadSafe {
+ public:
+ enum PrefInitializationStatus {
+ INITIALIZATION_STATUS_WAITING,
+ INITIALIZATION_STATUS_SUCCESS,
+ INITIALIZATION_STATUS_CREATED_NEW_PREF_STORE,
+ INITIALIZATION_STATUS_ERROR
+ };
+
+ // A helper class to store all the information associated with a preference.
+ class COMPONENTS_PREFS_EXPORT Preference {
+ public:
+ // The type of the preference is determined by the type with which it is
+ // registered. This type needs to be a boolean, integer, double, string,
+ // dictionary (a branch), or list. You shouldn't need to construct this on
+ // your own; use the PrefService::Register*Pref methods instead.
+ Preference(const PrefService* service,
+ const std::string& name,
+ base::Value::Type type);
+ ~Preference() {}
+
+ // Returns the name of the Preference (i.e., the key, e.g.,
+ // browser.window_placement).
+ const std::string name() const;
+
+ // Returns the registered type of the preference.
+ base::Value::Type GetType() const;
+
+ // Returns the value of the Preference, falling back to the registered
+ // default value if no other has been set.
+ const base::Value* GetValue() const;
+
+ // Returns the value recommended by the admin, if any.
+ const base::Value* GetRecommendedValue() const;
+
+ // Returns true if the Preference is managed, i.e. set by an admin policy.
+ // Since managed prefs have the highest priority, this also indicates
+ // whether the pref is actually being controlled by the policy setting.
+ bool IsManaged() const;
+
+ // Returns true if the Preference is controlled by the custodian of the
+ // supervised user. Since a supervised user is not expected to have an admin
+ // policy, this is the controlling pref if set.
+ bool IsManagedByCustodian() const;
+
+ // Returns true if the Preference is recommended, i.e. set by an admin
+ // policy but the user is allowed to change it.
+ bool IsRecommended() const;
+
+ // Returns true if the Preference has a value set by an extension, even if
+ // that value is being overridden by a higher-priority source.
+ bool HasExtensionSetting() const;
+
+ // Returns true if the Preference has a user setting, even if that value is
+ // being overridden by a higher-priority source.
+ bool HasUserSetting() const;
+
+ // Returns true if the Preference value is currently being controlled by an
+ // extension, and not by any higher-priority source.
+ bool IsExtensionControlled() const;
+
+ // Returns true if the Preference value is currently being controlled by a
+ // user setting, and not by any higher-priority source.
+ bool IsUserControlled() const;
+
+ // Returns true if the Preference is currently using its default value,
+ // and has not been set by any higher-priority source (even with the same
+ // value).
+ bool IsDefaultValue() const;
+
+ // Returns true if the user can change the Preference value, which is the
+ // case if no higher-priority source than the user store controls the
+ // Preference.
+ bool IsUserModifiable() const;
+
+ // Returns true if an extension can change the Preference value, which is
+ // the case if no higher-priority source than the extension store controls
+ // the Preference.
+ bool IsExtensionModifiable() const;
+
+ // Return the registration flags for this pref as a bitmask of
+ // PrefRegistry::PrefRegistrationFlags.
+ uint32_t registration_flags() const { return registration_flags_; }
+
+ private:
+ friend class PrefService;
+
+ PrefValueStore* pref_value_store() const {
+ return pref_service_->pref_value_store_.get();
+ }
+
+ const std::string name_;
+
+ const base::Value::Type type_;
+
+ uint32_t registration_flags_;
+
+ // Reference to the PrefService in which this pref was created.
+ const PrefService* pref_service_;
+ };
+
+ // You may wish to use PrefServiceFactory or one of its subclasses
+ // for simplified construction.
+ PrefService(
+ PrefNotifierImpl* pref_notifier,
+ PrefValueStore* pref_value_store,
+ PersistentPrefStore* user_prefs,
+ PrefRegistry* pref_registry,
+ base::Callback<void(PersistentPrefStore::PrefReadError)>
+ read_error_callback,
+ bool async);
+ virtual ~PrefService();
+
+ // Lands pending writes to disk. This should only be used if we need to save
+ // immediately (basically, during shutdown).
+ void CommitPendingWrite();
+
+ // Schedule a write if there is any lossy data pending. Unlike
+ // CommitPendingWrite() this does not immediately sync to disk, instead it
+ // triggers an eventual write if there is lossy data pending and if there
+ // isn't one scheduled already.
+ void SchedulePendingLossyWrites();
+
+ // Returns true if the preference for the given preference name is available
+ // and is managed.
+ bool IsManagedPreference(const std::string& pref_name) const;
+
+ // Returns true if the preference for the given preference name is available
+ // and is controlled by the parent/guardian of the child Account.
+ bool IsPreferenceManagedByCustodian(const std::string& pref_name) const;
+
+ // Returns |true| if a preference with the given name is available and its
+ // value can be changed by the user.
+ bool IsUserModifiablePreference(const std::string& pref_name) const;
+
+ // Look up a preference. Returns NULL if the preference is not
+ // registered.
+ const PrefService::Preference* FindPreference(const std::string& path) const;
+
+ // If the path is valid and the value at the end of the path matches the type
+ // specified, it will return the specified value. Otherwise, the default
+ // value (set when the pref was registered) will be returned.
+ bool GetBoolean(const std::string& path) const;
+ int GetInteger(const std::string& path) const;
+ double GetDouble(const std::string& path) const;
+ std::string GetString(const std::string& path) const;
+ base::FilePath GetFilePath(const std::string& path) const;
+
+ // Returns the branch if it exists, or the registered default value otherwise.
+ // Note that |path| must point to a registered preference. In that case, these
+ // functions will never return NULL.
+ const base::DictionaryValue* GetDictionary(const std::string& path) const;
+ const base::ListValue* GetList(const std::string& path) const;
+
+ // Removes a user pref and restores the pref to its default value.
+ void ClearPref(const std::string& path);
+
+ // If the path is valid (i.e., registered), update the pref value in the user
+ // prefs.
+ // To set the value of dictionary or list values in the pref tree use
+ // Set(), but to modify the value of a dictionary or list use either
+ // ListPrefUpdate or DictionaryPrefUpdate from scoped_user_pref_update.h.
+ void Set(const std::string& path, const base::Value& value);
+ void SetBoolean(const std::string& path, bool value);
+ void SetInteger(const std::string& path, int value);
+ void SetDouble(const std::string& path, double value);
+ void SetString(const std::string& path, const std::string& value);
+ void SetFilePath(const std::string& path, const base::FilePath& value);
+
+ // Int64 helper methods that actually store the given value as a string.
+ // Note that if obtaining the named value via GetDictionary or GetList, the
+ // Value type will be TYPE_STRING.
+ void SetInt64(const std::string& path, int64_t value);
+ int64_t GetInt64(const std::string& path) const;
+
+ // As above, but for unsigned values.
+ void SetUint64(const std::string& path, uint64_t value);
+ uint64_t GetUint64(const std::string& path) const;
+
+ // Returns the value of the given preference, from the user pref store. If
+ // the preference is not set in the user pref store, returns NULL.
+ const base::Value* GetUserPrefValue(const std::string& path) const;
+
+ // Changes the default value for a preference. Takes ownership of |value|.
+ //
+ // Will cause a pref change notification to be fired if this causes
+ // the effective value to change.
+ void SetDefaultPrefValue(const std::string& path, base::Value* value);
+
+ // Returns the default value of the given preference. |path| must point to a
+ // registered preference. In that case, will never return NULL.
+ const base::Value* GetDefaultPrefValue(const std::string& path) const;
+
+ // Returns true if a value has been set for the specified path.
+ // NOTE: this is NOT the same as FindPreference. In particular
+ // FindPreference returns whether RegisterXXX has been invoked, where as
+ // this checks if a value exists for the path.
+ bool HasPrefPath(const std::string& path) const;
+
+ // Returns a dictionary with effective preference values.
+ scoped_ptr<base::DictionaryValue> GetPreferenceValues() const;
+
+ // Returns a dictionary with effective preference values, omitting prefs that
+ // are at their default values.
+ scoped_ptr<base::DictionaryValue> GetPreferenceValuesOmitDefaults() const;
+
+ // Returns a dictionary with effective preference values. Contrary to
+ // GetPreferenceValues(), the paths of registered preferences are not split on
+ // '.' characters. If a registered preference stores a dictionary, however,
+ // the hierarchical structure inside the preference will be preserved.
+ // For example, if "foo.bar" is a registered preference, the result could look
+ // like this:
+ // {"foo.bar": {"a": {"b": true}}}.
+ scoped_ptr<base::DictionaryValue> GetPreferenceValuesWithoutPathExpansion()
+ const;
+
+ bool ReadOnly() const;
+
+ PrefInitializationStatus GetInitializationStatus() const;
+
+ // Tell our PrefValueStore to update itself to |command_line_store|.
+ // Takes ownership of the store.
+ virtual void UpdateCommandLinePrefStore(PrefStore* command_line_store);
+
+ // We run the callback once, when initialization completes. The bool
+ // parameter will be set to true for successful initialization,
+ // false for unsuccessful.
+ void AddPrefInitObserver(base::Callback<void(bool)> callback);
+
+ // Returns the PrefRegistry object for this service. You should not
+ // use this; the intent is for no registrations to take place after
+ // PrefService has been constructed.
+ //
+ // Instead of using this method, the recommended approach is to
+ // register all preferences for a class Xyz up front in a static
+ // Xyz::RegisterPrefs function, which gets invoked early in the
+ // application's start-up, before a PrefService is created.
+ //
+ // As an example, prefs registration in Chrome is triggered by the
+ // functions chrome::RegisterPrefs (for global preferences) and
+ // chrome::RegisterProfilePrefs (for user-specific preferences)
+ // implemented in chrome/browser/prefs/browser_prefs.cc.
+ PrefRegistry* DeprecatedGetPrefRegistry();
+
+ // Clears mutable values.
+ void ClearMutableValues();
+
+ protected:
+ // The PrefNotifier handles registering and notifying preference observers.
+ // It is created and owned by this PrefService. Subclasses may access it for
+ // unit testing.
+ scoped_ptr<PrefNotifierImpl> pref_notifier_;
+
+ // The PrefValueStore provides prioritized preference values. It is owned by
+ // this PrefService. Subclasses may access it for unit testing.
+ scoped_ptr<PrefValueStore> pref_value_store_;
+
+ scoped_refptr<PrefRegistry> pref_registry_;
+
+ // Pref Stores and profile that we passed to the PrefValueStore.
+ scoped_refptr<PersistentPrefStore> user_pref_store_;
+
+ // Callback to call when a read error occurs.
+ base::Callback<void(PersistentPrefStore::PrefReadError)> read_error_callback_;
+
+ private:
+ // Hash map expected to be fastest here since it minimises expensive
+ // string comparisons. Order is unimportant, and deletions are rare.
+ // Confirmed on Android where this speeded Chrome startup by roughly 50ms
+ // vs. std::map, and by roughly 180ms vs. std::set of Preference pointers.
+ typedef base::hash_map<std::string, Preference> PreferenceMap;
+
+ // Give access to ReportUserPrefChanged() and GetMutableUserPref().
+ friend class subtle::ScopedUserPrefUpdateBase;
+ friend class PrefServiceTest_WriteablePrefStoreFlags_Test;
+
+ // Registration of pref change observers must be done using the
+ // PrefChangeRegistrar, which is declared as a friend here to grant it
+ // access to the otherwise protected members Add/RemovePrefObserver.
+ // PrefMember registers for preferences changes notification directly to
+ // avoid the storage overhead of the registrar, so its base class must be
+ // declared as a friend, too.
+ friend class PrefChangeRegistrar;
+ friend class subtle::PrefMemberBase;
+
+ // These are protected so they can only be accessed by the friend
+ // classes listed above.
+ //
+ // If the pref at the given path changes, we call the observer's
+ // OnPreferenceChanged method. Note that observers should not call
+ // these methods directly but rather use a PrefChangeRegistrar to
+ // make sure the observer gets cleaned up properly.
+ //
+ // Virtual for testing.
+ virtual void AddPrefObserver(const std::string& path, PrefObserver* obs);
+ virtual void RemovePrefObserver(const std::string& path, PrefObserver* obs);
+
+ // Sends notification of a changed preference. This needs to be called by
+ // a ScopedUserPrefUpdate if a DictionaryValue or ListValue is changed.
+ void ReportUserPrefChanged(const std::string& key);
+
+ // Sets the value for this pref path in the user pref store and informs the
+ // PrefNotifier of the change.
+ void SetUserPrefValue(const std::string& path, base::Value* new_value);
+
+ // Load preferences from storage, attempting to diagnose and handle errors.
+ // This should only be called from the constructor.
+ void InitFromStorage(bool async);
+
+ // Used to set the value of dictionary or list values in the user pref store.
+ // This will create a dictionary or list if one does not exist in the user
+ // pref store. This method returns NULL only if you're requesting an
+ // unregistered pref or a non-dict/non-list pref.
+ // |type| may only be Values::TYPE_DICTIONARY or Values::TYPE_LIST and
+ // |path| must point to a registered preference of type |type|.
+ // Ownership of the returned value remains at the user pref store.
+ base::Value* GetMutableUserPref(const std::string& path,
+ base::Value::Type type);
+
+ // GetPreferenceValue is the equivalent of FindPreference(path)->GetValue(),
+ // it has been added for performance. If is faster because it does
+ // not need to find or create a Preference object to get the
+ // value (GetValue() calls back though the preference service to
+ // actually get the value.).
+ const base::Value* GetPreferenceValue(const std::string& path) const;
+
+ // Local cache of registered Preference objects. The pref_registry_
+ // is authoritative with respect to what the types and default values
+ // of registered preferences are.
+ mutable PreferenceMap prefs_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefService);
+};
+
+#endif // COMPONENTS_PREFS_PREF_SERVICE_H_
diff --git a/chromium/components/prefs/pref_service_factory.cc b/chromium/components/prefs/pref_service_factory.cc
new file mode 100644
index 00000000000..663695da988
--- /dev/null
+++ b/chromium/components/prefs/pref_service_factory.cc
@@ -0,0 +1,61 @@
+// 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 "components/prefs/pref_service_factory.h"
+
+#include "base/bind.h"
+#include "base/sequenced_task_runner.h"
+#include "components/prefs/default_pref_store.h"
+#include "components/prefs/json_pref_store.h"
+#include "components/prefs/pref_filter.h"
+#include "components/prefs/pref_notifier_impl.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/pref_value_store.h"
+
+namespace {
+
+// Do-nothing default implementation.
+void DoNothingHandleReadError(PersistentPrefStore::PrefReadError error) {
+}
+
+} // namespace
+
+PrefServiceFactory::PrefServiceFactory()
+ : managed_prefs_(NULL),
+ supervised_user_prefs_(NULL),
+ extension_prefs_(NULL),
+ command_line_prefs_(NULL),
+ user_prefs_(NULL),
+ recommended_prefs_(NULL),
+ read_error_callback_(base::Bind(&DoNothingHandleReadError)),
+ async_(false) {}
+
+PrefServiceFactory::~PrefServiceFactory() {}
+
+void PrefServiceFactory::SetUserPrefsFile(
+ const base::FilePath& prefs_file,
+ base::SequencedTaskRunner* task_runner) {
+ user_prefs_ = new JsonPrefStore(
+ prefs_file, task_runner, scoped_ptr<PrefFilter>());
+}
+
+scoped_ptr<PrefService> PrefServiceFactory::Create(
+ PrefRegistry* pref_registry) {
+ PrefNotifierImpl* pref_notifier = new PrefNotifierImpl();
+ scoped_ptr<PrefService> pref_service(
+ new PrefService(pref_notifier,
+ new PrefValueStore(managed_prefs_.get(),
+ supervised_user_prefs_.get(),
+ extension_prefs_.get(),
+ command_line_prefs_.get(),
+ user_prefs_.get(),
+ recommended_prefs_.get(),
+ pref_registry->defaults().get(),
+ pref_notifier),
+ user_prefs_.get(),
+ pref_registry,
+ read_error_callback_,
+ async_));
+ return pref_service;
+}
diff --git a/chromium/components/prefs/pref_service_factory.h b/chromium/components/prefs/pref_service_factory.h
new file mode 100644
index 00000000000..bf7f56ceec1
--- /dev/null
+++ b/chromium/components/prefs/pref_service_factory.h
@@ -0,0 +1,89 @@
+// 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 COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_
+#define COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/persistent_pref_store.h"
+#include "components/prefs/pref_registry.h"
+#include "components/prefs/pref_store.h"
+
+class PrefService;
+
+namespace base {
+class FilePath;
+class SequencedTaskRunner;
+}
+
+// A class that allows convenient building of PrefService.
+class COMPONENTS_PREFS_EXPORT PrefServiceFactory {
+ public:
+ PrefServiceFactory();
+ virtual ~PrefServiceFactory();
+
+ // Functions for setting the various parameters of the PrefService to build.
+ void set_managed_prefs(const scoped_refptr<PrefStore>& managed_prefs) {
+ managed_prefs_ = managed_prefs;
+ }
+ void set_supervised_user_prefs(
+ const scoped_refptr<PrefStore>& supervised_user_prefs) {
+ supervised_user_prefs_ = supervised_user_prefs;
+ }
+ void set_extension_prefs(const scoped_refptr<PrefStore>& extension_prefs) {
+ extension_prefs_ = extension_prefs;
+ }
+ void set_command_line_prefs(
+ const scoped_refptr<PrefStore>& command_line_prefs) {
+ command_line_prefs_ = command_line_prefs;
+ }
+ void set_user_prefs(const scoped_refptr<PersistentPrefStore>& user_prefs) {
+ user_prefs_ = user_prefs;
+ }
+ void set_recommended_prefs(
+ const scoped_refptr<PrefStore>& recommended_prefs) {
+ recommended_prefs_ = recommended_prefs;
+ }
+
+ // Sets up error callback for the PrefService. A do-nothing default
+ // is provided if this is not called.
+ void set_read_error_callback(
+ const base::Callback<void(PersistentPrefStore::PrefReadError)>&
+ read_error_callback) {
+ read_error_callback_ = read_error_callback;
+ }
+
+ // Specifies to use an actual file-backed user pref store.
+ void SetUserPrefsFile(const base::FilePath& prefs_file,
+ base::SequencedTaskRunner* task_runner);
+
+ void set_async(bool async) {
+ async_ = async;
+ }
+
+ // Creates a PrefService object initialized with the parameters from
+ // this factory.
+ scoped_ptr<PrefService> Create(PrefRegistry* registry);
+
+ protected:
+ scoped_refptr<PrefStore> managed_prefs_;
+ scoped_refptr<PrefStore> supervised_user_prefs_;
+ scoped_refptr<PrefStore> extension_prefs_;
+ scoped_refptr<PrefStore> command_line_prefs_;
+ scoped_refptr<PersistentPrefStore> user_prefs_;
+ scoped_refptr<PrefStore> recommended_prefs_;
+
+ base::Callback<void(PersistentPrefStore::PrefReadError)> read_error_callback_;
+
+ // Defaults to false.
+ bool async_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrefServiceFactory);
+};
+
+#endif // COMPONENTS_PREFS_PREF_SERVICE_FACTORY_H_
diff --git a/chromium/components/prefs/pref_service_unittest.cc b/chromium/components/prefs/pref_service_unittest.cc
new file mode 100644
index 00000000000..77ecd199fc9
--- /dev/null
+++ b/chromium/components/prefs/pref_service_unittest.cc
@@ -0,0 +1,430 @@
+// Copyright (c) 2012 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 <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/values.h"
+#include "components/prefs/json_pref_store.h"
+#include "components/prefs/mock_pref_change_callback.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service_factory.h"
+#include "components/prefs/pref_value_store.h"
+#include "components/prefs/testing_pref_service.h"
+#include "components/prefs/testing_pref_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Mock;
+
+const char kPrefName[] = "pref.name";
+
+TEST(PrefServiceTest, NoObserverFire) {
+ TestingPrefServiceSimple prefs;
+
+ const char pref_name[] = "homepage";
+ prefs.registry()->RegisterStringPref(pref_name, std::string());
+
+ const char new_pref_value[] = "http://www.google.com/";
+ MockPrefChangeCallback obs(&prefs);
+ PrefChangeRegistrar registrar;
+ registrar.Init(&prefs);
+ registrar.Add(pref_name, obs.GetCallback());
+
+ // This should fire the checks in MockPrefChangeCallback::OnPreferenceChanged.
+ const base::StringValue expected_value(new_pref_value);
+ obs.Expect(pref_name, &expected_value);
+ prefs.SetString(pref_name, new_pref_value);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Setting the pref to the same value should not set the pref value a second
+ // time.
+ EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0);
+ prefs.SetString(pref_name, new_pref_value);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Clearing the pref should cause the pref to fire.
+ const base::StringValue expected_default_value((std::string()));
+ obs.Expect(pref_name, &expected_default_value);
+ prefs.ClearPref(pref_name);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Clearing the pref again should not cause the pref to fire.
+ EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0);
+ prefs.ClearPref(pref_name);
+ Mock::VerifyAndClearExpectations(&obs);
+}
+
+TEST(PrefServiceTest, HasPrefPath) {
+ TestingPrefServiceSimple prefs;
+
+ const char path[] = "fake.path";
+
+ // Shouldn't initially have a path.
+ EXPECT_FALSE(prefs.HasPrefPath(path));
+
+ // Register the path. This doesn't set a value, so the path still shouldn't
+ // exist.
+ prefs.registry()->RegisterStringPref(path, std::string());
+ EXPECT_FALSE(prefs.HasPrefPath(path));
+
+ // Set a value and make sure we have a path.
+ prefs.SetString(path, "blah");
+ EXPECT_TRUE(prefs.HasPrefPath(path));
+}
+
+TEST(PrefServiceTest, Observers) {
+ const char pref_name[] = "homepage";
+
+ TestingPrefServiceSimple prefs;
+ prefs.SetUserPref(pref_name,
+ new base::StringValue("http://www.cnn.com"));
+ prefs.registry()->RegisterStringPref(pref_name, std::string());
+
+ const char new_pref_value[] = "http://www.google.com/";
+ const base::StringValue expected_new_pref_value(new_pref_value);
+ MockPrefChangeCallback obs(&prefs);
+ PrefChangeRegistrar registrar;
+ registrar.Init(&prefs);
+ registrar.Add(pref_name, obs.GetCallback());
+
+ PrefChangeRegistrar registrar_two;
+ registrar_two.Init(&prefs);
+
+ // This should fire the checks in MockPrefChangeCallback::OnPreferenceChanged.
+ obs.Expect(pref_name, &expected_new_pref_value);
+ prefs.SetString(pref_name, new_pref_value);
+ Mock::VerifyAndClearExpectations(&obs);
+
+ // Now try adding a second pref observer.
+ const char new_pref_value2[] = "http://www.youtube.com/";
+ const base::StringValue expected_new_pref_value2(new_pref_value2);
+ MockPrefChangeCallback obs2(&prefs);
+ obs.Expect(pref_name, &expected_new_pref_value2);
+ obs2.Expect(pref_name, &expected_new_pref_value2);
+ registrar_two.Add(pref_name, obs2.GetCallback());
+ // This should fire the checks in obs and obs2.
+ prefs.SetString(pref_name, new_pref_value2);
+ Mock::VerifyAndClearExpectations(&obs);
+ Mock::VerifyAndClearExpectations(&obs2);
+
+ // Set a recommended value.
+ const base::StringValue recommended_pref_value("http://www.gmail.com/");
+ obs.Expect(pref_name, &expected_new_pref_value2);
+ obs2.Expect(pref_name, &expected_new_pref_value2);
+ // This should fire the checks in obs and obs2 but with an unchanged value
+ // as the recommended value is being overridden by the user-set value.
+ prefs.SetRecommendedPref(pref_name, recommended_pref_value.DeepCopy());
+ Mock::VerifyAndClearExpectations(&obs);
+ Mock::VerifyAndClearExpectations(&obs2);
+
+ // Make sure obs2 still works after removing obs.
+ registrar.Remove(pref_name);
+ EXPECT_CALL(obs, OnPreferenceChanged(_)).Times(0);
+ obs2.Expect(pref_name, &expected_new_pref_value);
+ // This should only fire the observer in obs2.
+ prefs.SetString(pref_name, new_pref_value);
+ Mock::VerifyAndClearExpectations(&obs);
+ Mock::VerifyAndClearExpectations(&obs2);
+}
+
+// Make sure that if a preference changes type, so the wrong type is stored in
+// the user pref file, it uses the correct fallback value instead.
+TEST(PrefServiceTest, GetValueChangedType) {
+ const int kTestValue = 10;
+ TestingPrefServiceSimple prefs;
+ prefs.registry()->RegisterIntegerPref(kPrefName, kTestValue);
+
+ // Check falling back to a recommended value.
+ prefs.SetUserPref(kPrefName,
+ new base::StringValue("not an integer"));
+ const PrefService::Preference* pref = prefs.FindPreference(kPrefName);
+ ASSERT_TRUE(pref);
+ const base::Value* value = pref->GetValue();
+ ASSERT_TRUE(value);
+ EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ int actual_int_value = -1;
+ EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+ EXPECT_EQ(kTestValue, actual_int_value);
+}
+
+TEST(PrefServiceTest, GetValueAndGetRecommendedValue) {
+ const int kDefaultValue = 5;
+ const int kUserValue = 10;
+ const int kRecommendedValue = 15;
+ TestingPrefServiceSimple prefs;
+ prefs.registry()->RegisterIntegerPref(kPrefName, kDefaultValue);
+
+ // Create pref with a default value only.
+ const PrefService::Preference* pref = prefs.FindPreference(kPrefName);
+ ASSERT_TRUE(pref);
+
+ // Check that GetValue() returns the default value.
+ const base::Value* value = pref->GetValue();
+ ASSERT_TRUE(value);
+ EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ int actual_int_value = -1;
+ EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+ EXPECT_EQ(kDefaultValue, actual_int_value);
+
+ // Check that GetRecommendedValue() returns no value.
+ value = pref->GetRecommendedValue();
+ ASSERT_FALSE(value);
+
+ // Set a user-set value.
+ prefs.SetUserPref(kPrefName, new base::FundamentalValue(kUserValue));
+
+ // Check that GetValue() returns the user-set value.
+ value = pref->GetValue();
+ ASSERT_TRUE(value);
+ EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ actual_int_value = -1;
+ EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+ EXPECT_EQ(kUserValue, actual_int_value);
+
+ // Check that GetRecommendedValue() returns no value.
+ value = pref->GetRecommendedValue();
+ ASSERT_FALSE(value);
+
+ // Set a recommended value.
+ prefs.SetRecommendedPref(kPrefName,
+ new base::FundamentalValue(kRecommendedValue));
+
+ // Check that GetValue() returns the user-set value.
+ value = pref->GetValue();
+ ASSERT_TRUE(value);
+ EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ actual_int_value = -1;
+ EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+ EXPECT_EQ(kUserValue, actual_int_value);
+
+ // Check that GetRecommendedValue() returns the recommended value.
+ value = pref->GetRecommendedValue();
+ ASSERT_TRUE(value);
+ EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ actual_int_value = -1;
+ EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+ EXPECT_EQ(kRecommendedValue, actual_int_value);
+
+ // Remove the user-set value.
+ prefs.RemoveUserPref(kPrefName);
+
+ // Check that GetValue() returns the recommended value.
+ value = pref->GetValue();
+ ASSERT_TRUE(value);
+ EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ actual_int_value = -1;
+ EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+ EXPECT_EQ(kRecommendedValue, actual_int_value);
+
+ // Check that GetRecommendedValue() returns the recommended value.
+ value = pref->GetRecommendedValue();
+ ASSERT_TRUE(value);
+ EXPECT_EQ(base::Value::TYPE_INTEGER, value->GetType());
+ actual_int_value = -1;
+ EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+ EXPECT_EQ(kRecommendedValue, actual_int_value);
+}
+
+// A PrefStore which just stores the last write flags that were used to write
+// values to it.
+class WriteFlagChecker : public TestingPrefStore {
+ public:
+ WriteFlagChecker() {}
+
+ void ReportValueChanged(const std::string& key, uint32_t flags) override {
+ SetLastWriteFlags(flags);
+ }
+
+ void SetValue(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) override {
+ SetLastWriteFlags(flags);
+ }
+
+ void SetValueSilently(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) override {
+ SetLastWriteFlags(flags);
+ }
+
+ void RemoveValue(const std::string& key, uint32_t flags) override {
+ SetLastWriteFlags(flags);
+ }
+
+ uint32_t GetLastFlagsAndClear() {
+ CHECK(last_write_flags_set_);
+ uint32_t result = last_write_flags_;
+ last_write_flags_set_ = false;
+ last_write_flags_ = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS;
+ return result;
+ }
+
+ bool last_write_flags_set() { return last_write_flags_set_; }
+
+ private:
+ ~WriteFlagChecker() override {}
+
+ void SetLastWriteFlags(uint32_t flags) {
+ CHECK(!last_write_flags_set_);
+ last_write_flags_set_ = true;
+ last_write_flags_ = flags;
+ }
+
+ bool last_write_flags_set_ = false;
+ uint32_t last_write_flags_ = WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS;
+};
+
+TEST(PrefServiceTest, WriteablePrefStoreFlags) {
+ scoped_refptr<WriteFlagChecker> flag_checker(new WriteFlagChecker);
+ scoped_refptr<PrefRegistrySimple> registry(new PrefRegistrySimple);
+ PrefServiceFactory factory;
+ factory.set_user_prefs(flag_checker);
+ scoped_ptr<PrefService> prefs(factory.Create(registry.get()));
+
+ // The first 8 bits of write flags are reserved for subclasses. Create a
+ // custom flag in this range
+ uint32_t kCustomRegistrationFlag = 1 << 2;
+
+ // A map of the registration flags that will be tested and the write flags
+ // they are expected to convert to.
+ struct RegistrationToWriteFlags {
+ const char* pref_name;
+ uint32_t registration_flags;
+ uint32_t write_flags;
+ };
+ const RegistrationToWriteFlags kRegistrationToWriteFlags[] = {
+ {"none",
+ PrefRegistry::NO_REGISTRATION_FLAGS,
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS},
+ {"lossy",
+ PrefRegistry::LOSSY_PREF,
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG},
+ {"custom",
+ kCustomRegistrationFlag,
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS},
+ {"lossyandcustom",
+ PrefRegistry::LOSSY_PREF | kCustomRegistrationFlag,
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG}};
+
+ for (size_t i = 0; i < arraysize(kRegistrationToWriteFlags); ++i) {
+ RegistrationToWriteFlags entry = kRegistrationToWriteFlags[i];
+ registry->RegisterDictionaryPref(
+ entry.pref_name, new base::DictionaryValue(), entry.registration_flags);
+
+ SCOPED_TRACE("Currently testing pref with name: " +
+ std::string(entry.pref_name));
+
+ prefs->GetMutableUserPref(entry.pref_name, base::Value::TYPE_DICTIONARY);
+ EXPECT_TRUE(flag_checker->last_write_flags_set());
+ EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear());
+
+ prefs->ReportUserPrefChanged(entry.pref_name);
+ EXPECT_TRUE(flag_checker->last_write_flags_set());
+ EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear());
+
+ prefs->ClearPref(entry.pref_name);
+ EXPECT_TRUE(flag_checker->last_write_flags_set());
+ EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear());
+
+ prefs->SetUserPrefValue(entry.pref_name, new base::DictionaryValue());
+ EXPECT_TRUE(flag_checker->last_write_flags_set());
+ EXPECT_EQ(entry.write_flags, flag_checker->GetLastFlagsAndClear());
+ }
+}
+
+class PrefServiceSetValueTest : public testing::Test {
+ protected:
+ static const char kName[];
+ static const char kValue[];
+
+ PrefServiceSetValueTest() : observer_(&prefs_) {}
+
+ TestingPrefServiceSimple prefs_;
+ MockPrefChangeCallback observer_;
+};
+
+const char PrefServiceSetValueTest::kName[] = "name";
+const char PrefServiceSetValueTest::kValue[] = "value";
+
+TEST_F(PrefServiceSetValueTest, SetStringValue) {
+ const char default_string[] = "default";
+ const base::StringValue default_value(default_string);
+ prefs_.registry()->RegisterStringPref(kName, default_string);
+
+ PrefChangeRegistrar registrar;
+ registrar.Init(&prefs_);
+ registrar.Add(kName, observer_.GetCallback());
+
+ // Changing the controlling store from default to user triggers notification.
+ observer_.Expect(kName, &default_value);
+ prefs_.Set(kName, default_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+ prefs_.Set(kName, default_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ base::StringValue new_value(kValue);
+ observer_.Expect(kName, &new_value);
+ prefs_.Set(kName, new_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+}
+
+TEST_F(PrefServiceSetValueTest, SetDictionaryValue) {
+ prefs_.registry()->RegisterDictionaryPref(kName);
+ PrefChangeRegistrar registrar;
+ registrar.Init(&prefs_);
+ registrar.Add(kName, observer_.GetCallback());
+
+ EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+ prefs_.RemoveUserPref(kName);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ base::DictionaryValue new_value;
+ new_value.SetString(kName, kValue);
+ observer_.Expect(kName, &new_value);
+ prefs_.Set(kName, new_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+ prefs_.Set(kName, new_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ base::DictionaryValue empty;
+ observer_.Expect(kName, &empty);
+ prefs_.Set(kName, empty);
+ Mock::VerifyAndClearExpectations(&observer_);
+}
+
+TEST_F(PrefServiceSetValueTest, SetListValue) {
+ prefs_.registry()->RegisterListPref(kName);
+ PrefChangeRegistrar registrar;
+ registrar.Init(&prefs_);
+ registrar.Add(kName, observer_.GetCallback());
+
+ EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+ prefs_.RemoveUserPref(kName);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ base::ListValue new_value;
+ new_value.Append(new base::StringValue(kValue));
+ observer_.Expect(kName, &new_value);
+ prefs_.Set(kName, new_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+ prefs_.Set(kName, new_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ base::ListValue empty;
+ observer_.Expect(kName, &empty);
+ prefs_.Set(kName, empty);
+ Mock::VerifyAndClearExpectations(&observer_);
+}
diff --git a/chromium/components/prefs/pref_store.cc b/chromium/components/prefs/pref_store.cc
new file mode 100644
index 00000000000..2ae0afcb08d
--- /dev/null
+++ b/chromium/components/prefs/pref_store.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2012 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 "components/prefs/pref_store.h"
+
+bool PrefStore::HasObservers() const {
+ return false;
+}
+
+bool PrefStore::IsInitializationComplete() const {
+ return true;
+}
diff --git a/chromium/components/prefs/pref_store.h b/chromium/components/prefs/pref_store.h
new file mode 100644
index 00000000000..ef2bc494d15
--- /dev/null
+++ b/chromium/components/prefs/pref_store.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_PREF_STORE_H_
+#define COMPONENTS_PREFS_PREF_STORE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "components/prefs/base_prefs_export.h"
+
+namespace base {
+class Value;
+}
+
+// This is an abstract interface for reading and writing from/to a persistent
+// preference store, used by PrefService. An implementation using a JSON file
+// can be found in JsonPrefStore, while an implementation without any backing
+// store for testing can be found in TestingPrefStore. Furthermore, there is
+// CommandLinePrefStore, which bridges command line options to preferences and
+// ConfigurationPolicyPrefStore, which is used for hooking up configuration
+// policy with the preference subsystem.
+class COMPONENTS_PREFS_EXPORT PrefStore : public base::RefCounted<PrefStore> {
+ public:
+ // Observer interface for monitoring PrefStore.
+ class COMPONENTS_PREFS_EXPORT Observer {
+ public:
+ // Called when the value for the given |key| in the store changes.
+ virtual void OnPrefValueChanged(const std::string& key) = 0;
+ // Notification about the PrefStore being fully initialized.
+ virtual void OnInitializationCompleted(bool succeeded) = 0;
+
+ protected:
+ virtual ~Observer() {}
+ };
+
+ PrefStore() {}
+
+ // Add and remove observers.
+ virtual void AddObserver(Observer* observer) {}
+ virtual void RemoveObserver(Observer* observer) {}
+ virtual bool HasObservers() const;
+
+ // Whether the store has completed all asynchronous initialization.
+ virtual bool IsInitializationComplete() const;
+
+ // Get the value for a given preference |key| and stores it in |*result|.
+ // |*result| is only modified if the return value is true and if |result|
+ // is not NULL. Ownership of the |*result| value remains with the PrefStore.
+ virtual bool GetValue(const std::string& key,
+ const base::Value** result) const = 0;
+
+ protected:
+ friend class base::RefCounted<PrefStore>;
+ virtual ~PrefStore() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrefStore);
+};
+
+#endif // COMPONENTS_PREFS_PREF_STORE_H_
diff --git a/chromium/components/prefs/pref_store_observer_mock.cc b/chromium/components/prefs/pref_store_observer_mock.cc
new file mode 100644
index 00000000000..a03c8580056
--- /dev/null
+++ b/chromium/components/prefs/pref_store_observer_mock.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 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 "components/prefs/pref_store_observer_mock.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+PrefStoreObserverMock::PrefStoreObserverMock()
+ : initialized(false), initialization_success(false) {}
+
+PrefStoreObserverMock::~PrefStoreObserverMock() {}
+
+void PrefStoreObserverMock::VerifyAndResetChangedKey(
+ const std::string& expected) {
+ EXPECT_EQ(1u, changed_keys.size());
+ if (changed_keys.size() >= 1)
+ EXPECT_EQ(expected, changed_keys.front());
+ changed_keys.clear();
+}
+
+void PrefStoreObserverMock::OnPrefValueChanged(const std::string& key) {
+ changed_keys.push_back(key);
+}
+
+void PrefStoreObserverMock::OnInitializationCompleted(bool success) {
+ initialized = true;
+ initialization_success = success;
+}
diff --git a/chromium/components/prefs/pref_store_observer_mock.h b/chromium/components/prefs/pref_store_observer_mock.h
new file mode 100644
index 00000000000..01bd6a6d966
--- /dev/null
+++ b/chromium/components/prefs/pref_store_observer_mock.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2011 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 COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_
+#define COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_
+
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "components/prefs/pref_store.h"
+
+// A mock implementation of PrefStore::Observer.
+class PrefStoreObserverMock : public PrefStore::Observer {
+ public:
+ PrefStoreObserverMock();
+ ~PrefStoreObserverMock() override;
+
+ void VerifyAndResetChangedKey(const std::string& expected);
+
+ // PrefStore::Observer implementation
+ void OnPrefValueChanged(const std::string& key) override;
+ void OnInitializationCompleted(bool success) override;
+
+ std::vector<std::string> changed_keys;
+ bool initialized;
+ bool initialization_success; // Only valid if |initialized|.
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrefStoreObserverMock);
+};
+
+#endif // COMPONENTS_PREFS_PREF_STORE_OBSERVER_MOCK_H_
diff --git a/chromium/components/prefs/pref_value_map.cc b/chromium/components/prefs/pref_value_map.cc
new file mode 100644
index 00000000000..7b267cc7d16
--- /dev/null
+++ b/chromium/components/prefs/pref_value_map.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2011 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 "components/prefs/pref_value_map.h"
+
+#include <map>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/stl_util.h"
+#include "base/values.h"
+
+PrefValueMap::PrefValueMap() {}
+
+PrefValueMap::~PrefValueMap() {}
+
+bool PrefValueMap::GetValue(const std::string& key,
+ const base::Value** value) const {
+ const base::Value* got_value = prefs_.get(key);
+ if (value && got_value)
+ *value = got_value;
+
+ return !!got_value;
+}
+
+bool PrefValueMap::GetValue(const std::string& key, base::Value** value) {
+ base::Value* got_value = prefs_.get(key);
+ if (value && got_value)
+ *value = got_value;
+
+ return !!got_value;
+}
+
+bool PrefValueMap::SetValue(const std::string& key,
+ scoped_ptr<base::Value> value) {
+ DCHECK(value);
+
+ base::Value* old_value = prefs_.get(key);
+ if (old_value && value->Equals(old_value))
+ return false;
+
+ prefs_.set(key, std::move(value));
+ return true;
+}
+
+bool PrefValueMap::RemoveValue(const std::string& key) {
+ return prefs_.erase(key) != 0;
+}
+
+void PrefValueMap::Clear() {
+ prefs_.clear();
+}
+
+void PrefValueMap::Swap(PrefValueMap* other) {
+ prefs_.swap(other->prefs_);
+}
+
+PrefValueMap::iterator PrefValueMap::begin() {
+ return prefs_.begin();
+}
+
+PrefValueMap::iterator PrefValueMap::end() {
+ return prefs_.end();
+}
+
+PrefValueMap::const_iterator PrefValueMap::begin() const {
+ return prefs_.begin();
+}
+
+PrefValueMap::const_iterator PrefValueMap::end() const {
+ return prefs_.end();
+}
+
+bool PrefValueMap::GetBoolean(const std::string& key,
+ bool* value) const {
+ const base::Value* stored_value = nullptr;
+ return GetValue(key, &stored_value) && stored_value->GetAsBoolean(value);
+}
+
+void PrefValueMap::SetBoolean(const std::string& key, bool value) {
+ SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)));
+}
+
+bool PrefValueMap::GetString(const std::string& key,
+ std::string* value) const {
+ const base::Value* stored_value = nullptr;
+ return GetValue(key, &stored_value) && stored_value->GetAsString(value);
+}
+
+void PrefValueMap::SetString(const std::string& key,
+ const std::string& value) {
+ SetValue(key, make_scoped_ptr(new base::StringValue(value)));
+}
+
+bool PrefValueMap::GetInteger(const std::string& key, int* value) const {
+ const base::Value* stored_value = nullptr;
+ return GetValue(key, &stored_value) && stored_value->GetAsInteger(value);
+}
+
+void PrefValueMap::SetInteger(const std::string& key, const int value) {
+ SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)));
+}
+
+void PrefValueMap::SetDouble(const std::string& key, const double value) {
+ SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)));
+}
+
+void PrefValueMap::GetDifferingKeys(
+ const PrefValueMap* other,
+ std::vector<std::string>* differing_keys) const {
+ differing_keys->clear();
+
+ // Put everything into ordered maps.
+ std::map<std::string, base::Value*> this_prefs(prefs_.begin(), prefs_.end());
+ std::map<std::string, base::Value*> other_prefs(other->prefs_.begin(),
+ other->prefs_.end());
+
+ // Walk over the maps in lockstep, adding everything that is different.
+ auto this_pref(this_prefs.begin());
+ auto other_pref(other_prefs.begin());
+ while (this_pref != this_prefs.end() && other_pref != other_prefs.end()) {
+ const int diff = this_pref->first.compare(other_pref->first);
+ if (diff == 0) {
+ if (!this_pref->second->Equals(other_pref->second))
+ differing_keys->push_back(this_pref->first);
+ ++this_pref;
+ ++other_pref;
+ } else if (diff < 0) {
+ differing_keys->push_back(this_pref->first);
+ ++this_pref;
+ } else if (diff > 0) {
+ differing_keys->push_back(other_pref->first);
+ ++other_pref;
+ }
+ }
+
+ // Add the remaining entries.
+ for ( ; this_pref != this_prefs.end(); ++this_pref)
+ differing_keys->push_back(this_pref->first);
+ for ( ; other_pref != other_prefs.end(); ++other_pref)
+ differing_keys->push_back(other_pref->first);
+}
diff --git a/chromium/components/prefs/pref_value_map.h b/chromium/components/prefs/pref_value_map.h
new file mode 100644
index 00000000000..988a6389197
--- /dev/null
+++ b/chromium/components/prefs/pref_value_map.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2011 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 COMPONENTS_PREFS_PREF_VALUE_MAP_H_
+#define COMPONENTS_PREFS_PREF_VALUE_MAP_H_
+
+#include <string>
+#include <vector>
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/prefs/base_prefs_export.h"
+
+namespace base {
+class Value;
+}
+
+// A generic string to value map used by the PrefStore implementations.
+class COMPONENTS_PREFS_EXPORT PrefValueMap {
+ public:
+ using Map = base::ScopedPtrHashMap<std::string, scoped_ptr<base::Value>>;
+ using iterator = Map::iterator;
+ using const_iterator = Map::const_iterator;
+
+ PrefValueMap();
+ virtual ~PrefValueMap();
+
+ // Gets the value for |key| and stores it in |value|. Ownership remains with
+ // the map. Returns true if a value is present. If not, |value| is not
+ // touched.
+ bool GetValue(const std::string& key, const base::Value** value) const;
+ bool GetValue(const std::string& key, base::Value** value);
+
+ // Sets a new |value| for |key|. |value| must be non-null. Returns true if the
+ // value changed.
+ bool SetValue(const std::string& key, scoped_ptr<base::Value> value);
+
+ // Removes the value for |key| from the map. Returns true if a value was
+ // removed.
+ bool RemoveValue(const std::string& key);
+
+ // Clears the map.
+ void Clear();
+
+ // Swaps the contents of two maps.
+ void Swap(PrefValueMap* other);
+
+ iterator begin();
+ iterator end();
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ // Gets a boolean value for |key| and stores it in |value|. Returns true if
+ // the value was found and of the proper type.
+ bool GetBoolean(const std::string& key, bool* value) const;
+
+ // Sets the value for |key| to the boolean |value|.
+ void SetBoolean(const std::string& key, bool value);
+
+ // Gets a string value for |key| and stores it in |value|. Returns true if
+ // the value was found and of the proper type.
+ bool GetString(const std::string& key, std::string* value) const;
+
+ // Sets the value for |key| to the string |value|.
+ void SetString(const std::string& key, const std::string& value);
+
+ // Gets an int value for |key| and stores it in |value|. Returns true if
+ // the value was found and of the proper type.
+ bool GetInteger(const std::string& key, int* value) const;
+
+ // Sets the value for |key| to the int |value|.
+ void SetInteger(const std::string& key, const int value);
+
+ // Sets the value for |key| to the double |value|.
+ void SetDouble(const std::string& key, const double value);
+
+ // Compares this value map against |other| and stores all key names that have
+ // different values in |differing_keys|. This includes keys that are present
+ // only in one of the maps.
+ void GetDifferingKeys(const PrefValueMap* other,
+ std::vector<std::string>* differing_keys) const;
+
+ private:
+ Map prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefValueMap);
+};
+
+#endif // COMPONENTS_PREFS_PREF_VALUE_MAP_H_
diff --git a/chromium/components/prefs/pref_value_map_unittest.cc b/chromium/components/prefs/pref_value_map_unittest.cc
new file mode 100644
index 00000000000..04001a219ea
--- /dev/null
+++ b/chromium/components/prefs/pref_value_map_unittest.cc
@@ -0,0 +1,135 @@
+// Copyright (c) 2011 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 "components/prefs/pref_value_map.h"
+
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TEST(PrefValueMapTest, SetValue) {
+ PrefValueMap map;
+ const Value* result = NULL;
+ EXPECT_FALSE(map.GetValue("key", &result));
+ EXPECT_FALSE(result);
+
+ EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_FALSE(map.SetValue("key", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("hi mom!"))));
+
+ EXPECT_TRUE(map.GetValue("key", &result));
+ EXPECT_TRUE(StringValue("hi mom!").Equals(result));
+}
+
+TEST(PrefValueMapTest, GetAndSetIntegerValue) {
+ PrefValueMap map;
+ ASSERT_TRUE(map.SetValue("key", make_scoped_ptr(new FundamentalValue(5))));
+
+ int int_value = 0;
+ EXPECT_TRUE(map.GetInteger("key", &int_value));
+ EXPECT_EQ(5, int_value);
+
+ map.SetInteger("key", -14);
+ EXPECT_TRUE(map.GetInteger("key", &int_value));
+ EXPECT_EQ(-14, int_value);
+}
+
+TEST(PrefValueMapTest, SetDoubleValue) {
+ PrefValueMap map;
+ ASSERT_TRUE(map.SetValue("key", make_scoped_ptr(new FundamentalValue(5.5))));
+
+ const Value* result = NULL;
+ ASSERT_TRUE(map.GetValue("key", &result));
+ double double_value = 0.;
+ EXPECT_TRUE(result->GetAsDouble(&double_value));
+ EXPECT_DOUBLE_EQ(5.5, double_value);
+}
+
+TEST(PrefValueMapTest, RemoveValue) {
+ PrefValueMap map;
+ EXPECT_FALSE(map.RemoveValue("key"));
+
+ EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(map.GetValue("key", NULL));
+
+ EXPECT_TRUE(map.RemoveValue("key"));
+ EXPECT_FALSE(map.GetValue("key", NULL));
+
+ EXPECT_FALSE(map.RemoveValue("key"));
+}
+
+TEST(PrefValueMapTest, Clear) {
+ PrefValueMap map;
+ EXPECT_TRUE(map.SetValue("key", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(map.GetValue("key", NULL));
+
+ map.Clear();
+
+ EXPECT_FALSE(map.GetValue("key", NULL));
+}
+
+TEST(PrefValueMapTest, GetDifferingKeys) {
+ PrefValueMap reference;
+ EXPECT_TRUE(
+ reference.SetValue("b", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ reference.SetValue("c", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ reference.SetValue("e", make_scoped_ptr(new StringValue("test"))));
+
+ PrefValueMap check;
+ std::vector<std::string> differing_paths;
+ std::vector<std::string> expected_differing_paths;
+
+ reference.GetDifferingKeys(&check, &differing_paths);
+ expected_differing_paths.push_back("b");
+ expected_differing_paths.push_back("c");
+ expected_differing_paths.push_back("e");
+ EXPECT_EQ(expected_differing_paths, differing_paths);
+
+ EXPECT_TRUE(check.SetValue("a", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(check.SetValue("c", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(check.SetValue("d", make_scoped_ptr(new StringValue("test"))));
+
+ reference.GetDifferingKeys(&check, &differing_paths);
+ expected_differing_paths.clear();
+ expected_differing_paths.push_back("a");
+ expected_differing_paths.push_back("b");
+ expected_differing_paths.push_back("d");
+ expected_differing_paths.push_back("e");
+ EXPECT_EQ(expected_differing_paths, differing_paths);
+}
+
+TEST(PrefValueMapTest, SwapTwoMaps) {
+ PrefValueMap first_map;
+ EXPECT_TRUE(
+ first_map.SetValue("a", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ first_map.SetValue("b", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ first_map.SetValue("c", make_scoped_ptr(new StringValue("test"))));
+
+ PrefValueMap second_map;
+ EXPECT_TRUE(
+ second_map.SetValue("d", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ second_map.SetValue("e", make_scoped_ptr(new StringValue("test"))));
+ EXPECT_TRUE(
+ second_map.SetValue("f", make_scoped_ptr(new StringValue("test"))));
+
+ first_map.Swap(&second_map);
+
+ EXPECT_TRUE(first_map.GetValue("d", NULL));
+ EXPECT_TRUE(first_map.GetValue("e", NULL));
+ EXPECT_TRUE(first_map.GetValue("f", NULL));
+
+ EXPECT_TRUE(second_map.GetValue("a", NULL));
+ EXPECT_TRUE(second_map.GetValue("b", NULL));
+ EXPECT_TRUE(second_map.GetValue("c", NULL));
+}
+
+} // namespace
+} // namespace base
diff --git a/chromium/components/prefs/pref_value_store.cc b/chromium/components/prefs/pref_value_store.cc
new file mode 100644
index 00000000000..4c7a4e5d9cf
--- /dev/null
+++ b/chromium/components/prefs/pref_value_store.cc
@@ -0,0 +1,290 @@
+// Copyright (c) 2012 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 "components/prefs/pref_value_store.h"
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "components/prefs/pref_notifier.h"
+#include "components/prefs/pref_observer.h"
+
+PrefValueStore::PrefStoreKeeper::PrefStoreKeeper()
+ : pref_value_store_(NULL),
+ type_(PrefValueStore::INVALID_STORE) {
+}
+
+PrefValueStore::PrefStoreKeeper::~PrefStoreKeeper() {
+ if (pref_store_.get()) {
+ pref_store_->RemoveObserver(this);
+ pref_store_ = NULL;
+ }
+ pref_value_store_ = NULL;
+}
+
+void PrefValueStore::PrefStoreKeeper::Initialize(
+ PrefValueStore* store,
+ PrefStore* pref_store,
+ PrefValueStore::PrefStoreType type) {
+ if (pref_store_.get()) {
+ pref_store_->RemoveObserver(this);
+ DCHECK(!pref_store_->HasObservers());
+ }
+ type_ = type;
+ pref_value_store_ = store;
+ pref_store_ = pref_store;
+ if (pref_store_.get())
+ pref_store_->AddObserver(this);
+}
+
+void PrefValueStore::PrefStoreKeeper::OnPrefValueChanged(
+ const std::string& key) {
+ pref_value_store_->OnPrefValueChanged(type_, key);
+}
+
+void PrefValueStore::PrefStoreKeeper::OnInitializationCompleted(
+ bool succeeded) {
+ pref_value_store_->OnInitializationCompleted(type_, succeeded);
+}
+
+PrefValueStore::PrefValueStore(PrefStore* managed_prefs,
+ PrefStore* supervised_user_prefs,
+ PrefStore* extension_prefs,
+ PrefStore* command_line_prefs,
+ PrefStore* user_prefs,
+ PrefStore* recommended_prefs,
+ PrefStore* default_prefs,
+ PrefNotifier* pref_notifier)
+ : pref_notifier_(pref_notifier),
+ initialization_failed_(false) {
+ InitPrefStore(MANAGED_STORE, managed_prefs);
+ InitPrefStore(SUPERVISED_USER_STORE, supervised_user_prefs);
+ InitPrefStore(EXTENSION_STORE, extension_prefs);
+ InitPrefStore(COMMAND_LINE_STORE, command_line_prefs);
+ InitPrefStore(USER_STORE, user_prefs);
+ InitPrefStore(RECOMMENDED_STORE, recommended_prefs);
+ InitPrefStore(DEFAULT_STORE, default_prefs);
+
+ CheckInitializationCompleted();
+}
+
+PrefValueStore::~PrefValueStore() {}
+
+PrefValueStore* PrefValueStore::CloneAndSpecialize(
+ PrefStore* managed_prefs,
+ PrefStore* supervised_user_prefs,
+ PrefStore* extension_prefs,
+ PrefStore* command_line_prefs,
+ PrefStore* user_prefs,
+ PrefStore* recommended_prefs,
+ PrefStore* default_prefs,
+ PrefNotifier* pref_notifier) {
+ DCHECK(pref_notifier);
+ if (!managed_prefs)
+ managed_prefs = GetPrefStore(MANAGED_STORE);
+ if (!supervised_user_prefs)
+ supervised_user_prefs = GetPrefStore(SUPERVISED_USER_STORE);
+ if (!extension_prefs)
+ extension_prefs = GetPrefStore(EXTENSION_STORE);
+ if (!command_line_prefs)
+ command_line_prefs = GetPrefStore(COMMAND_LINE_STORE);
+ if (!user_prefs)
+ user_prefs = GetPrefStore(USER_STORE);
+ if (!recommended_prefs)
+ recommended_prefs = GetPrefStore(RECOMMENDED_STORE);
+ if (!default_prefs)
+ default_prefs = GetPrefStore(DEFAULT_STORE);
+
+ return new PrefValueStore(
+ managed_prefs, supervised_user_prefs, extension_prefs, command_line_prefs,
+ user_prefs, recommended_prefs, default_prefs, pref_notifier);
+}
+
+void PrefValueStore::set_callback(const PrefChangedCallback& callback) {
+ pref_changed_callback_ = callback;
+}
+
+bool PrefValueStore::GetValue(const std::string& name,
+ base::Value::Type type,
+ const base::Value** out_value) const {
+ // Check the |PrefStore|s in order of their priority from highest to lowest,
+ // looking for the first preference value with the given |name| and |type|.
+ for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) {
+ if (GetValueFromStoreWithType(name, type, static_cast<PrefStoreType>(i),
+ out_value))
+ return true;
+ }
+ return false;
+}
+
+bool PrefValueStore::GetRecommendedValue(const std::string& name,
+ base::Value::Type type,
+ const base::Value** out_value) const {
+ return GetValueFromStoreWithType(name, type, RECOMMENDED_STORE, out_value);
+}
+
+void PrefValueStore::NotifyPrefChanged(
+ const std::string& path,
+ PrefValueStore::PrefStoreType new_store) {
+ DCHECK(new_store != INVALID_STORE);
+ // A notification is sent when the pref value in any store changes. If this
+ // store is currently being overridden by a higher-priority store, the
+ // effective value of the pref will not have changed.
+ pref_notifier_->OnPreferenceChanged(path);
+ if (!pref_changed_callback_.is_null())
+ pref_changed_callback_.Run(path);
+}
+
+bool PrefValueStore::PrefValueInManagedStore(const std::string& name) const {
+ return PrefValueInStore(name, MANAGED_STORE);
+}
+
+bool PrefValueStore::PrefValueInSupervisedStore(const std::string& name) const {
+ return PrefValueInStore(name, SUPERVISED_USER_STORE);
+}
+
+bool PrefValueStore::PrefValueInExtensionStore(const std::string& name) const {
+ return PrefValueInStore(name, EXTENSION_STORE);
+}
+
+bool PrefValueStore::PrefValueInUserStore(const std::string& name) const {
+ return PrefValueInStore(name, USER_STORE);
+}
+
+bool PrefValueStore::PrefValueFromExtensionStore(
+ const std::string& name) const {
+ return ControllingPrefStoreForPref(name) == EXTENSION_STORE;
+}
+
+bool PrefValueStore::PrefValueFromUserStore(const std::string& name) const {
+ return ControllingPrefStoreForPref(name) == USER_STORE;
+}
+
+bool PrefValueStore::PrefValueFromRecommendedStore(
+ const std::string& name) const {
+ return ControllingPrefStoreForPref(name) == RECOMMENDED_STORE;
+}
+
+bool PrefValueStore::PrefValueFromDefaultStore(const std::string& name) const {
+ return ControllingPrefStoreForPref(name) == DEFAULT_STORE;
+}
+
+bool PrefValueStore::PrefValueUserModifiable(const std::string& name) const {
+ PrefStoreType effective_store = ControllingPrefStoreForPref(name);
+ return effective_store >= USER_STORE ||
+ effective_store == INVALID_STORE;
+}
+
+bool PrefValueStore::PrefValueExtensionModifiable(
+ const std::string& name) const {
+ PrefStoreType effective_store = ControllingPrefStoreForPref(name);
+ return effective_store >= EXTENSION_STORE ||
+ effective_store == INVALID_STORE;
+}
+
+void PrefValueStore::UpdateCommandLinePrefStore(PrefStore* command_line_prefs) {
+ InitPrefStore(COMMAND_LINE_STORE, command_line_prefs);
+}
+
+bool PrefValueStore::PrefValueInStore(
+ const std::string& name,
+ PrefValueStore::PrefStoreType store) const {
+ // Declare a temp Value* and call GetValueFromStore,
+ // ignoring the output value.
+ const base::Value* tmp_value = NULL;
+ return GetValueFromStore(name, store, &tmp_value);
+}
+
+bool PrefValueStore::PrefValueInStoreRange(
+ const std::string& name,
+ PrefValueStore::PrefStoreType first_checked_store,
+ PrefValueStore::PrefStoreType last_checked_store) const {
+ if (first_checked_store > last_checked_store) {
+ NOTREACHED();
+ return false;
+ }
+
+ for (size_t i = first_checked_store;
+ i <= static_cast<size_t>(last_checked_store); ++i) {
+ if (PrefValueInStore(name, static_cast<PrefStoreType>(i)))
+ return true;
+ }
+ return false;
+}
+
+PrefValueStore::PrefStoreType PrefValueStore::ControllingPrefStoreForPref(
+ const std::string& name) const {
+ for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) {
+ if (PrefValueInStore(name, static_cast<PrefStoreType>(i)))
+ return static_cast<PrefStoreType>(i);
+ }
+ return INVALID_STORE;
+}
+
+bool PrefValueStore::GetValueFromStore(const std::string& name,
+ PrefValueStore::PrefStoreType store_type,
+ const base::Value** out_value) const {
+ // Only return true if we find a value and it is the correct type, so stale
+ // values with the incorrect type will be ignored.
+ const PrefStore* store = GetPrefStore(static_cast<PrefStoreType>(store_type));
+ if (store && store->GetValue(name, out_value))
+ return true;
+
+ // No valid value found for the given preference name: set the return value
+ // to false.
+ *out_value = NULL;
+ return false;
+}
+
+bool PrefValueStore::GetValueFromStoreWithType(
+ const std::string& name,
+ base::Value::Type type,
+ PrefStoreType store,
+ const base::Value** out_value) const {
+ if (GetValueFromStore(name, store, out_value)) {
+ if ((*out_value)->IsType(type))
+ return true;
+
+ LOG(WARNING) << "Expected type for " << name << " is " << type
+ << " but got " << (*out_value)->GetType()
+ << " in store " << store;
+ }
+
+ *out_value = NULL;
+ return false;
+}
+
+void PrefValueStore::OnPrefValueChanged(PrefValueStore::PrefStoreType type,
+ const std::string& key) {
+ NotifyPrefChanged(key, type);
+}
+
+void PrefValueStore::OnInitializationCompleted(
+ PrefValueStore::PrefStoreType type, bool succeeded) {
+ if (initialization_failed_)
+ return;
+ if (!succeeded) {
+ initialization_failed_ = true;
+ pref_notifier_->OnInitializationCompleted(false);
+ return;
+ }
+ CheckInitializationCompleted();
+}
+
+void PrefValueStore::InitPrefStore(PrefValueStore::PrefStoreType type,
+ PrefStore* pref_store) {
+ pref_stores_[type].Initialize(this, pref_store, type);
+}
+
+void PrefValueStore::CheckInitializationCompleted() {
+ if (initialization_failed_)
+ return;
+ for (size_t i = 0; i <= PREF_STORE_TYPE_MAX; ++i) {
+ scoped_refptr<PrefStore> store =
+ GetPrefStore(static_cast<PrefStoreType>(i));
+ if (store.get() && !store->IsInitializationComplete())
+ return;
+ }
+ pref_notifier_->OnInitializationCompleted(true);
+}
diff --git a/chromium/components/prefs/pref_value_store.h b/chromium/components/prefs/pref_value_store.h
new file mode 100644
index 00000000000..39b6bf1870e
--- /dev/null
+++ b/chromium/components/prefs/pref_value_store.h
@@ -0,0 +1,260 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_PREF_VALUE_STORE_H_
+#define COMPONENTS_PREFS_PREF_VALUE_STORE_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/pref_store.h"
+
+class PrefNotifier;
+class PrefStore;
+
+// The PrefValueStore manages various sources of values for Preferences
+// (e.g., configuration policies, extensions, and user settings). It returns
+// the value of a Preference from the source with the highest priority, and
+// allows setting user-defined values for preferences that are not managed.
+//
+// Unless otherwise explicitly noted, all of the methods of this class must
+// be called on the UI thread.
+class COMPONENTS_PREFS_EXPORT PrefValueStore {
+ public:
+ typedef base::Callback<void(const std::string&)> PrefChangedCallback;
+
+ // In decreasing order of precedence:
+ // |managed_prefs| contains all preferences from mandatory policies.
+ // |supervised_user_prefs| contains all preferences from supervised user
+ // settings, i.e. settings configured for a supervised user by their
+ // custodian.
+ // |extension_prefs| contains preference values set by extensions.
+ // |command_line_prefs| contains preference values set by command-line
+ // switches.
+ // |user_prefs| contains all user-set preference values.
+ // |recommended_prefs| contains all preferences from recommended policies.
+ // |default_prefs| contains application-default preference values. It must
+ // be non-null if any preferences are to be registered.
+ //
+ // |pref_notifier| facilitates broadcasting preference change notifications
+ // to the world.
+ PrefValueStore(PrefStore* managed_prefs,
+ PrefStore* supervised_user_prefs,
+ PrefStore* extension_prefs,
+ PrefStore* command_line_prefs,
+ PrefStore* user_prefs,
+ PrefStore* recommended_prefs,
+ PrefStore* default_prefs,
+ PrefNotifier* pref_notifier);
+ virtual ~PrefValueStore();
+
+ // Creates a clone of this PrefValueStore with PrefStores overwritten
+ // by the parameters passed, if unequal NULL.
+ PrefValueStore* CloneAndSpecialize(PrefStore* managed_prefs,
+ PrefStore* supervised_user_prefs,
+ PrefStore* extension_prefs,
+ PrefStore* command_line_prefs,
+ PrefStore* user_prefs,
+ PrefStore* recommended_prefs,
+ PrefStore* default_prefs,
+ PrefNotifier* pref_notifier);
+
+ // A PrefValueStore can have exactly one callback that is directly
+ // notified of preferences changing in the store. This does not
+ // filter through the PrefNotifier mechanism, which may not forward
+ // certain changes (e.g. unregistered prefs).
+ void set_callback(const PrefChangedCallback& callback);
+
+ // Gets the value for the given preference name that has the specified value
+ // type. Values stored in a PrefStore that have the matching |name| but
+ // a non-matching |type| are silently skipped. Returns true if a valid value
+ // was found in any of the available PrefStores. Most callers should use
+ // Preference::GetValue() instead of calling this method directly.
+ bool GetValue(const std::string& name,
+ base::Value::Type type,
+ const base::Value** out_value) const;
+
+ // Gets the recommended value for the given preference name that has the
+ // specified value type. A value stored in the recommended PrefStore that has
+ // the matching |name| but a non-matching |type| is silently ignored. Returns
+ // true if a valid value was found. Most callers should use
+ // Preference::GetRecommendedValue() instead of calling this method directly.
+ bool GetRecommendedValue(const std::string& name,
+ base::Value::Type type,
+ const base::Value** out_value) const;
+
+ // These methods return true if a preference with the given name is in the
+ // indicated pref store, even if that value is currently being overridden by
+ // a higher-priority source.
+ bool PrefValueInManagedStore(const std::string& name) const;
+ bool PrefValueInSupervisedStore(const std::string& name) const;
+ bool PrefValueInExtensionStore(const std::string& name) const;
+ bool PrefValueInUserStore(const std::string& name) const;
+
+ // These methods return true if a preference with the given name is actually
+ // being controlled by the indicated pref store and not being overridden by
+ // a higher-priority source.
+ bool PrefValueFromExtensionStore(const std::string& name) const;
+ bool PrefValueFromUserStore(const std::string& name) const;
+ bool PrefValueFromRecommendedStore(const std::string& name) const;
+ bool PrefValueFromDefaultStore(const std::string& name) const;
+
+ // Check whether a Preference value is modifiable by the user, i.e. whether
+ // there is no higher-priority source controlling it.
+ bool PrefValueUserModifiable(const std::string& name) const;
+
+ // Check whether a Preference value is modifiable by an extension, i.e.
+ // whether there is no higher-priority source controlling it.
+ bool PrefValueExtensionModifiable(const std::string& name) const;
+
+ // Update the command line PrefStore with |command_line_prefs|.
+ void UpdateCommandLinePrefStore(PrefStore* command_line_prefs);
+
+ private:
+ // PrefStores must be listed here in order from highest to lowest priority.
+ // MANAGED contains all managed preference values that are provided by
+ // mandatory policies (e.g. Windows Group Policy or cloud policy).
+ // SUPERVISED_USER contains preferences that are valid for supervised users.
+ // EXTENSION contains preference values set by extensions.
+ // COMMAND_LINE contains preference values set by command-line switches.
+ // USER contains all user-set preference values.
+ // RECOMMENDED contains all preferences that are provided by recommended
+ // policies.
+ // DEFAULT contains all application default preference values.
+ enum PrefStoreType {
+ // INVALID_STORE is not associated with an actual PrefStore but used as
+ // an invalid marker, e.g. as a return value.
+ INVALID_STORE = -1,
+ MANAGED_STORE = 0,
+ SUPERVISED_USER_STORE,
+ EXTENSION_STORE,
+ COMMAND_LINE_STORE,
+ USER_STORE,
+ RECOMMENDED_STORE,
+ DEFAULT_STORE,
+ PREF_STORE_TYPE_MAX = DEFAULT_STORE
+ };
+
+ // Keeps a PrefStore reference on behalf of the PrefValueStore and monitors
+ // the PrefStore for changes, forwarding notifications to PrefValueStore. This
+ // indirection is here for the sake of disambiguating notifications from the
+ // individual PrefStores.
+ class PrefStoreKeeper : public PrefStore::Observer {
+ public:
+ PrefStoreKeeper();
+ ~PrefStoreKeeper() override;
+
+ // Takes ownership of |pref_store|.
+ void Initialize(PrefValueStore* store,
+ PrefStore* pref_store,
+ PrefStoreType type);
+
+ PrefStore* store() { return pref_store_.get(); }
+ const PrefStore* store() const { return pref_store_.get(); }
+
+ private:
+ // PrefStore::Observer implementation.
+ void OnPrefValueChanged(const std::string& key) override;
+ void OnInitializationCompleted(bool succeeded) override;
+
+ // PrefValueStore this keeper is part of.
+ PrefValueStore* pref_value_store_;
+
+ // The PrefStore managed by this keeper.
+ scoped_refptr<PrefStore> pref_store_;
+
+ // Type of the pref store.
+ PrefStoreType type_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefStoreKeeper);
+ };
+
+ typedef std::map<std::string, base::Value::Type> PrefTypeMap;
+
+ // Returns true if the preference with the given name has a value in the
+ // given PrefStoreType, of the same value type as the preference was
+ // registered with.
+ bool PrefValueInStore(const std::string& name, PrefStoreType store) const;
+
+ // Returns true if a preference has an explicit value in any of the
+ // stores in the range specified by |first_checked_store| and
+ // |last_checked_store|, even if that value is currently being
+ // overridden by a higher-priority store.
+ bool PrefValueInStoreRange(const std::string& name,
+ PrefStoreType first_checked_store,
+ PrefStoreType last_checked_store) const;
+
+ // Returns the pref store type identifying the source that controls the
+ // Preference identified by |name|. If none of the sources has a value,
+ // INVALID_STORE is returned. In practice, the default PrefStore
+ // should always have a value for any registered preferencem, so INVALID_STORE
+ // indicates an error.
+ PrefStoreType ControllingPrefStoreForPref(const std::string& name) const;
+
+ // Get a value from the specified |store|.
+ bool GetValueFromStore(const std::string& name,
+ PrefStoreType store,
+ const base::Value** out_value) const;
+
+ // Get a value from the specified |store| if its |type| matches.
+ bool GetValueFromStoreWithType(const std::string& name,
+ base::Value::Type type,
+ PrefStoreType store,
+ const base::Value** out_value) const;
+
+ // Called upon changes in individual pref stores in order to determine whether
+ // the user-visible pref value has changed. Triggers the change notification
+ // if the effective value of the preference has changed, or if the store
+ // controlling the pref has changed.
+ void NotifyPrefChanged(const std::string& path, PrefStoreType new_store);
+
+ // Called from the PrefStoreKeeper implementation when a pref value for |key|
+ // changed in the pref store for |type|.
+ void OnPrefValueChanged(PrefStoreType type, const std::string& key);
+
+ // Handle the event that the store for |type| has completed initialization.
+ void OnInitializationCompleted(PrefStoreType type, bool succeeded);
+
+ // Initializes a pref store keeper. Sets up a PrefStoreKeeper that will take
+ // ownership of the passed |pref_store|.
+ void InitPrefStore(PrefStoreType type, PrefStore* pref_store);
+
+ // Checks whether initialization is completed and tells the notifier if that
+ // is the case.
+ void CheckInitializationCompleted();
+
+ // Get the PrefStore pointer for the given type. May return NULL if there is
+ // no PrefStore for that type.
+ PrefStore* GetPrefStore(PrefStoreType type) {
+ return pref_stores_[type].store();
+ }
+ const PrefStore* GetPrefStore(PrefStoreType type) const {
+ return pref_stores_[type].store();
+ }
+
+ // Keeps the PrefStore references in order of precedence.
+ PrefStoreKeeper pref_stores_[PREF_STORE_TYPE_MAX + 1];
+
+ PrefChangedCallback pref_changed_callback_;
+
+ // Used for generating notifications. This is a weak reference,
+ // since the notifier is owned by the corresponding PrefService.
+ PrefNotifier* pref_notifier_;
+
+ // A mapping of preference names to their registered types.
+ PrefTypeMap pref_types_;
+
+ // True if not all of the PrefStores were initialized successfully.
+ bool initialization_failed_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefValueStore);
+};
+
+#endif // COMPONENTS_PREFS_PREF_VALUE_STORE_H_
diff --git a/chromium/components/prefs/pref_value_store_unittest.cc b/chromium/components/prefs/pref_value_store_unittest.cc
new file mode 100644
index 00000000000..90ee7d689a8
--- /dev/null
+++ b/chromium/components/prefs/pref_value_store_unittest.cc
@@ -0,0 +1,670 @@
+// Copyright (c) 2012 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 <string>
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "components/prefs/pref_notifier.h"
+#include "components/prefs/pref_value_store.h"
+#include "components/prefs/testing_pref_store.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::Mock;
+using testing::_;
+
+namespace {
+
+// Allows to capture pref notifications through gmock.
+class MockPrefNotifier : public PrefNotifier {
+ public:
+ MOCK_METHOD1(OnPreferenceChanged, void(const std::string&));
+ MOCK_METHOD1(OnInitializationCompleted, void(bool));
+};
+
+// Allows to capture sync model associator interaction.
+class MockPrefModelAssociator {
+ public:
+ MOCK_METHOD1(ProcessPrefChange, void(const std::string&));
+};
+
+} // namespace
+
+// Names of the preferences used in this test.
+namespace prefs {
+const char kManagedPref[] = "this.pref.managed";
+const char kSupervisedUserPref[] = "this.pref.supervised_user";
+const char kCommandLinePref[] = "this.pref.command_line";
+const char kExtensionPref[] = "this.pref.extension";
+const char kUserPref[] = "this.pref.user";
+const char kRecommendedPref[] = "this.pref.recommended";
+const char kDefaultPref[] = "this.pref.default";
+const char kMissingPref[] = "this.pref.does_not_exist";
+}
+
+// Potentially expected values of all preferences used in this test program.
+namespace managed_pref {
+const char kManagedValue[] = "managed:managed";
+}
+
+namespace supervised_user_pref {
+const char kManagedValue[] = "supervised_user:managed";
+const char kSupervisedUserValue[] = "supervised_user:supervised_user";
+}
+
+namespace extension_pref {
+const char kManagedValue[] = "extension:managed";
+const char kSupervisedUserValue[] = "extension:supervised_user";
+const char kExtensionValue[] = "extension:extension";
+}
+
+namespace command_line_pref {
+const char kManagedValue[] = "command_line:managed";
+const char kSupervisedUserValue[] = "command_line:supervised_user";
+const char kExtensionValue[] = "command_line:extension";
+const char kCommandLineValue[] = "command_line:command_line";
+}
+
+namespace user_pref {
+const char kManagedValue[] = "user:managed";
+const char kSupervisedUserValue[] = "supervised_user:supervised_user";
+const char kExtensionValue[] = "user:extension";
+const char kCommandLineValue[] = "user:command_line";
+const char kUserValue[] = "user:user";
+}
+
+namespace recommended_pref {
+const char kManagedValue[] = "recommended:managed";
+const char kSupervisedUserValue[] = "recommended:supervised_user";
+const char kExtensionValue[] = "recommended:extension";
+const char kCommandLineValue[] = "recommended:command_line";
+const char kUserValue[] = "recommended:user";
+const char kRecommendedValue[] = "recommended:recommended";
+}
+
+namespace default_pref {
+const char kManagedValue[] = "default:managed";
+const char kSupervisedUserValue[] = "default:supervised_user";
+const char kExtensionValue[] = "default:extension";
+const char kCommandLineValue[] = "default:command_line";
+const char kUserValue[] = "default:user";
+const char kRecommendedValue[] = "default:recommended";
+const char kDefaultValue[] = "default:default";
+}
+
+class PrefValueStoreTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ // Create TestingPrefStores.
+ CreateManagedPrefs();
+ CreateSupervisedUserPrefs();
+ CreateExtensionPrefs();
+ CreateCommandLinePrefs();
+ CreateUserPrefs();
+ CreateRecommendedPrefs();
+ CreateDefaultPrefs();
+ sync_associator_.reset(new MockPrefModelAssociator());
+
+ // Create a fresh PrefValueStore.
+ pref_value_store_.reset(
+ new PrefValueStore(managed_pref_store_.get(),
+ supervised_user_pref_store_.get(),
+ extension_pref_store_.get(),
+ command_line_pref_store_.get(),
+ user_pref_store_.get(),
+ recommended_pref_store_.get(),
+ default_pref_store_.get(),
+ &pref_notifier_));
+
+ pref_value_store_->set_callback(
+ base::Bind(&MockPrefModelAssociator::ProcessPrefChange,
+ base::Unretained(sync_associator_.get())));
+ }
+
+ void CreateManagedPrefs() {
+ managed_pref_store_ = new TestingPrefStore;
+ managed_pref_store_->SetString(
+ prefs::kManagedPref,
+ managed_pref::kManagedValue);
+ }
+
+ void CreateSupervisedUserPrefs() {
+ supervised_user_pref_store_ = new TestingPrefStore;
+ supervised_user_pref_store_->SetString(
+ prefs::kManagedPref,
+ supervised_user_pref::kManagedValue);
+ supervised_user_pref_store_->SetString(
+ prefs::kSupervisedUserPref,
+ supervised_user_pref::kSupervisedUserValue);
+ }
+
+ void CreateExtensionPrefs() {
+ extension_pref_store_ = new TestingPrefStore;
+ extension_pref_store_->SetString(
+ prefs::kManagedPref,
+ extension_pref::kManagedValue);
+ extension_pref_store_->SetString(
+ prefs::kSupervisedUserPref,
+ extension_pref::kSupervisedUserValue);
+ extension_pref_store_->SetString(
+ prefs::kExtensionPref,
+ extension_pref::kExtensionValue);
+ }
+
+ void CreateCommandLinePrefs() {
+ command_line_pref_store_ = new TestingPrefStore;
+ command_line_pref_store_->SetString(
+ prefs::kManagedPref,
+ command_line_pref::kManagedValue);
+ command_line_pref_store_->SetString(
+ prefs::kSupervisedUserPref,
+ command_line_pref::kSupervisedUserValue);
+ command_line_pref_store_->SetString(
+ prefs::kExtensionPref,
+ command_line_pref::kExtensionValue);
+ command_line_pref_store_->SetString(
+ prefs::kCommandLinePref,
+ command_line_pref::kCommandLineValue);
+ }
+
+ void CreateUserPrefs() {
+ user_pref_store_ = new TestingPrefStore;
+ user_pref_store_->SetString(
+ prefs::kManagedPref,
+ user_pref::kManagedValue);
+ user_pref_store_->SetString(
+ prefs::kSupervisedUserPref,
+ user_pref::kSupervisedUserValue);
+ user_pref_store_->SetString(
+ prefs::kCommandLinePref,
+ user_pref::kCommandLineValue);
+ user_pref_store_->SetString(
+ prefs::kExtensionPref,
+ user_pref::kExtensionValue);
+ user_pref_store_->SetString(
+ prefs::kUserPref,
+ user_pref::kUserValue);
+ }
+
+ void CreateRecommendedPrefs() {
+ recommended_pref_store_ = new TestingPrefStore;
+ recommended_pref_store_->SetString(
+ prefs::kManagedPref,
+ recommended_pref::kManagedValue);
+ recommended_pref_store_->SetString(
+ prefs::kSupervisedUserPref,
+ recommended_pref::kSupervisedUserValue);
+ recommended_pref_store_->SetString(
+ prefs::kCommandLinePref,
+ recommended_pref::kCommandLineValue);
+ recommended_pref_store_->SetString(
+ prefs::kExtensionPref,
+ recommended_pref::kExtensionValue);
+ recommended_pref_store_->SetString(
+ prefs::kUserPref,
+ recommended_pref::kUserValue);
+ recommended_pref_store_->SetString(
+ prefs::kRecommendedPref,
+ recommended_pref::kRecommendedValue);
+ }
+
+ void CreateDefaultPrefs() {
+ default_pref_store_ = new TestingPrefStore;
+ default_pref_store_->SetString(
+ prefs::kSupervisedUserPref,
+ default_pref::kSupervisedUserValue);
+ default_pref_store_->SetString(
+ prefs::kManagedPref,
+ default_pref::kManagedValue);
+ default_pref_store_->SetString(
+ prefs::kCommandLinePref,
+ default_pref::kCommandLineValue);
+ default_pref_store_->SetString(
+ prefs::kExtensionPref,
+ default_pref::kExtensionValue);
+ default_pref_store_->SetString(
+ prefs::kUserPref,
+ default_pref::kUserValue);
+ default_pref_store_->SetString(
+ prefs::kRecommendedPref,
+ default_pref::kRecommendedValue);
+ default_pref_store_->SetString(
+ prefs::kDefaultPref,
+ default_pref::kDefaultValue);
+ }
+
+ void ExpectValueChangeNotifications(const std::string& name) {
+ EXPECT_CALL(pref_notifier_, OnPreferenceChanged(name));
+ EXPECT_CALL(*sync_associator_, ProcessPrefChange(name));
+ }
+
+ void CheckAndClearValueChangeNotifications() {
+ Mock::VerifyAndClearExpectations(&pref_notifier_);
+ Mock::VerifyAndClearExpectations(sync_associator_.get());
+ }
+
+ MockPrefNotifier pref_notifier_;
+ scoped_ptr<MockPrefModelAssociator> sync_associator_;
+ scoped_ptr<PrefValueStore> pref_value_store_;
+
+ scoped_refptr<TestingPrefStore> managed_pref_store_;
+ scoped_refptr<TestingPrefStore> supervised_user_pref_store_;
+ scoped_refptr<TestingPrefStore> extension_pref_store_;
+ scoped_refptr<TestingPrefStore> command_line_pref_store_;
+ scoped_refptr<TestingPrefStore> user_pref_store_;
+ scoped_refptr<TestingPrefStore> recommended_pref_store_;
+ scoped_refptr<TestingPrefStore> default_pref_store_;
+};
+
+TEST_F(PrefValueStoreTest, GetValue) {
+ const base::Value* value;
+
+ // The following tests read a value from the PrefService. The preferences are
+ // set in a way such that all lower-priority stores have a value and we can
+ // test whether overrides work correctly.
+
+ // Test getting a managed value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kManagedPref,
+ base::Value::TYPE_STRING, &value));
+ std::string actual_str_value;
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(managed_pref::kManagedValue, actual_str_value);
+
+ // Test getting a supervised user value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kSupervisedUserPref,
+ base::Value::TYPE_STRING, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(supervised_user_pref::kSupervisedUserValue, actual_str_value);
+
+ // Test getting an extension value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kExtensionPref,
+ base::Value::TYPE_STRING, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(extension_pref::kExtensionValue, actual_str_value);
+
+ // Test getting a command-line value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kCommandLinePref,
+ base::Value::TYPE_STRING, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(command_line_pref::kCommandLineValue, actual_str_value);
+
+ // Test getting a user-set value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kUserPref,
+ base::Value::TYPE_STRING, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(user_pref::kUserValue, actual_str_value);
+
+ // Test getting a user set value overwriting a recommended value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kRecommendedPref,
+ base::Value::TYPE_STRING, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(recommended_pref::kRecommendedValue,
+ actual_str_value);
+
+ // Test getting a default value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kDefaultPref,
+ base::Value::TYPE_STRING, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(default_pref::kDefaultValue, actual_str_value);
+
+ // Test getting a preference value that the |PrefValueStore|
+ // does not contain.
+ base::FundamentalValue tmp_dummy_value(true);
+ value = &tmp_dummy_value;
+ ASSERT_FALSE(pref_value_store_->GetValue(prefs::kMissingPref,
+ base::Value::TYPE_STRING, &value));
+ ASSERT_FALSE(value);
+}
+
+TEST_F(PrefValueStoreTest, GetRecommendedValue) {
+ const base::Value* value;
+
+ // The following tests read a value from the PrefService. The preferences are
+ // set in a way such that all lower-priority stores have a value and we can
+ // test whether overrides do not clutter the recommended value.
+
+ // Test getting recommended value when a managed value is present.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+ prefs::kManagedPref,
+ base::Value::TYPE_STRING, &value));
+ std::string actual_str_value;
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(recommended_pref::kManagedValue, actual_str_value);
+
+ // Test getting recommended value when a supervised user value is present.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+ prefs::kSupervisedUserPref,
+ base::Value::TYPE_STRING, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(recommended_pref::kSupervisedUserValue, actual_str_value);
+
+ // Test getting recommended value when an extension value is present.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+ prefs::kExtensionPref,
+ base::Value::TYPE_STRING, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(recommended_pref::kExtensionValue, actual_str_value);
+
+ // Test getting recommended value when a command-line value is present.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+ prefs::kCommandLinePref,
+ base::Value::TYPE_STRING, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(recommended_pref::kCommandLineValue, actual_str_value);
+
+ // Test getting recommended value when a user-set value is present.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+ prefs::kUserPref,
+ base::Value::TYPE_STRING, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(recommended_pref::kUserValue, actual_str_value);
+
+ // Test getting recommended value when no higher-priority value is present.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetRecommendedValue(
+ prefs::kRecommendedPref,
+ base::Value::TYPE_STRING, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(recommended_pref::kRecommendedValue,
+ actual_str_value);
+
+ // Test getting recommended value when no recommended value is present.
+ base::FundamentalValue tmp_dummy_value(true);
+ value = &tmp_dummy_value;
+ ASSERT_FALSE(pref_value_store_->GetRecommendedValue(
+ prefs::kDefaultPref,
+ base::Value::TYPE_STRING, &value));
+ ASSERT_FALSE(value);
+
+ // Test getting a preference value that the |PrefValueStore|
+ // does not contain.
+ value = &tmp_dummy_value;
+ ASSERT_FALSE(pref_value_store_->GetRecommendedValue(
+ prefs::kMissingPref,
+ base::Value::TYPE_STRING, &value));
+ ASSERT_FALSE(value);
+}
+
+TEST_F(PrefValueStoreTest, PrefChanges) {
+ // Check pref controlled by highest-priority store.
+ ExpectValueChangeNotifications(prefs::kManagedPref);
+ managed_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kManagedPref);
+ supervised_user_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kManagedPref);
+ extension_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kManagedPref);
+ command_line_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kManagedPref);
+ user_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kManagedPref);
+ recommended_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kManagedPref);
+ default_pref_store_->NotifyPrefValueChanged(prefs::kManagedPref);
+ CheckAndClearValueChangeNotifications();
+
+ // Check pref controlled by user store.
+ ExpectValueChangeNotifications(prefs::kUserPref);
+ managed_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kUserPref);
+ extension_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kUserPref);
+ command_line_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kUserPref);
+ user_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kUserPref);
+ recommended_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kUserPref);
+ default_pref_store_->NotifyPrefValueChanged(prefs::kUserPref);
+ CheckAndClearValueChangeNotifications();
+
+ // Check pref controlled by default-pref store.
+ ExpectValueChangeNotifications(prefs::kDefaultPref);
+ managed_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kDefaultPref);
+ extension_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kDefaultPref);
+ command_line_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kDefaultPref);
+ user_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kDefaultPref);
+ recommended_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+ CheckAndClearValueChangeNotifications();
+
+ ExpectValueChangeNotifications(prefs::kDefaultPref);
+ default_pref_store_->NotifyPrefValueChanged(prefs::kDefaultPref);
+ CheckAndClearValueChangeNotifications();
+}
+
+TEST_F(PrefValueStoreTest, OnInitializationCompleted) {
+ EXPECT_CALL(pref_notifier_, OnInitializationCompleted(true)).Times(0);
+ managed_pref_store_->SetInitializationCompleted();
+ supervised_user_pref_store_->SetInitializationCompleted();
+ extension_pref_store_->SetInitializationCompleted();
+ command_line_pref_store_->SetInitializationCompleted();
+ recommended_pref_store_->SetInitializationCompleted();
+ default_pref_store_->SetInitializationCompleted();
+ Mock::VerifyAndClearExpectations(&pref_notifier_);
+
+ // The notification should only be triggered after the last store is done.
+ EXPECT_CALL(pref_notifier_, OnInitializationCompleted(true)).Times(1);
+ user_pref_store_->SetInitializationCompleted();
+ Mock::VerifyAndClearExpectations(&pref_notifier_);
+}
+
+TEST_F(PrefValueStoreTest, PrefValueInManagedStore) {
+ EXPECT_TRUE(pref_value_store_->PrefValueInManagedStore(
+ prefs::kManagedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+ prefs::kSupervisedUserPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+ prefs::kExtensionPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+ prefs::kCommandLinePref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+ prefs::kUserPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+ prefs::kRecommendedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+ prefs::kDefaultPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+ prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueInExtensionStore) {
+ EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kManagedPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kSupervisedUserPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kExtensionPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kCommandLinePref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kUserPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kRecommendedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kDefaultPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueInUserStore) {
+ EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+ prefs::kManagedPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+ prefs::kSupervisedUserPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+ prefs::kExtensionPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+ prefs::kCommandLinePref));
+ EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+ prefs::kUserPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInUserStore(
+ prefs::kRecommendedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInUserStore(
+ prefs::kDefaultPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInUserStore(
+ prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueFromExtensionStore) {
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kManagedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kSupervisedUserPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kExtensionPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kCommandLinePref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kUserPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kRecommendedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kDefaultPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueFromUserStore) {
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+ prefs::kManagedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+ prefs::kSupervisedUserPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+ prefs::kExtensionPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+ prefs::kCommandLinePref));
+ EXPECT_TRUE(pref_value_store_->PrefValueFromUserStore(
+ prefs::kUserPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+ prefs::kRecommendedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+ prefs::kDefaultPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+ prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueFromRecommendedStore) {
+ EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+ prefs::kManagedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+ prefs::kSupervisedUserPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+ prefs::kExtensionPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+ prefs::kCommandLinePref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+ prefs::kUserPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueFromRecommendedStore(
+ prefs::kRecommendedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+ prefs::kDefaultPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromRecommendedStore(
+ prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueFromDefaultStore) {
+ EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+ prefs::kManagedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+ prefs::kSupervisedUserPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+ prefs::kExtensionPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+ prefs::kCommandLinePref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+ prefs::kUserPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+ prefs::kRecommendedPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueFromDefaultStore(
+ prefs::kDefaultPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromDefaultStore(
+ prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueUserModifiable) {
+ EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
+ prefs::kManagedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
+ prefs::kSupervisedUserPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
+ prefs::kExtensionPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueUserModifiable(
+ prefs::kCommandLinePref));
+ EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable(
+ prefs::kUserPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable(
+ prefs::kRecommendedPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable(
+ prefs::kDefaultPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueUserModifiable(
+ prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueExtensionModifiable) {
+ EXPECT_FALSE(pref_value_store_->PrefValueExtensionModifiable(
+ prefs::kManagedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueExtensionModifiable(
+ prefs::kSupervisedUserPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+ prefs::kExtensionPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+ prefs::kCommandLinePref));
+ EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+ prefs::kUserPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+ prefs::kRecommendedPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+ prefs::kDefaultPref));
+ EXPECT_TRUE(pref_value_store_->PrefValueExtensionModifiable(
+ prefs::kMissingPref));
+}
diff --git a/chromium/components/prefs/prefs.gyp b/chromium/components/prefs/prefs.gyp
new file mode 100644
index 00000000000..554bef4f7ff
--- /dev/null
+++ b/chromium/components/prefs/prefs.gyp
@@ -0,0 +1,87 @@
+# Copyright (c) 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'prefs',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../../base/base.gyp:base',
+ ],
+ 'include_dirs': [
+ '../..',
+ ],
+ 'defines': [
+ 'COMPONENTS_PREFS_IMPLEMENTATION',
+ ],
+ 'sources': [
+ 'default_pref_store.cc',
+ 'default_pref_store.h',
+ 'json_pref_store.cc',
+ 'json_pref_store.h',
+ 'overlay_user_pref_store.cc',
+ 'overlay_user_pref_store.h',
+ 'pref_change_registrar.cc',
+ 'pref_change_registrar.h',
+ 'pref_member.cc',
+ 'pref_member.h',
+ 'pref_notifier_impl.cc',
+ 'pref_notifier_impl.h',
+ 'pref_registry.cc',
+ 'pref_registry.h',
+ 'pref_registry_simple.cc',
+ 'pref_registry_simple.h',
+ 'pref_service.cc',
+ 'pref_service.h',
+ 'pref_service_factory.cc',
+ 'pref_service_factory.h',
+ 'pref_store.cc',
+ 'pref_store.h',
+ 'pref_value_map.cc',
+ 'pref_value_map.h',
+ 'pref_value_store.cc',
+ 'pref_value_store.h',
+ 'scoped_user_pref_update.cc',
+ 'scoped_user_pref_update.h',
+ 'value_map_pref_store.cc',
+ 'value_map_pref_store.h',
+ ],
+ 'conditions': [
+ ['OS!="ios"', {
+ 'sources': [
+ 'base_prefs_export.h',
+ 'persistent_pref_store.h',
+ 'pref_filter.h',
+ 'pref_notifier.h',
+ 'pref_observer.h',
+ 'writeable_pref_store.h',
+ ]
+ }]
+ ],
+ },
+ {
+ 'target_name': 'prefs_test_support',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '../..',
+ ],
+ 'dependencies': [
+ 'prefs',
+ '<(DEPTH)/testing/gmock.gyp:gmock',
+ '<(DEPTH)/testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'mock_pref_change_callback.cc',
+ 'mock_pref_change_callback.h',
+ 'pref_store_observer_mock.cc',
+ 'pref_store_observer_mock.h',
+ 'testing_pref_service.cc',
+ 'testing_pref_service.h',
+ 'testing_pref_store.cc',
+ 'testing_pref_store.h',
+ ],
+ },
+ ],
+}
diff --git a/chromium/components/prefs/scoped_user_pref_update.cc b/chromium/components/prefs/scoped_user_pref_update.cc
new file mode 100644
index 00000000000..dfba3349a2d
--- /dev/null
+++ b/chromium/components/prefs/scoped_user_pref_update.cc
@@ -0,0 +1,37 @@
+// 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 "components/prefs/scoped_user_pref_update.h"
+
+#include "base/logging.h"
+#include "components/prefs/pref_notifier.h"
+#include "components/prefs/pref_service.h"
+
+namespace subtle {
+
+ScopedUserPrefUpdateBase::ScopedUserPrefUpdateBase(PrefService* service,
+ const std::string& path)
+ : service_(service), path_(path), value_(NULL) {
+ DCHECK(service_->CalledOnValidThread());
+}
+
+ScopedUserPrefUpdateBase::~ScopedUserPrefUpdateBase() {
+ Notify();
+}
+
+base::Value* ScopedUserPrefUpdateBase::GetValueOfType(base::Value::Type type) {
+ DCHECK(CalledOnValidThread());
+ if (!value_)
+ value_ = service_->GetMutableUserPref(path_, type);
+ return value_;
+}
+
+void ScopedUserPrefUpdateBase::Notify() {
+ if (value_) {
+ service_->ReportUserPrefChanged(path_);
+ value_ = NULL;
+ }
+}
+
+} // namespace subtle
diff --git a/chromium/components/prefs/scoped_user_pref_update.h b/chromium/components/prefs/scoped_user_pref_update.h
new file mode 100644
index 00000000000..bfcdb3f5574
--- /dev/null
+++ b/chromium/components/prefs/scoped_user_pref_update.h
@@ -0,0 +1,109 @@
+// 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.
+//
+// A helper class that assists preferences in firing notifications when lists
+// or dictionaries are changed.
+
+#ifndef COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_
+#define COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/values.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/pref_service.h"
+
+class PrefService;
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace subtle {
+
+// Base class for ScopedUserPrefUpdateTemplate that contains the parts
+// that do not depend on ScopedUserPrefUpdateTemplate's template parameter.
+//
+// We need this base class mostly for making it a friend of PrefService
+// and getting access to PrefService::GetMutableUserPref and
+// PrefService::ReportUserPrefChanged.
+class COMPONENTS_PREFS_EXPORT ScopedUserPrefUpdateBase
+ : public base::NonThreadSafe {
+ protected:
+ ScopedUserPrefUpdateBase(PrefService* service, const std::string& path);
+
+ // Calls Notify().
+ ~ScopedUserPrefUpdateBase();
+
+ // Sets |value_| to |service_|->GetMutableUserPref and returns it.
+ base::Value* GetValueOfType(base::Value::Type type);
+
+ private:
+ // If |value_| is not null, triggers a notification of PrefObservers and
+ // resets |value_|.
+ void Notify();
+
+ // Weak pointer.
+ PrefService* service_;
+ // Path of the preference being updated.
+ std::string path_;
+ // Cache of value from user pref store (set between Get() and Notify() calls).
+ base::Value* value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedUserPrefUpdateBase);
+};
+
+} // namespace subtle
+
+// Class to support modifications to DictionaryValues and ListValues while
+// guaranteeing that PrefObservers are notified of changed values.
+//
+// This class may only be used on the UI thread as it requires access to the
+// PrefService.
+template <typename T, base::Value::Type type_enum_value>
+class ScopedUserPrefUpdate : public subtle::ScopedUserPrefUpdateBase {
+ public:
+ ScopedUserPrefUpdate(PrefService* service, const std::string& path)
+ : ScopedUserPrefUpdateBase(service, path) {}
+
+ // Triggers an update notification if Get() was called.
+ virtual ~ScopedUserPrefUpdate() {}
+
+ // Returns a mutable |T| instance that
+ // - is already in the user pref store, or
+ // - is (silently) created and written to the user pref store if none existed
+ // before.
+ //
+ // Calling Get() implies that an update notification is necessary at
+ // destruction time.
+ //
+ // The ownership of the return value remains with the user pref store.
+ // Virtual so it can be overriden in subclasses that transform the value
+ // before returning it (for example to return a subelement of a dictionary).
+ virtual T* Get() {
+ return static_cast<T*>(GetValueOfType(type_enum_value));
+ }
+
+ T& operator*() {
+ return *Get();
+ }
+
+ T* operator->() {
+ return Get();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedUserPrefUpdate);
+};
+
+typedef ScopedUserPrefUpdate<base::DictionaryValue,
+ base::Value::TYPE_DICTIONARY>
+ DictionaryPrefUpdate;
+typedef ScopedUserPrefUpdate<base::ListValue, base::Value::TYPE_LIST>
+ ListPrefUpdate;
+
+#endif // COMPONENTS_PREFS_SCOPED_USER_PREF_UPDATE_H_
diff --git a/chromium/components/prefs/scoped_user_pref_update_unittest.cc b/chromium/components/prefs/scoped_user_pref_update_unittest.cc
new file mode 100644
index 00000000000..437dff6fe9f
--- /dev/null
+++ b/chromium/components/prefs/scoped_user_pref_update_unittest.cc
@@ -0,0 +1,81 @@
+// 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 "components/prefs/mock_pref_change_callback.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/scoped_user_pref_update.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Mock;
+
+class ScopedUserPrefUpdateTest : public testing::Test {
+ public:
+ ScopedUserPrefUpdateTest() : observer_(&prefs_) {}
+ ~ScopedUserPrefUpdateTest() override {}
+
+ protected:
+ void SetUp() override {
+ prefs_.registry()->RegisterDictionaryPref(kPref);
+ registrar_.Init(&prefs_);
+ registrar_.Add(kPref, observer_.GetCallback());
+ }
+
+ static const char kPref[];
+ static const char kKey[];
+ static const char kValue[];
+
+ TestingPrefServiceSimple prefs_;
+ MockPrefChangeCallback observer_;
+ PrefChangeRegistrar registrar_;
+};
+
+const char ScopedUserPrefUpdateTest::kPref[] = "name";
+const char ScopedUserPrefUpdateTest::kKey[] = "key";
+const char ScopedUserPrefUpdateTest::kValue[] = "value";
+
+TEST_F(ScopedUserPrefUpdateTest, RegularUse) {
+ // Dictionary that will be expected to be set at the end.
+ base::DictionaryValue expected_dictionary;
+ expected_dictionary.SetString(kKey, kValue);
+
+ {
+ EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+ DictionaryPrefUpdate update(&prefs_, kPref);
+ base::DictionaryValue* value = update.Get();
+ ASSERT_TRUE(value);
+ value->SetString(kKey, kValue);
+
+ // The dictionary was created for us but the creation should have happened
+ // silently without notifications.
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ // Modifications happen online and are instantly visible, though.
+ const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref);
+ ASSERT_TRUE(current_value);
+ EXPECT_TRUE(expected_dictionary.Equals(current_value));
+
+ // Now we are leaving the scope of the update so we should be notified.
+ observer_.Expect(kPref, &expected_dictionary);
+ }
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref);
+ ASSERT_TRUE(current_value);
+ EXPECT_TRUE(expected_dictionary.Equals(current_value));
+}
+
+TEST_F(ScopedUserPrefUpdateTest, NeverTouchAnything) {
+ const base::DictionaryValue* old_value = prefs_.GetDictionary(kPref);
+ EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+ {
+ DictionaryPrefUpdate update(&prefs_, kPref);
+ }
+ const base::DictionaryValue* new_value = prefs_.GetDictionary(kPref);
+ EXPECT_EQ(old_value, new_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+}
diff --git a/chromium/components/prefs/testing_pref_service.cc b/chromium/components/prefs/testing_pref_service.cc
new file mode 100644
index 00000000000..1dd760e72d6
--- /dev/null
+++ b/chromium/components/prefs/testing_pref_service.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2012 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 "components/prefs/testing_pref_service.h"
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "components/prefs/default_pref_store.h"
+#include "components/prefs/pref_notifier_impl.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_value_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+template <>
+TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase(
+ TestingPrefStore* managed_prefs,
+ TestingPrefStore* user_prefs,
+ TestingPrefStore* recommended_prefs,
+ PrefRegistry* pref_registry,
+ PrefNotifierImpl* pref_notifier)
+ : PrefService(
+ pref_notifier,
+ new PrefValueStore(managed_prefs,
+ NULL,
+ NULL,
+ NULL,
+ user_prefs,
+ recommended_prefs,
+ pref_registry->defaults().get(),
+ pref_notifier),
+ user_prefs,
+ pref_registry,
+ base::Bind(&TestingPrefServiceBase<PrefService,
+ PrefRegistry>::HandleReadError),
+ false),
+ managed_prefs_(managed_prefs),
+ user_prefs_(user_prefs),
+ recommended_prefs_(recommended_prefs) {}
+
+TestingPrefServiceSimple::TestingPrefServiceSimple()
+ : TestingPrefServiceBase<PrefService, PrefRegistry>(
+ new TestingPrefStore(),
+ new TestingPrefStore(),
+ new TestingPrefStore(),
+ new PrefRegistrySimple(),
+ new PrefNotifierImpl()) {}
+
+TestingPrefServiceSimple::~TestingPrefServiceSimple() {
+}
+
+PrefRegistrySimple* TestingPrefServiceSimple::registry() {
+ return static_cast<PrefRegistrySimple*>(DeprecatedGetPrefRegistry());
+}
diff --git a/chromium/components/prefs/testing_pref_service.h b/chromium/components/prefs/testing_pref_service.h
new file mode 100644
index 00000000000..756858907f1
--- /dev/null
+++ b/chromium/components/prefs/testing_pref_service.h
@@ -0,0 +1,196 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_
+#define COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/prefs/pref_registry.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/testing_pref_store.h"
+
+class PrefNotifierImpl;
+class PrefRegistrySimple;
+class TestingPrefStore;
+
+// A PrefService subclass for testing. It operates totally in memory and
+// provides additional API for manipulating preferences at the different levels
+// (managed, extension, user) conveniently.
+//
+// Use this via its specializations, e.g. TestingPrefServiceSimple.
+template <class SuperPrefService, class ConstructionPrefRegistry>
+class TestingPrefServiceBase : public SuperPrefService {
+ public:
+ virtual ~TestingPrefServiceBase();
+
+ // Read the value of a preference from the managed layer. Returns NULL if the
+ // preference is not defined at the managed layer.
+ const base::Value* GetManagedPref(const std::string& path) const;
+
+ // Set a preference on the managed layer and fire observers if the preference
+ // changed. Assumes ownership of |value|.
+ void SetManagedPref(const std::string& path, base::Value* value);
+
+ // Clear the preference on the managed layer and fire observers if the
+ // preference has been defined previously.
+ void RemoveManagedPref(const std::string& path);
+
+ // Similar to the above, but for user preferences.
+ const base::Value* GetUserPref(const std::string& path) const;
+ void SetUserPref(const std::string& path, base::Value* value);
+ void RemoveUserPref(const std::string& path);
+
+ // Similar to the above, but for recommended policy preferences.
+ const base::Value* GetRecommendedPref(const std::string& path) const;
+ void SetRecommendedPref(const std::string& path, base::Value* value);
+ void RemoveRecommendedPref(const std::string& path);
+
+ // Do-nothing implementation for TestingPrefService.
+ static void HandleReadError(PersistentPrefStore::PrefReadError error) {}
+
+ protected:
+ TestingPrefServiceBase(
+ TestingPrefStore* managed_prefs,
+ TestingPrefStore* user_prefs,
+ TestingPrefStore* recommended_prefs,
+ ConstructionPrefRegistry* pref_registry,
+ PrefNotifierImpl* pref_notifier);
+
+ private:
+ // Reads the value of the preference indicated by |path| from |pref_store|.
+ // Returns NULL if the preference was not found.
+ const base::Value* GetPref(TestingPrefStore* pref_store,
+ const std::string& path) const;
+
+ // Sets the value for |path| in |pref_store|.
+ void SetPref(TestingPrefStore* pref_store,
+ const std::string& path,
+ base::Value* value);
+
+ // Removes the preference identified by |path| from |pref_store|.
+ void RemovePref(TestingPrefStore* pref_store, const std::string& path);
+
+ // Pointers to the pref stores our value store uses.
+ scoped_refptr<TestingPrefStore> managed_prefs_;
+ scoped_refptr<TestingPrefStore> user_prefs_;
+ scoped_refptr<TestingPrefStore> recommended_prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestingPrefServiceBase);
+};
+
+// Test version of PrefService.
+class TestingPrefServiceSimple
+ : public TestingPrefServiceBase<PrefService, PrefRegistry> {
+ public:
+ TestingPrefServiceSimple();
+ ~TestingPrefServiceSimple() override;
+
+ // This is provided as a convenience for registering preferences on
+ // an existing TestingPrefServiceSimple instance. On a production
+ // PrefService you would do all registrations before constructing
+ // it, passing it a PrefRegistry via its constructor (or via
+ // e.g. PrefServiceFactory).
+ PrefRegistrySimple* registry();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestingPrefServiceSimple);
+};
+
+template<>
+TestingPrefServiceBase<PrefService, PrefRegistry>::TestingPrefServiceBase(
+ TestingPrefStore* managed_prefs,
+ TestingPrefStore* user_prefs,
+ TestingPrefStore* recommended_prefs,
+ PrefRegistry* pref_registry,
+ PrefNotifierImpl* pref_notifier);
+
+template<class SuperPrefService, class ConstructionPrefRegistry>
+TestingPrefServiceBase<
+ SuperPrefService, ConstructionPrefRegistry>::~TestingPrefServiceBase() {
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+const base::Value* TestingPrefServiceBase<
+ SuperPrefService,
+ ConstructionPrefRegistry>::GetManagedPref(const std::string& path) const {
+ return GetPref(managed_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+ SetManagedPref(const std::string& path, base::Value* value) {
+ SetPref(managed_prefs_.get(), path, value);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+ RemoveManagedPref(const std::string& path) {
+ RemovePref(managed_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+const base::Value*
+TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::GetUserPref(
+ const std::string& path) const {
+ return GetPref(user_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+ SetUserPref(const std::string& path, base::Value* value) {
+ SetPref(user_prefs_.get(), path, value);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+ RemoveUserPref(const std::string& path) {
+ RemovePref(user_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+const base::Value*
+TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+ GetRecommendedPref(const std::string& path) const {
+ return GetPref(recommended_prefs_, path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+ SetRecommendedPref(const std::string& path, base::Value* value) {
+ SetPref(recommended_prefs_.get(), path, value);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+ RemoveRecommendedPref(const std::string& path) {
+ RemovePref(recommended_prefs_.get(), path);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+const base::Value*
+TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::GetPref(
+ TestingPrefStore* pref_store,
+ const std::string& path) const {
+ const base::Value* res;
+ return pref_store->GetValue(path, &res) ? res : NULL;
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+ SetPref(TestingPrefStore* pref_store,
+ const std::string& path,
+ base::Value* value) {
+ pref_store->SetValue(path, make_scoped_ptr(value),
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+}
+
+template <class SuperPrefService, class ConstructionPrefRegistry>
+void TestingPrefServiceBase<SuperPrefService, ConstructionPrefRegistry>::
+ RemovePref(TestingPrefStore* pref_store, const std::string& path) {
+ pref_store->RemoveValue(path, WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS);
+}
+
+#endif // COMPONENTS_PREFS_TESTING_PREF_SERVICE_H_
diff --git a/chromium/components/prefs/testing_pref_store.cc b/chromium/components/prefs/testing_pref_store.cc
new file mode 100644
index 00000000000..3a8be42c9d8
--- /dev/null
+++ b/chromium/components/prefs/testing_pref_store.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2012 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 "components/prefs/testing_pref_store.h"
+
+#include <utility>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+
+TestingPrefStore::TestingPrefStore()
+ : read_only_(true),
+ read_success_(true),
+ read_error_(PersistentPrefStore::PREF_READ_ERROR_NONE),
+ block_async_read_(false),
+ pending_async_read_(false),
+ init_complete_(false),
+ committed_(true) {}
+
+bool TestingPrefStore::GetValue(const std::string& key,
+ const base::Value** value) const {
+ return prefs_.GetValue(key, value);
+}
+
+bool TestingPrefStore::GetMutableValue(const std::string& key,
+ base::Value** value) {
+ return prefs_.GetValue(key, value);
+}
+
+void TestingPrefStore::AddObserver(PrefStore::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void TestingPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool TestingPrefStore::HasObservers() const {
+ return observers_.might_have_observers();
+}
+
+bool TestingPrefStore::IsInitializationComplete() const {
+ return init_complete_;
+}
+
+void TestingPrefStore::SetValue(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) {
+ if (prefs_.SetValue(key, std::move(value))) {
+ committed_ = false;
+ NotifyPrefValueChanged(key);
+ }
+}
+
+void TestingPrefStore::SetValueSilently(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) {
+ if (prefs_.SetValue(key, std::move(value)))
+ committed_ = false;
+}
+
+void TestingPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
+ if (prefs_.RemoveValue(key)) {
+ committed_ = false;
+ NotifyPrefValueChanged(key);
+ }
+}
+
+bool TestingPrefStore::ReadOnly() const {
+ return read_only_;
+}
+
+PersistentPrefStore::PrefReadError TestingPrefStore::GetReadError() const {
+ return read_error_;
+}
+
+PersistentPrefStore::PrefReadError TestingPrefStore::ReadPrefs() {
+ NotifyInitializationCompleted();
+ return read_error_;
+}
+
+void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
+ DCHECK(!pending_async_read_);
+ error_delegate_.reset(error_delegate);
+ if (block_async_read_)
+ pending_async_read_ = true;
+ else
+ NotifyInitializationCompleted();
+}
+
+void TestingPrefStore::CommitPendingWrite() { committed_ = true; }
+
+void TestingPrefStore::SchedulePendingLossyWrites() {}
+
+void TestingPrefStore::SetInitializationCompleted() {
+ NotifyInitializationCompleted();
+}
+
+void TestingPrefStore::NotifyPrefValueChanged(const std::string& key) {
+ FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
+}
+
+void TestingPrefStore::NotifyInitializationCompleted() {
+ DCHECK(!init_complete_);
+ init_complete_ = true;
+ if (read_success_ && read_error_ != PREF_READ_ERROR_NONE && error_delegate_)
+ error_delegate_->OnError(read_error_);
+ FOR_EACH_OBSERVER(
+ Observer, observers_, OnInitializationCompleted(read_success_));
+}
+
+void TestingPrefStore::ReportValueChanged(const std::string& key,
+ uint32_t flags) {
+ FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
+}
+
+void TestingPrefStore::SetString(const std::string& key,
+ const std::string& value) {
+ SetValue(key, make_scoped_ptr(new base::StringValue(value)),
+ DEFAULT_PREF_WRITE_FLAGS);
+}
+
+void TestingPrefStore::SetInteger(const std::string& key, int value) {
+ SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)),
+ DEFAULT_PREF_WRITE_FLAGS);
+}
+
+void TestingPrefStore::SetBoolean(const std::string& key, bool value) {
+ SetValue(key, make_scoped_ptr(new base::FundamentalValue(value)),
+ DEFAULT_PREF_WRITE_FLAGS);
+}
+
+bool TestingPrefStore::GetString(const std::string& key,
+ std::string* value) const {
+ const base::Value* stored_value;
+ if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+ return false;
+
+ return stored_value->GetAsString(value);
+}
+
+bool TestingPrefStore::GetInteger(const std::string& key, int* value) const {
+ const base::Value* stored_value;
+ if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+ return false;
+
+ return stored_value->GetAsInteger(value);
+}
+
+bool TestingPrefStore::GetBoolean(const std::string& key, bool* value) const {
+ const base::Value* stored_value;
+ if (!prefs_.GetValue(key, &stored_value) || !stored_value)
+ return false;
+
+ return stored_value->GetAsBoolean(value);
+}
+
+void TestingPrefStore::SetBlockAsyncRead(bool block_async_read) {
+ DCHECK(!init_complete_);
+ block_async_read_ = block_async_read;
+ if (pending_async_read_ && !block_async_read_)
+ NotifyInitializationCompleted();
+}
+
+void TestingPrefStore::ClearMutableValues() {
+ NOTIMPLEMENTED();
+}
+
+void TestingPrefStore::set_read_only(bool read_only) {
+ read_only_ = read_only;
+}
+
+void TestingPrefStore::set_read_success(bool read_success) {
+ DCHECK(!init_complete_);
+ read_success_ = read_success;
+}
+
+void TestingPrefStore::set_read_error(
+ PersistentPrefStore::PrefReadError read_error) {
+ DCHECK(!init_complete_);
+ read_error_ = read_error;
+}
+
+TestingPrefStore::~TestingPrefStore() {}
diff --git a/chromium/components/prefs/testing_pref_store.h b/chromium/components/prefs/testing_pref_store.h
new file mode 100644
index 00000000000..8fb4b8d2c24
--- /dev/null
+++ b/chromium/components/prefs/testing_pref_store.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_TESTING_PREF_STORE_H_
+#define COMPONENTS_PREFS_TESTING_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/prefs/persistent_pref_store.h"
+#include "components/prefs/pref_value_map.h"
+
+// |TestingPrefStore| is a preference store implementation that allows tests to
+// explicitly manipulate the contents of the store, triggering notifications
+// where appropriate.
+class TestingPrefStore : public PersistentPrefStore {
+ public:
+ TestingPrefStore();
+
+ // Overriden from PrefStore.
+ bool GetValue(const std::string& key,
+ const base::Value** result) const override;
+ void AddObserver(PrefStore::Observer* observer) override;
+ void RemoveObserver(PrefStore::Observer* observer) override;
+ bool HasObservers() const override;
+ bool IsInitializationComplete() const override;
+
+ // PersistentPrefStore overrides:
+ bool GetMutableValue(const std::string& key, base::Value** result) override;
+ void ReportValueChanged(const std::string& key, uint32_t flags) override;
+ void SetValue(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) override;
+ void SetValueSilently(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) override;
+ void RemoveValue(const std::string& key, uint32_t flags) override;
+ bool ReadOnly() const override;
+ PrefReadError GetReadError() const override;
+ PersistentPrefStore::PrefReadError ReadPrefs() override;
+ void ReadPrefsAsync(ReadErrorDelegate* error_delegate) override;
+ void CommitPendingWrite() override;
+ void SchedulePendingLossyWrites() override;
+
+ // Marks the store as having completed initialization.
+ void SetInitializationCompleted();
+
+ // Used for tests to trigger notifications explicitly.
+ void NotifyPrefValueChanged(const std::string& key);
+ void NotifyInitializationCompleted();
+
+ // Some convenience getters/setters.
+ void SetString(const std::string& key, const std::string& value);
+ void SetInteger(const std::string& key, int value);
+ void SetBoolean(const std::string& key, bool value);
+
+ bool GetString(const std::string& key, std::string* value) const;
+ bool GetInteger(const std::string& key, int* value) const;
+ bool GetBoolean(const std::string& key, bool* value) const;
+
+ // Determines whether ReadPrefsAsync completes immediately. Defaults to false
+ // (non-blocking). To block, invoke this with true (blocking) before the call
+ // to ReadPrefsAsync. To unblock, invoke again with false (non-blocking) after
+ // the call to ReadPrefsAsync.
+ void SetBlockAsyncRead(bool block_async_read);
+
+ void ClearMutableValues() override;
+
+ // Getter and Setter methods for setting and getting the state of the
+ // |TestingPrefStore|.
+ virtual void set_read_only(bool read_only);
+ void set_read_success(bool read_success);
+ void set_read_error(PersistentPrefStore::PrefReadError read_error);
+ bool committed() { return committed_; }
+
+ protected:
+ ~TestingPrefStore() override;
+
+ private:
+ // Stores the preference values.
+ PrefValueMap prefs_;
+
+ // Flag that indicates if the PrefStore is read-only
+ bool read_only_;
+
+ // The result to pass to PrefStore::Observer::OnInitializationCompleted
+ bool read_success_;
+
+ // The result to return from ReadPrefs or ReadPrefsAsync.
+ PersistentPrefStore::PrefReadError read_error_;
+
+ // Whether a call to ReadPrefsAsync should block.
+ bool block_async_read_;
+
+ // Whether there is a pending call to ReadPrefsAsync.
+ bool pending_async_read_;
+
+ // Whether initialization has been completed.
+ bool init_complete_;
+
+ // Whether the store contents have been committed to disk since the last
+ // mutation.
+ bool committed_;
+
+ scoped_ptr<ReadErrorDelegate> error_delegate_;
+ base::ObserverList<PrefStore::Observer, true> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestingPrefStore);
+};
+
+#endif // COMPONENTS_PREFS_TESTING_PREF_STORE_H_
diff --git a/chromium/components/prefs/value_map_pref_store.cc b/chromium/components/prefs/value_map_pref_store.cc
new file mode 100644
index 00000000000..fe2c80893db
--- /dev/null
+++ b/chromium/components/prefs/value_map_pref_store.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 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 "components/prefs/value_map_pref_store.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/stl_util.h"
+#include "base/values.h"
+
+ValueMapPrefStore::ValueMapPrefStore() {}
+
+bool ValueMapPrefStore::GetValue(const std::string& key,
+ const base::Value** value) const {
+ return prefs_.GetValue(key, value);
+}
+
+void ValueMapPrefStore::AddObserver(PrefStore::Observer* observer) {
+ observers_.AddObserver(observer);
+}
+
+void ValueMapPrefStore::RemoveObserver(PrefStore::Observer* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+bool ValueMapPrefStore::HasObservers() const {
+ return observers_.might_have_observers();
+}
+
+void ValueMapPrefStore::SetValue(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) {
+ if (prefs_.SetValue(key, std::move(value)))
+ FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
+}
+
+void ValueMapPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
+ if (prefs_.RemoveValue(key))
+ FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
+}
+
+bool ValueMapPrefStore::GetMutableValue(const std::string& key,
+ base::Value** value) {
+ return prefs_.GetValue(key, value);
+}
+
+void ValueMapPrefStore::ReportValueChanged(const std::string& key,
+ uint32_t flags) {
+ FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
+}
+
+void ValueMapPrefStore::SetValueSilently(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) {
+ prefs_.SetValue(key, std::move(value));
+}
+
+ValueMapPrefStore::~ValueMapPrefStore() {}
+
+void ValueMapPrefStore::NotifyInitializationCompleted() {
+ FOR_EACH_OBSERVER(Observer, observers_, OnInitializationCompleted(true));
+}
diff --git a/chromium/components/prefs/value_map_pref_store.h b/chromium/components/prefs/value_map_pref_store.h
new file mode 100644
index 00000000000..bddc64196b7
--- /dev/null
+++ b/chromium/components/prefs/value_map_pref_store.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 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 COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_
+#define COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/prefs/base_prefs_export.h"
+#include "components/prefs/pref_value_map.h"
+#include "components/prefs/writeable_pref_store.h"
+
+// A basic PrefStore implementation that uses a simple name-value map for
+// storing the preference values.
+class COMPONENTS_PREFS_EXPORT ValueMapPrefStore : public WriteablePrefStore {
+ public:
+ ValueMapPrefStore();
+
+ // PrefStore overrides:
+ bool GetValue(const std::string& key,
+ const base::Value** value) const override;
+ void AddObserver(PrefStore::Observer* observer) override;
+ void RemoveObserver(PrefStore::Observer* observer) override;
+ bool HasObservers() const override;
+
+ // WriteablePrefStore overrides:
+ void SetValue(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) override;
+ void RemoveValue(const std::string& key, uint32_t flags) override;
+ bool GetMutableValue(const std::string& key, base::Value** value) override;
+ void ReportValueChanged(const std::string& key, uint32_t flags) override;
+ void SetValueSilently(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) override;
+
+ protected:
+ ~ValueMapPrefStore() override;
+
+ // Notify observers about the initialization completed event.
+ void NotifyInitializationCompleted();
+
+ private:
+ PrefValueMap prefs_;
+
+ base::ObserverList<PrefStore::Observer, true> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValueMapPrefStore);
+};
+
+#endif // COMPONENTS_PREFS_VALUE_MAP_PREF_STORE_H_
diff --git a/chromium/components/prefs/writeable_pref_store.h b/chromium/components/prefs/writeable_pref_store.h
new file mode 100644
index 00000000000..0cb9f83d7b3
--- /dev/null
+++ b/chromium/components/prefs/writeable_pref_store.h
@@ -0,0 +1,72 @@
+// Copyright 2014 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 COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_
+#define COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/prefs/pref_store.h"
+
+namespace base {
+class Value;
+}
+
+// A pref store that can be written to as well as read from.
+class COMPONENTS_PREFS_EXPORT WriteablePrefStore : public PrefStore {
+ public:
+ // PrefWriteFlags can be used to change the way a pref will be written to
+ // storage.
+ enum PrefWriteFlags : uint32_t {
+ // No flags are specified.
+ DEFAULT_PREF_WRITE_FLAGS = 0,
+
+ // This marks the pref as "lossy". There is no strict time guarantee on when
+ // a lossy pref will be persisted to permanent storage when it is modified.
+ LOSSY_PREF_WRITE_FLAG = 1 << 1
+ };
+
+ WriteablePrefStore() {}
+
+ // Sets a |value| for |key| in the store. |value| must be non-NULL. |flags| is
+ // a bitmask of PrefWriteFlags.
+ virtual void SetValue(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) = 0;
+
+ // Removes the value for |key|.
+ virtual void RemoveValue(const std::string& key, uint32_t flags) = 0;
+
+ // Equivalent to PrefStore::GetValue but returns a mutable value.
+ virtual bool GetMutableValue(const std::string& key,
+ base::Value** result) = 0;
+
+ // Triggers a value changed notification. This function needs to be called
+ // if one retrieves a list or dictionary with GetMutableValue and change its
+ // value. SetValue takes care of notifications itself. Note that
+ // ReportValueChanged will trigger notifications even if nothing has changed.
+ // |flags| is a bitmask of PrefWriteFlags.
+ virtual void ReportValueChanged(const std::string& key, uint32_t flags) = 0;
+
+ // Same as SetValue, but doesn't generate notifications. This is used by
+ // PrefService::GetMutableUserPref() in order to put empty entries
+ // into the user pref store. Using SetValue is not an option since existing
+ // tests rely on the number of notifications generated. |flags| is a bitmask
+ // of PrefWriteFlags.
+ virtual void SetValueSilently(const std::string& key,
+ scoped_ptr<base::Value> value,
+ uint32_t flags) = 0;
+
+ protected:
+ ~WriteablePrefStore() override {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WriteablePrefStore);
+};
+
+#endif // COMPONENTS_PREFS_WRITEABLE_PREF_STORE_H_