summaryrefslogtreecommitdiffstats
path: root/chrome/browser/prefs
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/prefs')
-rw-r--r--chrome/browser/prefs/browser_prefs.cc140
-rw-r--r--chrome/browser/prefs/browser_prefs.h20
-rw-r--r--chrome/browser/prefs/command_line_pref_store.cc66
-rw-r--r--chrome/browser/prefs/command_line_pref_store.h60
-rw-r--r--chrome/browser/prefs/command_line_pref_store_unittest.cc130
-rw-r--r--chrome/browser/prefs/dummy_pref_store.cc22
-rw-r--r--chrome/browser/prefs/dummy_pref_store.h50
-rw-r--r--chrome/browser/prefs/pref_member.cc132
-rw-r--r--chrome/browser/prefs/pref_member.h208
-rw-r--r--chrome/browser/prefs/pref_member_unittest.cc194
-rw-r--r--chrome/browser/prefs/pref_notifier.cc140
-rw-r--r--chrome/browser/prefs/pref_notifier.h112
-rw-r--r--chrome/browser/prefs/pref_notifier_unittest.cc290
-rw-r--r--chrome/browser/prefs/pref_service.cc737
-rw-r--r--chrome/browser/prefs/pref_service.h254
-rw-r--r--chrome/browser/prefs/pref_service_uitest.cc134
-rw-r--r--chrome/browser/prefs/pref_service_unittest.cc289
-rw-r--r--chrome/browser/prefs/pref_set_observer.cc66
-rw-r--r--chrome/browser/prefs/pref_set_observer.h54
-rw-r--r--chrome/browser/prefs/pref_set_observer_unittest.cc89
-rw-r--r--chrome/browser/prefs/pref_value_store.cc299
-rw-r--r--chrome/browser/prefs/pref_value_store.h198
-rw-r--r--chrome/browser/prefs/pref_value_store_unittest.cc663
-rw-r--r--chrome/browser/prefs/scoped_pref_update.cc20
-rw-r--r--chrome/browser/prefs/scoped_pref_update.h26
-rw-r--r--chrome/browser/prefs/session_startup_pref.cc107
-rw-r--r--chrome/browser/prefs/session_startup_pref.h51
27 files changed, 4551 insertions, 0 deletions
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
new file mode 100644
index 0000000..93bb6be
--- /dev/null
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -0,0 +1,140 @@
+// 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 "chrome/browser/prefs/browser_prefs.h"
+
+#include "chrome/browser/autofill/autofill_manager.h"
+#include "chrome/browser/background_contents_service.h"
+#include "chrome/browser/background_mode_manager.h"
+#include "chrome/browser/bookmarks/bookmark_utils.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_shutdown.h"
+#include "chrome/browser/cookie_modal_dialog.h"
+#include "chrome/browser/debugger/devtools_manager.h"
+#include "chrome/browser/dom_ui/new_tab_ui.h"
+#include "chrome/browser/dom_ui/plugins_ui.h"
+#include "chrome/browser/download/download_manager.h"
+#include "chrome/browser/extensions/extension_dom_ui.h"
+#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/browser/extensions/extensions_ui.h"
+#include "chrome/browser/external_protocol_handler.h"
+#include "chrome/browser/geolocation/geolocation_content_settings_map.h"
+#include "chrome/browser/geolocation/geolocation_prefs.h"
+#include "chrome/browser/google_url_tracker.h"
+#include "chrome/browser/gtk/certificate_manager.h"
+#include "chrome/browser/host_content_settings_map.h"
+#include "chrome/browser/host_zoom_map.h"
+#include "chrome/browser/intranet_redirect_detector.h"
+#include "chrome/browser/metrics/metrics_log.h"
+#include "chrome/browser/metrics/metrics_service.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/browser/net/predictor_api.h"
+#include "chrome/browser/notifications/desktop_notification_service.h"
+#include "chrome/browser/page_info_model.h"
+#include "chrome/browser/password_manager/password_manager.h"
+#include "chrome/browser/prefs/session_startup_pref.h"
+#include "chrome/browser/renderer_host/browser_render_process_host.h"
+#include "chrome/browser/renderer_host/web_cache_manager.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/search_engines/keyword_editor_controller.h"
+#include "chrome/browser/search_engines/template_url_prepopulate_data.h"
+#include "chrome/browser/ssl/ssl_manager.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tabs/pinned_tab_codec.h"
+#include "chrome/browser/task_manager.h"
+#include "chrome/browser/translate/translate_prefs.h"
+#include "chrome/browser/upgrade_detector.h"
+
+#if defined(TOOLKIT_VIEWS) // TODO(port): whittle this down as we port
+#include "chrome/browser/views/browser_actions_container.h"
+#include "chrome/browser/views/frame/browser_view.h"
+#endif
+
+#if defined(TOOLKIT_GTK)
+#include "chrome/browser/gtk/browser_window_gtk.h"
+#endif
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/login/apply_services_customization.h"
+#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/chromeos/login/wizard_controller.h"
+#include "chrome/browser/chromeos/preferences.h"
+#include "chrome/browser/chromeos/status/language_menu_button.h"
+#endif
+
+namespace browser {
+
+void RegisterAllPrefs(PrefService* user_prefs, PrefService* local_state) {
+ RegisterLocalState(local_state);
+ RegisterUserPrefs(user_prefs);
+}
+
+void RegisterLocalState(PrefService* local_state) {
+ // Prefs in Local State
+ Browser::RegisterPrefs(local_state);
+ WebCacheManager::RegisterPrefs(local_state);
+ ExternalProtocolHandler::RegisterPrefs(local_state);
+ GoogleURLTracker::RegisterPrefs(local_state);
+ IntranetRedirectDetector::RegisterPrefs(local_state);
+ KeywordEditorController::RegisterPrefs(local_state);
+ MetricsLog::RegisterPrefs(local_state);
+ MetricsService::RegisterPrefs(local_state);
+ SafeBrowsingService::RegisterPrefs(local_state);
+ browser_shutdown::RegisterPrefs(local_state);
+ chrome_browser_net::RegisterPrefs(local_state);
+ PageInfoModel::RegisterPrefs(local_state);
+#if defined(TOOLKIT_VIEWS)
+ BrowserView::RegisterBrowserViewPrefs(local_state);
+#endif
+ UpgradeDetector::RegisterPrefs(local_state);
+ TaskManager::RegisterPrefs(local_state);
+ geolocation::RegisterPrefs(local_state);
+ AutoFillManager::RegisterBrowserPrefs(local_state);
+#if defined(OS_CHROMEOS)
+ chromeos::UserManager::RegisterPrefs(local_state);
+ WizardController::RegisterPrefs(local_state);
+ chromeos::LanguageMenuButton::RegisterPrefs(local_state);
+ chromeos::ApplyServicesCustomization::RegisterPrefs(local_state);
+#endif
+}
+
+void RegisterUserPrefs(PrefService* user_prefs) {
+ // User prefs
+ AutoFillManager::RegisterUserPrefs(user_prefs);
+ BackgroundModeManager::RegisterUserPrefs(user_prefs);
+ SessionStartupPref::RegisterUserPrefs(user_prefs);
+ Browser::RegisterUserPrefs(user_prefs);
+ PasswordManager::RegisterUserPrefs(user_prefs);
+ chrome_browser_net::RegisterUserPrefs(user_prefs);
+ DownloadManager::RegisterUserPrefs(user_prefs);
+ bookmark_utils::RegisterUserPrefs(user_prefs);
+ TabContents::RegisterUserPrefs(user_prefs);
+ TemplateURLPrepopulateData::RegisterUserPrefs(user_prefs);
+ ExtensionDOMUI::RegisterUserPrefs(user_prefs);
+ ExtensionsUI::RegisterUserPrefs(user_prefs);
+ NewTabUI::RegisterUserPrefs(user_prefs);
+ PluginsUI::RegisterUserPrefs(user_prefs);
+ HostContentSettingsMap::RegisterUserPrefs(user_prefs);
+ HostZoomMap::RegisterUserPrefs(user_prefs);
+ DevToolsManager::RegisterUserPrefs(user_prefs);
+ PinnedTabCodec::RegisterUserPrefs(user_prefs);
+ ExtensionPrefs::RegisterUserPrefs(user_prefs);
+ GeolocationContentSettingsMap::RegisterUserPrefs(user_prefs);
+ TranslatePrefs::RegisterUserPrefs(user_prefs);
+ DesktopNotificationService::RegisterUserPrefs(user_prefs);
+ ChromeURLRequestContextGetter::RegisterUserPrefs(user_prefs);
+#if defined(TOOLKIT_VIEWS)
+ BrowserActionsContainer::RegisterUserPrefs(user_prefs);
+#elif defined(TOOLKIT_GTK)
+ BrowserWindowGtk::RegisterUserPrefs(user_prefs);
+ certificate_manager_util::RegisterUserPrefs(user_prefs);
+#endif
+#if defined(OS_CHROMEOS)
+ chromeos::Preferences::RegisterUserPrefs(user_prefs);
+#endif
+ BackgroundContentsService::RegisterUserPrefs(user_prefs);
+ CookiePromptModalDialog::RegisterUserPrefs(user_prefs);
+}
+
+} // namespace browser
diff --git a/chrome/browser/prefs/browser_prefs.h b/chrome/browser/prefs/browser_prefs.h
new file mode 100644
index 0000000..ef109a4
--- /dev/null
+++ b/chrome/browser/prefs/browser_prefs.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PREFS_BROWSER_PREFS_H__
+#define CHROME_BROWSER_PREFS_BROWSER_PREFS_H__
+#pragma once
+
+class PrefService;
+
+namespace browser {
+
+// Makes the PrefService objects aware of all the prefs.
+void RegisterAllPrefs(PrefService* user_prefs, PrefService* local_state);
+void RegisterLocalState(PrefService* local_state);
+void RegisterUserPrefs(PrefService* user_prefs);
+
+} // namespace browser
+
+#endif // CHROME_BROWSER_PREFS_BROWSER_PREFS_H__
diff --git a/chrome/browser/prefs/command_line_pref_store.cc b/chrome/browser/prefs/command_line_pref_store.cc
new file mode 100644
index 0000000..41770e9
--- /dev/null
+++ b/chrome/browser/prefs/command_line_pref_store.cc
@@ -0,0 +1,66 @@
+// 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 "chrome/browser/prefs/command_line_pref_store.h"
+
+#include "app/app_switches.h"
+#include "base/values.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+
+const CommandLinePrefStore::StringSwitchToPreferenceMapEntry
+ CommandLinePrefStore::string_switch_map_[] = {
+ { switches::kLang, prefs::kApplicationLocale },
+ { switches::kProxyServer, prefs::kProxyServer },
+ { switches::kProxyPacUrl, prefs::kProxyPacUrl },
+ { switches::kProxyBypassList, prefs::kProxyBypassList },
+};
+
+const CommandLinePrefStore::BooleanSwitchToPreferenceMapEntry
+ CommandLinePrefStore::boolean_switch_map_[] = {
+ { switches::kNoProxyServer, prefs::kNoProxyServer, true },
+ { switches::kProxyAutoDetect, prefs::kProxyAutoDetect, true },
+};
+
+CommandLinePrefStore::CommandLinePrefStore(const CommandLine* command_line)
+ : command_line_(command_line),
+ prefs_(new DictionaryValue()) {}
+
+PrefStore::PrefReadError CommandLinePrefStore::ReadPrefs() {
+ ApplySimpleSwitches();
+ ValidateProxySwitches();
+ return PrefStore::PREF_READ_ERROR_NONE;
+}
+
+void CommandLinePrefStore::ApplySimpleSwitches() {
+ // Look for each switch we know about and set its preference accordingly.
+ for (size_t i = 0; i < arraysize(string_switch_map_); ++i) {
+ if (command_line_->HasSwitch(string_switch_map_[i].switch_name)) {
+ Value* value = Value::CreateStringValue(command_line_->
+ GetSwitchValueASCII(string_switch_map_[i].switch_name));
+ prefs_->Set(string_switch_map_[i].preference_path, value);
+ }
+ }
+
+ for (size_t i = 0; i < arraysize(boolean_switch_map_); ++i) {
+ if (command_line_->HasSwitch(boolean_switch_map_[i].switch_name)) {
+ Value* value = Value::CreateBooleanValue(
+ boolean_switch_map_[i].set_value);
+ prefs_->Set(boolean_switch_map_[i].preference_path, value);
+ }
+ }
+}
+
+bool CommandLinePrefStore::ValidateProxySwitches() {
+ if (command_line_->HasSwitch(switches::kNoProxyServer) &&
+ (command_line_->HasSwitch(switches::kProxyAutoDetect) ||
+ command_line_->HasSwitch(switches::kProxyServer) ||
+ command_line_->HasSwitch(switches::kProxyPacUrl) ||
+ command_line_->HasSwitch(switches::kProxyBypassList))) {
+ LOG(WARNING) << "Additional command-line proxy switches specified when --"
+ << switches::kNoProxyServer << " was also specified.";
+ return false;
+ }
+ return true;
+}
diff --git a/chrome/browser/prefs/command_line_pref_store.h b/chrome/browser/prefs/command_line_pref_store.h
new file mode 100644
index 0000000..89e49ce
--- /dev/null
+++ b/chrome/browser/prefs/command_line_pref_store.h
@@ -0,0 +1,60 @@
+// 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 CHROME_BROWSER_PREFS_COMMAND_LINE_PREF_STORE_H_
+#define CHROME_BROWSER_PREFS_COMMAND_LINE_PREF_STORE_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/scoped_ptr.h"
+#include "chrome/common/pref_store.h"
+
+class DictionaryValue;
+
+// This PrefStore keeps track of preferences set by command-line switches,
+// such as proxy settings.
+class CommandLinePrefStore : public PrefStore {
+ public:
+ explicit CommandLinePrefStore(const CommandLine* command_line);
+ virtual ~CommandLinePrefStore() {}
+
+ // PrefStore methods:
+ virtual PrefReadError ReadPrefs();
+ virtual DictionaryValue* prefs() { return prefs_.get(); }
+
+ protected:
+ // Logs a message and returns false if the proxy switches are
+ // self-contradictory. Protected so it can be used in unit testing.
+ bool ValidateProxySwitches();
+
+ private:
+ // Weak reference.
+ const CommandLine* command_line_;
+
+ scoped_ptr<DictionaryValue> prefs_;
+
+ struct StringSwitchToPreferenceMapEntry {
+ const char* switch_name;
+ const char* preference_path;
+ };
+ static const StringSwitchToPreferenceMapEntry string_switch_map_[];
+
+ // |set_value| indicates what the preference should be set to if the switch
+ // is present.
+ struct BooleanSwitchToPreferenceMapEntry {
+ const char* switch_name;
+ const char* preference_path;
+ bool set_value;
+ };
+ static const BooleanSwitchToPreferenceMapEntry boolean_switch_map_[];
+
+ // Using the string and boolean maps, apply command-line switches to their
+ // corresponding preferences in this pref store.
+ void ApplySimpleSwitches();
+
+ DISALLOW_COPY_AND_ASSIGN(CommandLinePrefStore);
+};
+
+#endif // CHROME_BROWSER_PREFS_COMMAND_LINE_PREF_STORE_H_
diff --git a/chrome/browser/prefs/command_line_pref_store_unittest.cc b/chrome/browser/prefs/command_line_pref_store_unittest.cc
new file mode 100644
index 0000000..c6d2bd2
--- /dev/null
+++ b/chrome/browser/prefs/command_line_pref_store_unittest.cc
@@ -0,0 +1,130 @@
+// 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 <gtest/gtest.h>
+
+#include "app/app_switches.h"
+#include "base/command_line.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/prefs/command_line_pref_store.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+
+namespace {
+
+class TestCommandLinePrefStore : public CommandLinePrefStore {
+ public:
+ explicit TestCommandLinePrefStore(CommandLine* cl)
+ : CommandLinePrefStore(cl) {}
+
+ bool ProxySwitchesAreValid() {
+ return ValidateProxySwitches();
+ }
+};
+
+const char unknown_bool[] = "unknown_switch";
+const char unknown_string[] = "unknown_other_switch";
+
+} // namespace
+
+// Tests a simple string pref on the command line.
+TEST(CommandLinePrefStoreTest, SimpleStringPref) {
+ CommandLine cl(CommandLine::ARGUMENTS_ONLY);
+ cl.AppendSwitchASCII(switches::kLang, "hi-MOM");
+ CommandLinePrefStore store(&cl);
+ EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE);
+
+ std::string result;
+ EXPECT_TRUE(store.prefs()->GetString(prefs::kApplicationLocale, &result));
+ EXPECT_EQ("hi-MOM", result);
+}
+
+// Tests a simple boolean pref on the command line.
+TEST(CommandLinePrefStoreTest, SimpleBooleanPref) {
+ CommandLine cl(CommandLine::ARGUMENTS_ONLY);
+ cl.AppendSwitch(switches::kNoProxyServer);
+ CommandLinePrefStore store(&cl);
+ EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE);
+
+ bool result;
+ EXPECT_TRUE(store.prefs()->GetBoolean(prefs::kNoProxyServer, &result));
+ EXPECT_TRUE(result);
+}
+
+// Tests a command line with no recognized prefs.
+TEST(CommandLinePrefStoreTest, NoPrefs) {
+ CommandLine cl(CommandLine::ARGUMENTS_ONLY);
+ cl.AppendSwitch(unknown_string);
+ cl.AppendSwitchASCII(unknown_bool, "a value");
+ CommandLinePrefStore store(&cl);
+ EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE);
+
+ bool bool_result = false;
+ EXPECT_FALSE(store.prefs()->GetBoolean(unknown_bool, &bool_result));
+ EXPECT_FALSE(bool_result);
+
+ std::string string_result = "";
+ EXPECT_FALSE(store.prefs()->GetString(unknown_string, &string_result));
+ EXPECT_EQ("", string_result);
+}
+
+// Tests a complex command line with multiple known and unknown switches.
+TEST(CommandLinePrefStoreTest, MultipleSwitches) {
+ CommandLine cl(CommandLine::ARGUMENTS_ONLY);
+ cl.AppendSwitch(unknown_string);
+ cl.AppendSwitch(switches::kProxyAutoDetect);
+ cl.AppendSwitchASCII(switches::kProxyServer, "proxy");
+ cl.AppendSwitchASCII(switches::kProxyBypassList, "list");
+ cl.AppendSwitchASCII(unknown_bool, "a value");
+ CommandLinePrefStore store(&cl);
+ EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE);
+
+ bool bool_result = false;
+ EXPECT_FALSE(store.prefs()->GetBoolean(unknown_bool, &bool_result));
+ EXPECT_FALSE(bool_result);
+ EXPECT_TRUE(store.prefs()->GetBoolean(prefs::kProxyAutoDetect, &bool_result));
+ EXPECT_TRUE(bool_result);
+
+ std::string string_result = "";
+ EXPECT_FALSE(store.prefs()->GetString(unknown_string, &string_result));
+ EXPECT_EQ("", string_result);
+ EXPECT_TRUE(store.prefs()->GetString(prefs::kProxyServer, &string_result));
+ EXPECT_EQ("proxy", string_result);
+ EXPECT_TRUE(store.prefs()->GetString(prefs::kProxyBypassList,
+ &string_result));
+ EXPECT_EQ("list", string_result);
+}
+
+// Tests proxy switch validation.
+TEST(CommandLinePrefStoreTest, ProxySwitchValidation) {
+ CommandLine cl(CommandLine::ARGUMENTS_ONLY);
+
+ // No switches.
+ TestCommandLinePrefStore store(&cl);
+ EXPECT_EQ(store.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE);
+ EXPECT_TRUE(store.ProxySwitchesAreValid());
+
+ // Only no-proxy.
+ cl.AppendSwitch(switches::kNoProxyServer);
+ TestCommandLinePrefStore store2(&cl);
+ EXPECT_EQ(store2.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE);
+ EXPECT_TRUE(store2.ProxySwitchesAreValid());
+
+ // Another proxy switch too.
+ cl.AppendSwitch(switches::kProxyAutoDetect);
+ TestCommandLinePrefStore store3(&cl);
+ EXPECT_EQ(store3.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE);
+ EXPECT_FALSE(store3.ProxySwitchesAreValid());
+
+ // All proxy switches except no-proxy.
+ CommandLine cl2(CommandLine::ARGUMENTS_ONLY);
+ cl2.AppendSwitch(switches::kProxyAutoDetect);
+ cl2.AppendSwitchASCII(switches::kProxyServer, "server");
+ cl2.AppendSwitchASCII(switches::kProxyPacUrl, "url");
+ cl2.AppendSwitchASCII(switches::kProxyBypassList, "list");
+ TestCommandLinePrefStore store4(&cl2);
+ EXPECT_EQ(store4.ReadPrefs(), PrefStore::PREF_READ_ERROR_NONE);
+ EXPECT_TRUE(store4.ProxySwitchesAreValid());
+}
diff --git a/chrome/browser/prefs/dummy_pref_store.cc b/chrome/browser/prefs/dummy_pref_store.cc
new file mode 100644
index 0000000..3a0a81a
--- /dev/null
+++ b/chrome/browser/prefs/dummy_pref_store.cc
@@ -0,0 +1,22 @@
+// 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 "chrome/browser/prefs/dummy_pref_store.h"
+
+#include "base/values.h"
+
+DummyPrefStore::DummyPrefStore()
+ : prefs_(new DictionaryValue()),
+ read_only_(true),
+ prefs_written_(false) { }
+
+PrefStore::PrefReadError DummyPrefStore::ReadPrefs() {
+ prefs_.reset(new DictionaryValue());
+ return PrefStore::PREF_READ_ERROR_NONE;
+}
+
+bool DummyPrefStore::WritePrefs() {
+ prefs_written_ = true;
+ return prefs_written_;
+}
diff --git a/chrome/browser/prefs/dummy_pref_store.h b/chrome/browser/prefs/dummy_pref_store.h
new file mode 100644
index 0000000..ebf92fc
--- /dev/null
+++ b/chrome/browser/prefs/dummy_pref_store.h
@@ -0,0 +1,50 @@
+// 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 CHROME_BROWSER_PREFS_DUMMY_PREF_STORE_H_
+#define CHROME_BROWSER_PREFS_DUMMY_PREF_STORE_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "chrome/common/pref_store.h"
+
+class DictionaryValue;
+
+// |DummyPrefStore| is a stub implementation of the |PrefStore| interface.
+// It allows to get and set the state of the |PrefStore|.
+class DummyPrefStore : public PrefStore {
+ public:
+ DummyPrefStore();
+
+ virtual ~DummyPrefStore() {}
+
+ virtual DictionaryValue* prefs() { return prefs_.get(); }
+
+ virtual PrefStore::PrefReadError ReadPrefs();
+
+ virtual bool ReadOnly() { return read_only_; }
+
+ virtual bool WritePrefs();
+
+ // Getter and Setter methods for setting and getting the state of the
+ // |DummyPrefStore|.
+ virtual void set_read_only(bool read_only) { read_only_ = read_only; }
+ virtual void set_prefs(DictionaryValue* prefs) { prefs_.reset(prefs); }
+ virtual void set_prefs_written(bool status) { prefs_written_ = status; }
+ virtual bool get_prefs_written() { return prefs_written_; }
+
+ private:
+ scoped_ptr<DictionaryValue> prefs_;
+
+ // Flag that indicates if the PrefStore is read-only
+ bool read_only_;
+
+ // Flag that indicates if the method WritePrefs was called.
+ bool prefs_written_;
+
+ DISALLOW_COPY_AND_ASSIGN(DummyPrefStore);
+};
+
+#endif // CHROME_BROWSER_PREFS_DUMMY_PREF_STORE_H_
diff --git a/chrome/browser/prefs/pref_member.cc b/chrome/browser/prefs/pref_member.cc
new file mode 100644
index 0000000..f326419
--- /dev/null
+++ b/chrome/browser/prefs/pref_member.cc
@@ -0,0 +1,132 @@
+// 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 "chrome/browser/prefs/pref_member.h"
+
+#include "base/logging.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/common/notification_type.h"
+
+namespace subtle {
+
+PrefMemberBase::PrefMemberBase()
+ : observer_(NULL),
+ prefs_(NULL),
+ is_synced_(false),
+ setting_value_(false) {
+}
+
+PrefMemberBase::~PrefMemberBase() {
+ if (!pref_name_.empty())
+ prefs_->RemovePrefObserver(pref_name_.c_str(), this);
+}
+
+
+void PrefMemberBase::Init(const char* pref_name, PrefService* prefs,
+ NotificationObserver* observer) {
+ DCHECK(pref_name);
+ DCHECK(prefs);
+ DCHECK(pref_name_.empty()); // Check that Init is only called once.
+ observer_ = observer;
+ prefs_ = prefs;
+ pref_name_ = pref_name;
+ DCHECK(!pref_name_.empty());
+
+ // Add ourself as a pref observer so we can keep our local value in sync.
+ prefs_->AddPrefObserver(pref_name, this);
+}
+
+bool PrefMemberBase::IsManaged() {
+ DCHECK(!pref_name_.empty());
+ const PrefService::Preference* pref =
+ prefs_->FindPreference(pref_name_.c_str());
+ return pref && pref->IsManaged();
+}
+
+void PrefMemberBase::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(!pref_name_.empty());
+ DCHECK(NotificationType::PREF_CHANGED == type);
+ UpdateValueFromPref();
+ is_synced_ = true;
+ if (!setting_value_ && observer_)
+ observer_->Observe(type, source, details);
+}
+
+void PrefMemberBase::VerifyValuePrefName() {
+ DCHECK(!pref_name_.empty());
+}
+
+} // namespace subtle
+
+BooleanPrefMember::BooleanPrefMember() : PrefMember<bool>() {
+}
+
+BooleanPrefMember::~BooleanPrefMember() {
+}
+
+void BooleanPrefMember::UpdateValueFromPref() {
+ value_ = prefs()->GetBoolean(pref_name().c_str());
+}
+
+void BooleanPrefMember::UpdatePref(const bool& value) {
+ prefs()->SetBoolean(pref_name().c_str(), value);
+}
+
+IntegerPrefMember::IntegerPrefMember() : PrefMember<int>() {
+}
+
+IntegerPrefMember::~IntegerPrefMember() {
+}
+
+void IntegerPrefMember::UpdateValueFromPref() {
+ value_ = prefs()->GetInteger(pref_name().c_str());
+}
+
+void IntegerPrefMember::UpdatePref(const int& value) {
+ prefs()->SetInteger(pref_name().c_str(), value);
+}
+
+RealPrefMember::RealPrefMember() : PrefMember<double>() {
+}
+
+RealPrefMember::~RealPrefMember() {
+}
+
+void RealPrefMember::UpdateValueFromPref() {
+ value_ = prefs()->GetReal(pref_name().c_str());
+}
+
+void RealPrefMember::UpdatePref(const double& value) {
+ prefs()->SetReal(pref_name().c_str(), value);
+}
+
+StringPrefMember::StringPrefMember() : PrefMember<std::string>() {
+}
+
+StringPrefMember::~StringPrefMember() {
+}
+
+void StringPrefMember::UpdateValueFromPref() {
+ value_ = prefs()->GetString(pref_name().c_str());
+}
+
+void StringPrefMember::UpdatePref(const std::string& value) {
+ prefs()->SetString(pref_name().c_str(), value);
+}
+
+FilePathPrefMember::FilePathPrefMember() : PrefMember<FilePath>() {
+}
+
+FilePathPrefMember::~FilePathPrefMember() {
+}
+
+void FilePathPrefMember::UpdateValueFromPref() {
+ value_ = prefs()->GetFilePath(pref_name().c_str());
+}
+
+void FilePathPrefMember::UpdatePref(const FilePath& value) {
+ prefs()->SetFilePath(pref_name().c_str(), value);
+}
diff --git a/chrome/browser/prefs/pref_member.h b/chrome/browser/prefs/pref_member.h
new file mode 100644
index 0000000..1a4e385
--- /dev/null
+++ b/chrome/browser/prefs/pref_member.h
@@ -0,0 +1,208 @@
+// 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.
+//
+// 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, NULL /* no observer */);
+// }
+// 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 CHROME_BROWSER_PREFS_PREF_MEMBER_H_
+#define CHROME_BROWSER_PREFS_PREF_MEMBER_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "chrome/common/notification_observer.h"
+
+class PrefService;
+
+namespace subtle {
+
+class PrefMemberBase : public NotificationObserver {
+ protected:
+ PrefMemberBase();
+ virtual ~PrefMemberBase();
+
+ // See PrefMember<> for description.
+ void Init(const char* pref_name, PrefService* prefs,
+ NotificationObserver* observer);
+
+ // See PrefMember<> for description.
+ bool IsManaged();
+
+ // NotificationObserver
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ void VerifyValuePrefName();
+
+ // This methods is used to do the actual sync with pref of the specified type.
+ virtual void UpdateValueFromPref() = 0;
+
+ const std::string& pref_name() const { return pref_name_; }
+ PrefService* prefs() { return prefs_; }
+
+ // Ordered the members to compact the class instance.
+ private:
+ std::string pref_name_;
+ NotificationObserver* observer_;
+ PrefService* prefs_;
+
+ protected:
+ bool is_synced_;
+ bool setting_value_;
+};
+
+} // 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. |observer| may be null if you
+ // don't want any notifications of changes.
+ void Init(const char* pref_name, PrefService* prefs,
+ NotificationObserver* observer) {
+ subtle::PrefMemberBase::Init(pref_name, prefs, observer);
+ }
+
+ // Check whether the pref is managed, i.e. controlled externally through
+ // enterprise configuration management (e.g. windows group policy). Returns
+ // false for unknown prefs.
+ bool IsManaged() {
+ return subtle::PrefMemberBase::IsManaged();
+ }
+
+ // Retrieve the value of the member variable.
+ ValueType GetValue() {
+ VerifyValuePrefName();
+ // We lazily fetch the value from the pref service the first time GetValue
+ // is called.
+ if (!is_synced_) {
+ UpdateValueFromPref();
+ is_synced_ = true;
+ }
+ return value_;
+ }
+
+ // Provided as a convenience.
+ ValueType operator*() {
+ return GetValue();
+ }
+
+ // Set the value of the member variable.
+ void SetValue(const ValueType& value) {
+ VerifyValuePrefName();
+ setting_value_ = true;
+ UpdatePref(value);
+ setting_value_ = false;
+ }
+
+ // Set the value of the member variable if it is not managed.
+ void SetValueIfNotManaged(const ValueType& value) {
+ if (!IsManaged()) {
+ SetValue(value);
+ }
+ }
+
+ protected:
+ // This methods is used to do the actual sync with pref of the specified type.
+ virtual void UpdatePref(const ValueType& value) = 0;
+
+ // We cache the value of the pref so we don't have to keep walking the pref
+ // tree.
+ ValueType value_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Implementations of Boolean, Integer, Real, and String PrefMember below.
+
+class BooleanPrefMember : public PrefMember<bool> {
+ public:
+ BooleanPrefMember();
+ virtual ~BooleanPrefMember();
+
+ protected:
+ virtual void UpdateValueFromPref();
+ virtual void UpdatePref(const bool& value);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BooleanPrefMember);
+};
+
+class IntegerPrefMember : public PrefMember<int> {
+ public:
+ IntegerPrefMember();
+ virtual ~IntegerPrefMember();
+
+ protected:
+ virtual void UpdateValueFromPref();
+ virtual void UpdatePref(const int& value);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IntegerPrefMember);
+};
+
+class RealPrefMember : public PrefMember<double> {
+ public:
+ RealPrefMember();
+ virtual ~RealPrefMember();
+
+ protected:
+ virtual void UpdateValueFromPref();
+ virtual void UpdatePref(const double& value);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RealPrefMember);
+};
+
+class StringPrefMember : public PrefMember<std::string> {
+ public:
+ StringPrefMember();
+ virtual ~StringPrefMember();
+
+ protected:
+ virtual void UpdateValueFromPref();
+ virtual void UpdatePref(const std::string& value);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StringPrefMember);
+};
+
+class FilePathPrefMember : public PrefMember<FilePath> {
+ public:
+ FilePathPrefMember();
+ virtual ~FilePathPrefMember();
+
+ protected:
+ virtual void UpdateValueFromPref();
+ virtual void UpdatePref(const FilePath& value);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FilePathPrefMember);
+};
+
+#endif // CHROME_BROWSER_PREFS_PREF_MEMBER_H_
diff --git a/chrome/browser/prefs/pref_member_unittest.cc b/chrome/browser/prefs/pref_member_unittest.cc
new file mode 100644
index 0000000..f59f937
--- /dev/null
+++ b/chrome/browser/prefs/pref_member_unittest.cc
@@ -0,0 +1,194 @@
+// 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 "chrome/browser/prefs/dummy_pref_store.h"
+#include "chrome/browser/prefs/pref_member.h"
+#include "chrome/browser/prefs/pref_value_store.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/test/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+static const char kBoolPref[] = "bool";
+static const char kIntPref[] = "int";
+static const char kRealPref[] = "real";
+static const char kStringPref[] = "string";
+
+void RegisterTestPrefs(PrefService* prefs) {
+ prefs->RegisterBooleanPref(kBoolPref, false);
+ prefs->RegisterIntegerPref(kIntPref, 0);
+ prefs->RegisterRealPref(kRealPref, 0.0);
+ prefs->RegisterStringPref(kStringPref, "default");
+}
+
+class PrefMemberTestClass : public NotificationObserver {
+ public:
+ explicit PrefMemberTestClass(PrefService* prefs)
+ : observe_cnt_(0), prefs_(prefs) {
+ str_.Init(kStringPref, prefs, this);
+ }
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(NotificationType::PREF_CHANGED == type);
+ PrefService* prefs_in = Source<PrefService>(source).ptr();
+ EXPECT_EQ(prefs_in, prefs_);
+ std::string* pref_name_in = Details<std::string>(details).ptr();
+ EXPECT_EQ(*pref_name_in, kStringPref);
+ EXPECT_EQ(str_.GetValue(), prefs_->GetString(kStringPref));
+ ++observe_cnt_;
+ }
+
+ StringPrefMember str_;
+ int observe_cnt_;
+
+ private:
+ PrefService* prefs_;
+};
+
+} // anonymous namespace
+
+TEST(PrefMemberTest, BasicGetAndSet) {
+ TestingPrefService prefs;
+ RegisterTestPrefs(&prefs);
+
+ // Test bool
+ BooleanPrefMember boolean;
+ boolean.Init(kBoolPref, &prefs, NULL);
+
+ // 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, NULL);
+
+ // 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 real (double)
+ RealPrefMember real;
+ real.Init(kRealPref, &prefs, NULL);
+
+ // Check the defaults
+ EXPECT_EQ(0.0, prefs.GetReal(kRealPref));
+ EXPECT_EQ(0.0, real.GetValue());
+ EXPECT_EQ(0.0, *real);
+
+ // Try changing through the member variable.
+ real.SetValue(1.0);
+ EXPECT_EQ(1.0, real.GetValue());
+ EXPECT_EQ(1.0, prefs.GetReal(kRealPref));
+ EXPECT_EQ(1.0, *real);
+
+ // Try changing back through the pref.
+ prefs.SetReal(kRealPref, 3.0);
+ EXPECT_EQ(3.0, prefs.GetReal(kRealPref));
+ EXPECT_EQ(3.0, real.GetValue());
+ EXPECT_EQ(3.0, *real);
+
+ // Test string
+ StringPrefMember string;
+ string.Init(kStringPref, &prefs, NULL);
+
+ // 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(PrefMemberTest, TwoPrefs) {
+ // Make sure two RealPrefMembers stay in sync.
+ TestingPrefService prefs;
+ RegisterTestPrefs(&prefs);
+
+ RealPrefMember pref1;
+ pref1.Init(kRealPref, &prefs, NULL);
+ RealPrefMember pref2;
+ pref2.Init(kRealPref, &prefs, NULL);
+
+ pref1.SetValue(2.3);
+ EXPECT_EQ(2.3, *pref2);
+
+ pref2.SetValue(3.5);
+ EXPECT_EQ(3.5, *pref1);
+
+ prefs.SetReal(kRealPref, 4.2);
+ EXPECT_EQ(4.2, *pref1);
+ EXPECT_EQ(4.2, *pref2);
+}
+
+TEST(PrefMemberTest, Observer) {
+ TestingPrefService prefs;
+ RegisterTestPrefs(&prefs);
+
+ 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(PrefMemberTest, NoInit) {
+ // Make sure not calling Init on a PrefMember doesn't cause problems.
+ IntegerPrefMember pref;
+}
diff --git a/chrome/browser/prefs/pref_notifier.cc b/chrome/browser/prefs/pref_notifier.cc
new file mode 100644
index 0000000..665bffb
--- /dev/null
+++ b/chrome/browser/prefs/pref_notifier.cc
@@ -0,0 +1,140 @@
+// 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 "chrome/browser/prefs/pref_notifier.h"
+
+#include "base/stl_util-inl.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/policy/configuration_policy_pref_store.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/prefs/pref_value_store.h"
+#include "chrome/common/notification_service.h"
+
+
+PrefNotifier::PrefNotifier(PrefService* service, PrefValueStore* value_store)
+ : pref_service_(service),
+ pref_value_store_(value_store) {
+ registrar_.Add(this,
+ NotificationType(NotificationType::POLICY_CHANGED),
+ NotificationService::AllSources());
+}
+
+PrefNotifier::~PrefNotifier() {
+ DCHECK(CalledOnValidThread());
+
+ // Verify that there are no pref observers when we shut down.
+ for (PrefObserverMap::iterator it = pref_observers_.begin();
+ it != pref_observers_.end(); ++it) {
+ NotificationObserverList::Iterator obs_iterator(*(it->second));
+ if (obs_iterator.GetNext()) {
+ LOG(WARNING) << "pref observer found at shutdown " << it->first;
+ }
+ }
+
+ STLDeleteContainerPairSecondPointers(pref_observers_.begin(),
+ pref_observers_.end());
+ pref_observers_.clear();
+}
+
+void PrefNotifier::OnPreferenceSet(const char* pref_name,
+ PrefNotifier::PrefStoreType new_store,
+ const Value* old_value) {
+ if (pref_value_store_->PrefHasChanged(pref_name, new_store, old_value))
+ FireObservers(pref_name);
+}
+
+void PrefNotifier::OnUserPreferenceSet(const char* pref_name,
+ const Value* old_value) {
+ OnPreferenceSet(pref_name, PrefNotifier::USER_STORE, old_value);
+}
+
+void PrefNotifier::FireObservers(const char* path) {
+ DCHECK(CalledOnValidThread());
+
+ // Convert path to a std::string because the Details constructor requires a
+ // class.
+ std::string path_str(path);
+ PrefObserverMap::iterator observer_iterator = pref_observers_.find(path_str);
+ if (observer_iterator == pref_observers_.end())
+ return;
+
+ NotificationObserverList::Iterator it(*(observer_iterator->second));
+ NotificationObserver* observer;
+ while ((observer = it.GetNext()) != NULL) {
+ observer->Observe(NotificationType::PREF_CHANGED,
+ Source<PrefService>(pref_service_),
+ Details<std::string>(&path_str));
+ }
+}
+
+void PrefNotifier::AddPrefObserver(const char* path,
+ NotificationObserver* obs) {
+ // Get the pref observer list associated with the path.
+ NotificationObserverList* observer_list = NULL;
+ PrefObserverMap::iterator observer_iterator = pref_observers_.find(path);
+ if (observer_iterator == pref_observers_.end()) {
+ observer_list = new NotificationObserverList;
+ pref_observers_[path] = observer_list;
+ } else {
+ observer_list = observer_iterator->second;
+ }
+
+ // Verify that this observer doesn't already exist.
+ NotificationObserverList::Iterator it(*observer_list);
+ NotificationObserver* existing_obs;
+ while ((existing_obs = it.GetNext()) != NULL) {
+ DCHECK(existing_obs != obs) << path << " observer already registered";
+ if (existing_obs == obs)
+ return;
+ }
+
+ // Ok, safe to add the pref observer.
+ observer_list->AddObserver(obs);
+}
+
+void PrefNotifier::RemovePrefObserver(const char* path,
+ NotificationObserver* obs) {
+ DCHECK(CalledOnValidThread());
+
+ PrefObserverMap::iterator observer_iterator = pref_observers_.find(path);
+ if (observer_iterator == pref_observers_.end()) {
+ return;
+ }
+
+ NotificationObserverList* observer_list = observer_iterator->second;
+ observer_list->RemoveObserver(obs);
+}
+
+void PrefNotifier::FireObserversForRefreshedManagedPrefs(
+ std::vector<std::string> changed_prefs_paths) {
+ DCHECK(CalledOnValidThread());
+ std::vector<std::string>::const_iterator current;
+ for (current = changed_prefs_paths.begin();
+ current != changed_prefs_paths.end();
+ ++current) {
+ FireObservers(current->c_str());
+ }
+}
+
+void PrefNotifier::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type == NotificationType::POLICY_CHANGED) {
+ PrefValueStore::AfterRefreshCallback* callback =
+ NewCallback(this,
+ &PrefNotifier::FireObserversForRefreshedManagedPrefs);
+ // The notification of the policy refresh can come from any thread,
+ // but the manipulation of the PrefValueStore must happen on the UI
+ // thread, thus the policy refresh must be explicitly started on it.
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ pref_value_store_,
+ &PrefValueStore::RefreshPolicyPrefs,
+ ConfigurationPolicyPrefStore::CreateManagedPolicyPrefStore(),
+ ConfigurationPolicyPrefStore::CreateRecommendedPolicyPrefStore(),
+
+ callback));
+ }
+}
diff --git a/chrome/browser/prefs/pref_notifier.h b/chrome/browser/prefs/pref_notifier.h
new file mode 100644
index 0000000..c627de9
--- /dev/null
+++ b/chrome/browser/prefs/pref_notifier.h
@@ -0,0 +1,112 @@
+// 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 CHROME_BROWSER_PREFS_PREF_NOTIFIER_H_
+#define CHROME_BROWSER_PREFS_PREF_NOTIFIER_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/hash_tables.h"
+#include "base/non_thread_safe.h"
+#include "base/observer_list.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+
+class NotificationObserver;
+class PrefService;
+class PrefValueStore;
+class Value;
+
+// Registers observers for particular preferences and sends notifications when
+// preference values or sources (i.e., which preference layer controls the
+// preference) change.
+class PrefNotifier : public NonThreadSafe,
+ public NotificationObserver {
+ public:
+ // PrefStores must be listed here in order from highest to lowest priority.
+ // MANAGED contains all managed (policy) preference values.
+ // 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 recommended (policy) preference values.
+ // This enum is kept in pref_notifier.h rather than pref_value_store.h in
+ // order to minimize additional headers needed by the *PrefStore files.
+ 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,
+ EXTENSION_STORE,
+ COMMAND_LINE_STORE,
+ USER_STORE,
+ RECOMMENDED_STORE,
+ PREF_STORE_TYPE_MAX = RECOMMENDED_STORE
+ };
+
+ // The |service| with which this notifier is associated will be sent as the
+ // source of any notifications.
+ PrefNotifier(PrefService* service, PrefValueStore* value_store);
+
+ virtual ~PrefNotifier();
+
+ // For the given pref_name, fire any observer of the pref if |old_value| is
+ // different from the current value, or if the store controlling the value
+ // has changed.
+ // TODO(pamg): Currently notifies in some cases when it shouldn't. See
+ // comments for PrefHasChanged() in pref_value_store.h.
+ void OnPreferenceSet(const char* pref_name,
+ PrefNotifier::PrefStoreType new_store,
+ const Value* old_value);
+
+ // Convenience method to be called when a preference is set in the
+ // USER_STORE. See OnPreferenceSet().
+ void OnUserPreferenceSet(const char* pref_name, const Value* old_value);
+
+ // For the given pref_name, fire any observer of the pref. Virtual so it can
+ // be mocked for unit testing.
+ virtual void FireObservers(const char* path);
+
+ // If the pref at the given path changes, we call the observer's Observe
+ // method with NOTIFY_PREF_CHANGED.
+ void AddPrefObserver(const char* path, NotificationObserver* obs);
+ void RemovePrefObserver(const char* path, NotificationObserver* obs);
+
+ protected:
+ // 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 ObserverList<NotificationObserver> NotificationObserverList;
+ typedef base::hash_map<std::string, NotificationObserverList*>
+ PrefObserverMap;
+ const PrefObserverMap* pref_observers() { return &pref_observers_; }
+
+ private:
+ // Weak references.
+ PrefService* pref_service_;
+ PrefValueStore* pref_value_store_;
+
+ NotificationRegistrar registrar_;
+
+ PrefObserverMap pref_observers_;
+
+ // Called after a policy refresh to notify relevant preference observers.
+ // |changed_prefs_paths| is the vector of preference paths changed by the
+ // policy update. It is passed by value and not reference because
+ // this method is called asynchronously as a callback from another thread.
+ // Copying the vector guarantees that the vector's lifecycle spans the
+ // method's invocation.
+ void FireObserversForRefreshedManagedPrefs(
+ std::vector<std::string> changed_prefs_paths);
+
+ // NotificationObserver methods:
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ DISALLOW_COPY_AND_ASSIGN(PrefNotifier);
+};
+
+#endif // CHROME_BROWSER_PREFS_PREF_NOTIFIER_H_
diff --git a/chrome/browser/prefs/pref_notifier_unittest.cc b/chrome/browser/prefs/pref_notifier_unittest.cc
new file mode 100644
index 0000000..5a1a6f4
--- /dev/null
+++ b/chrome/browser/prefs/pref_notifier_unittest.cc
@@ -0,0 +1,290 @@
+// 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 "chrome/browser/prefs/pref_notifier.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/prefs/pref_value_store.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/notification_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+namespace {
+
+const char kChangedPref[] = "changed_pref";
+const char kUnchangedPref[] = "unchanged_pref";
+
+bool DetailsAreChangedPref(const Details<std::string>& details) {
+ std::string* string_in = Details<std::string>(details).ptr();
+ return strcmp(string_in->c_str(), kChangedPref) == 0;
+}
+
+// Test PrefNotifier that allows tracking of observers and notifications.
+class MockPrefNotifier : public PrefNotifier {
+ public:
+ MockPrefNotifier(PrefService* prefs, PrefValueStore* value_store)
+ : PrefNotifier(prefs, value_store) {}
+
+ virtual ~MockPrefNotifier() {}
+
+ MOCK_METHOD1(FireObservers, void(const char* path));
+
+ void RealFireObservers(const char* path) {
+ PrefNotifier::FireObservers(path);
+ }
+
+ size_t CountObserver(const char* path, NotificationObserver* obs) {
+ PrefObserverMap::const_iterator observer_iterator =
+ pref_observers()->find(path);
+ if (observer_iterator == pref_observers()->end())
+ return false;
+
+ NotificationObserverList* observer_list = observer_iterator->second;
+ NotificationObserverList::Iterator it(*observer_list);
+ NotificationObserver* existing_obs;
+ size_t count = 0;
+ while ((existing_obs = it.GetNext()) != NULL) {
+ if (existing_obs == obs)
+ count++;
+ }
+
+ return count;
+ }
+};
+
+// Mock PrefValueStore that has no PrefStores and injects a simpler
+// PrefHasChanged().
+class MockPrefValueStore : public PrefValueStore {
+ public:
+ MockPrefValueStore() : PrefValueStore(NULL, NULL, NULL, NULL, NULL) {}
+
+ virtual ~MockPrefValueStore() {}
+
+ // This mock version returns true if the pref path starts with "changed".
+ virtual bool PrefHasChanged(const char* path,
+ PrefNotifier::PrefStoreType new_store,
+ const Value* old_value) {
+ std::string path_str(path);
+ if (path_str.compare(0, 7, "changed") == 0)
+ return true;
+ return false;
+ }
+};
+
+// Mock PrefService that allows the PrefNotifier to be injected.
+class MockPrefService : public PrefService {
+ public:
+ explicit MockPrefService(PrefValueStore* pref_value_store)
+ : PrefService(pref_value_store) {}
+
+ void SetPrefNotifier(PrefNotifier* notifier) {
+ pref_notifier_.reset(notifier);
+ }
+};
+
+// Mock PrefObserver that verifies notifications.
+class MockPrefObserver : public NotificationObserver {
+ public:
+ virtual ~MockPrefObserver() {}
+
+ MOCK_METHOD3(Observe, void(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details));
+};
+
+// Test fixture class.
+class PrefNotifierTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ value_store_ = new MockPrefValueStore;
+ pref_service_.reset(new MockPrefService(value_store_));
+ notifier_ = new MockPrefNotifier(pref_service_.get(), value_store_);
+ pref_service_->SetPrefNotifier(notifier_);
+ }
+
+ // The PrefService takes ownership of the PrefValueStore and PrefNotifier.
+ PrefValueStore* value_store_;
+ MockPrefNotifier* notifier_;
+ scoped_ptr<MockPrefService> pref_service_;
+
+ MockPrefObserver obs1_;
+ MockPrefObserver obs2_;
+};
+
+
+TEST_F(PrefNotifierTest, OnPreferenceSet) {
+ EXPECT_CALL(*notifier_, FireObservers(kChangedPref))
+ .Times(PrefNotifier::PREF_STORE_TYPE_MAX + 1);
+ EXPECT_CALL(*notifier_, FireObservers(kUnchangedPref)).Times(0);
+
+ for (size_t i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) {
+ notifier_->OnPreferenceSet(kChangedPref,
+ static_cast<PrefNotifier::PrefStoreType>(i), NULL);
+ notifier_->OnPreferenceSet(kUnchangedPref,
+ static_cast<PrefNotifier::PrefStoreType>(i), NULL);
+ }
+}
+
+TEST_F(PrefNotifierTest, OnUserPreferenceSet) {
+ EXPECT_CALL(*notifier_, FireObservers(kChangedPref));
+ EXPECT_CALL(*notifier_, FireObservers(kUnchangedPref)).Times(0);
+ notifier_->OnUserPreferenceSet(kChangedPref, NULL);
+ notifier_->OnUserPreferenceSet(kUnchangedPref, NULL);
+}
+
+TEST_F(PrefNotifierTest, AddAndRemovePrefObservers) {
+ const char pref_name[] = "homepage";
+ const char pref_name2[] = "proxy";
+
+ 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)
+ 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 // NDEBUG
+
+ // 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) {
+ using testing::_;
+ using testing::Field;
+ using testing::Invoke;
+ using testing::Mock;
+ using testing::Truly;
+
+ // Tell googlemock to pass calls to the mock's "back door" too.
+ ON_CALL(*notifier_, FireObservers(_))
+ .WillByDefault(Invoke(notifier_, &MockPrefNotifier::RealFireObservers));
+ EXPECT_CALL(*notifier_, FireObservers(kChangedPref)).Times(4);
+ EXPECT_CALL(*notifier_, FireObservers(kUnchangedPref)).Times(0);
+
+ notifier_->AddPrefObserver(kChangedPref, &obs1_);
+ notifier_->AddPrefObserver(kUnchangedPref, &obs1_);
+
+ EXPECT_CALL(obs1_, Observe(
+ Field(&NotificationType::value, NotificationType::PREF_CHANGED),
+ Source<PrefService>(pref_service_.get()),
+ Truly(DetailsAreChangedPref)));
+ EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0);
+ notifier_->OnUserPreferenceSet(kChangedPref, NULL);
+ Mock::VerifyAndClearExpectations(&obs1_);
+ Mock::VerifyAndClearExpectations(&obs2_);
+
+ EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0);
+ EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0);
+ notifier_->OnUserPreferenceSet(kUnchangedPref, NULL);
+ Mock::VerifyAndClearExpectations(&obs1_);
+ Mock::VerifyAndClearExpectations(&obs2_);
+
+ notifier_->AddPrefObserver(kChangedPref, &obs2_);
+ notifier_->AddPrefObserver(kUnchangedPref, &obs2_);
+
+ EXPECT_CALL(obs1_, Observe(
+ Field(&NotificationType::value, NotificationType::PREF_CHANGED),
+ Source<PrefService>(pref_service_.get()),
+ Truly(DetailsAreChangedPref)));
+ EXPECT_CALL(obs2_, Observe(
+ Field(&NotificationType::value, NotificationType::PREF_CHANGED),
+ Source<PrefService>(pref_service_.get()),
+ Truly(DetailsAreChangedPref)));
+ notifier_->OnUserPreferenceSet(kChangedPref, NULL);
+ Mock::VerifyAndClearExpectations(&obs1_);
+ Mock::VerifyAndClearExpectations(&obs2_);
+
+ EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0);
+ EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0);
+ notifier_->OnUserPreferenceSet(kUnchangedPref, NULL);
+ 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_, Observe(_, _, _)).Times(0);
+ EXPECT_CALL(obs2_, Observe(
+ Field(&NotificationType::value, NotificationType::PREF_CHANGED),
+ Source<PrefService>(pref_service_.get()),
+ Truly(DetailsAreChangedPref)));
+ notifier_->OnUserPreferenceSet(kChangedPref, NULL);
+ Mock::VerifyAndClearExpectations(&obs1_);
+ Mock::VerifyAndClearExpectations(&obs2_);
+
+ EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0);
+ EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0);
+ notifier_->OnUserPreferenceSet(kUnchangedPref, NULL);
+ Mock::VerifyAndClearExpectations(&obs1_);
+ Mock::VerifyAndClearExpectations(&obs2_);
+
+ // Make sure removing an observer entirely doesn't affect anything else.
+ notifier_->RemovePrefObserver(kUnchangedPref, &obs1_);
+
+ EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0);
+ EXPECT_CALL(obs2_, Observe(
+ Field(&NotificationType::value, NotificationType::PREF_CHANGED),
+ Source<PrefService>(pref_service_.get()),
+ Truly(DetailsAreChangedPref)));
+ notifier_->OnUserPreferenceSet(kChangedPref, NULL);
+ Mock::VerifyAndClearExpectations(&obs1_);
+ Mock::VerifyAndClearExpectations(&obs2_);
+
+ EXPECT_CALL(obs1_, Observe(_, _, _)).Times(0);
+ EXPECT_CALL(obs2_, Observe(_, _, _)).Times(0);
+ notifier_->OnUserPreferenceSet(kUnchangedPref, NULL);
+
+ notifier_->RemovePrefObserver(kChangedPref, &obs2_);
+ notifier_->RemovePrefObserver(kUnchangedPref, &obs2_);
+}
+
+} // namespace
diff --git a/chrome/browser/prefs/pref_service.cc b/chrome/browser/prefs/pref_service.cc
new file mode 100644
index 0000000..1ab671a
--- /dev/null
+++ b/chrome/browser/prefs/pref_service.cc
@@ -0,0 +1,737 @@
+// 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 "chrome/browser/prefs/pref_service.h"
+
+#include <algorithm>
+#include <string>
+
+#include "app/l10n_util.h"
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/histogram.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/stl_util-inl.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/sys_string_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/notification_service.h"
+#include "grit/chromium_strings.h"
+#include "grit/generated_resources.h"
+
+namespace {
+
+// A helper function for RegisterLocalized*Pref that creates a Value* based on
+// the string value in the locale dll. Because we control the values in a
+// locale dll, this should always return a Value of the appropriate type.
+Value* CreateLocaleDefaultValue(Value::ValueType type, int message_id) {
+ std::string resource_string = l10n_util::GetStringUTF8(message_id);
+ DCHECK(!resource_string.empty());
+ switch (type) {
+ case Value::TYPE_BOOLEAN: {
+ if ("true" == resource_string)
+ return Value::CreateBooleanValue(true);
+ if ("false" == resource_string)
+ return Value::CreateBooleanValue(false);
+ break;
+ }
+
+ case Value::TYPE_INTEGER: {
+ int val;
+ base::StringToInt(resource_string, &val);
+ return Value::CreateIntegerValue(val);
+ }
+
+ case Value::TYPE_REAL: {
+ double val;
+ base::StringToDouble(resource_string, &val);
+ return Value::CreateRealValue(val);
+ }
+
+ case Value::TYPE_STRING: {
+ return Value::CreateStringValue(resource_string);
+ }
+
+ default: {
+ NOTREACHED() <<
+ "list and dictionary types can not have default locale values";
+ }
+ }
+ NOTREACHED();
+ return Value::CreateNullValue();
+}
+
+// Forwards a notification after a PostMessage so that we can wait for the
+// MessageLoop to run.
+void NotifyReadError(PrefService* pref, int message_id) {
+ Source<PrefService> source(pref);
+ NotificationService::current()->Notify(NotificationType::PROFILE_ERROR,
+ source, Details<int>(&message_id));
+}
+
+} // namespace
+
+// static
+PrefService* PrefService::CreatePrefService(const FilePath& pref_filename,
+ Profile* profile) {
+#if defined(OS_LINUX)
+ // We'd like to see what fraction of our users have the preferences
+ // stored on a network file system, as we've had no end of troubles
+ // with NFS/AFS.
+ // TODO(evanm): remove this once we've collected state.
+ file_util::FileSystemType fstype;
+ if (file_util::GetFileSystemType(pref_filename.DirName(), &fstype)) {
+ UMA_HISTOGRAM_ENUMERATION("PrefService.FileSystemType",
+ static_cast<int>(fstype),
+ file_util::FILE_SYSTEM_TYPE_COUNT);
+ }
+#endif
+
+ return new PrefService(
+ PrefValueStore::CreatePrefValueStore(pref_filename, profile, false));
+}
+
+// static
+PrefService* PrefService::CreateUserPrefService(const FilePath& pref_filename) {
+ return new PrefService(
+ PrefValueStore::CreatePrefValueStore(pref_filename, NULL, true));
+}
+
+PrefService::PrefService(PrefValueStore* pref_value_store)
+ : pref_value_store_(pref_value_store) {
+ pref_notifier_.reset(new PrefNotifier(this, pref_value_store));
+ InitFromStorage();
+}
+
+PrefService::~PrefService() {
+ DCHECK(CalledOnValidThread());
+ STLDeleteContainerPointers(prefs_.begin(), prefs_.end());
+ prefs_.clear();
+}
+
+void PrefService::InitFromStorage() {
+ PrefStore::PrefReadError error = LoadPersistentPrefs();
+ if (error == PrefStore::PREF_READ_ERROR_NONE)
+ return;
+
+ // Failing to load prefs on startup is a bad thing(TM). See bug 38352 for
+ // an example problem that this can cause.
+ // Do some diagnosis and try to avoid losing data.
+ int message_id = 0;
+ if (error <= PrefStore::PREF_READ_ERROR_JSON_TYPE) {
+ message_id = IDS_PREFERENCES_CORRUPT_ERROR;
+ } else if (error != PrefStore::PREF_READ_ERROR_NO_FILE) {
+ message_id = IDS_PREFERENCES_UNREADABLE_ERROR;
+ }
+
+ if (message_id) {
+ ChromeThread::PostTask(ChromeThread::UI, FROM_HERE,
+ NewRunnableFunction(&NotifyReadError, this, message_id));
+ }
+ UMA_HISTOGRAM_ENUMERATION("PrefService.ReadError", error, 20);
+}
+
+bool PrefService::ReloadPersistentPrefs() {
+ return (LoadPersistentPrefs() == PrefStore::PREF_READ_ERROR_NONE);
+}
+
+PrefStore::PrefReadError PrefService::LoadPersistentPrefs() {
+ DCHECK(CalledOnValidThread());
+
+ PrefStore::PrefReadError pref_error = pref_value_store_->ReadPrefs();
+
+ for (PreferenceSet::iterator it = prefs_.begin();
+ it != prefs_.end(); ++it) {
+ (*it)->pref_value_store_ = pref_value_store_.get();
+ }
+
+ return pref_error;
+}
+
+bool PrefService::SavePersistentPrefs() {
+ DCHECK(CalledOnValidThread());
+
+ return pref_value_store_->WritePrefs();
+}
+
+void PrefService::ScheduleSavePersistentPrefs() {
+ DCHECK(CalledOnValidThread());
+
+ pref_value_store_->ScheduleWritePrefs();
+}
+
+void PrefService::RegisterBooleanPref(const char* path,
+ bool default_value) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ Value::CreateBooleanValue(default_value));
+ RegisterPreference(pref);
+}
+
+void PrefService::RegisterIntegerPref(const char* path, int default_value) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ Value::CreateIntegerValue(default_value));
+ RegisterPreference(pref);
+}
+
+void PrefService::RegisterRealPref(const char* path, double default_value) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ Value::CreateRealValue(default_value));
+ RegisterPreference(pref);
+}
+
+void PrefService::RegisterStringPref(const char* path,
+ const std::string& default_value) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ Value::CreateStringValue(default_value));
+ RegisterPreference(pref);
+}
+
+void PrefService::RegisterFilePathPref(const char* path,
+ const FilePath& default_value) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ Value::CreateStringValue(default_value.value()));
+ RegisterPreference(pref);
+}
+
+void PrefService::RegisterListPref(const char* path) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ new ListValue);
+ RegisterPreference(pref);
+}
+
+void PrefService::RegisterDictionaryPref(const char* path) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ new DictionaryValue());
+ RegisterPreference(pref);
+}
+
+void PrefService::RegisterLocalizedBooleanPref(const char* path,
+ int locale_default_message_id) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ CreateLocaleDefaultValue(Value::TYPE_BOOLEAN, locale_default_message_id));
+ RegisterPreference(pref);
+}
+
+void PrefService::RegisterLocalizedIntegerPref(const char* path,
+ int locale_default_message_id) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ CreateLocaleDefaultValue(Value::TYPE_INTEGER, locale_default_message_id));
+ RegisterPreference(pref);
+}
+
+void PrefService::RegisterLocalizedRealPref(const char* path,
+ int locale_default_message_id) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ CreateLocaleDefaultValue(Value::TYPE_REAL, locale_default_message_id));
+ RegisterPreference(pref);
+}
+
+void PrefService::RegisterLocalizedStringPref(const char* path,
+ int locale_default_message_id) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ CreateLocaleDefaultValue(Value::TYPE_STRING, locale_default_message_id));
+ RegisterPreference(pref);
+}
+
+bool PrefService::GetBoolean(const char* path) const {
+ DCHECK(CalledOnValidThread());
+
+ bool result = false;
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return result;
+ }
+ bool rv = pref->GetValue()->GetAsBoolean(&result);
+ DCHECK(rv);
+ return result;
+}
+
+int PrefService::GetInteger(const char* path) const {
+ DCHECK(CalledOnValidThread());
+
+ int result = 0;
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return result;
+ }
+ bool rv = pref->GetValue()->GetAsInteger(&result);
+ DCHECK(rv);
+ return result;
+}
+
+double PrefService::GetReal(const char* path) const {
+ DCHECK(CalledOnValidThread());
+
+ double result = 0.0;
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return result;
+ }
+ bool rv = pref->GetValue()->GetAsReal(&result);
+ DCHECK(rv);
+ return result;
+}
+
+std::string PrefService::GetString(const char* path) const {
+ DCHECK(CalledOnValidThread());
+
+ std::string result;
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return result;
+ }
+ bool rv = pref->GetValue()->GetAsString(&result);
+ DCHECK(rv);
+ return result;
+}
+
+FilePath PrefService::GetFilePath(const char* path) const {
+ DCHECK(CalledOnValidThread());
+
+ FilePath::StringType result;
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return FilePath(result);
+ }
+ bool rv = pref->GetValue()->GetAsString(&result);
+ DCHECK(rv);
+#if defined(OS_POSIX)
+ // We store filepaths as UTF8, so convert it back to the system type.
+ result = base::SysWideToNativeMB(UTF8ToWide(result));
+#endif
+ return FilePath(result);
+}
+
+bool PrefService::HasPrefPath(const char* path) const {
+ return pref_value_store_->HasPrefPath(path);
+}
+
+const PrefService::Preference* PrefService::FindPreference(
+ const char* pref_name) const {
+ DCHECK(CalledOnValidThread());
+ Preference p(NULL, pref_name, NULL);
+ PreferenceSet::const_iterator it = prefs_.find(&p);
+ return it == prefs_.end() ? NULL : *it;
+}
+
+bool PrefService::IsManagedPreference(const char* pref_name) const {
+ const Preference* pref = FindPreference(pref_name);
+ if (pref && pref->IsManaged()) {
+ return true;
+ }
+ return false;
+}
+
+const DictionaryValue* PrefService::GetDictionary(const char* path) const {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return NULL;
+ }
+ const Value* value = pref->GetValue();
+ if (value->GetType() == Value::TYPE_NULL)
+ return NULL;
+ return static_cast<const DictionaryValue*>(value);
+}
+
+const ListValue* PrefService::GetList(const char* path) const {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return NULL;
+ }
+ const Value* value = pref->GetValue();
+ if (value->GetType() == Value::TYPE_NULL)
+ return NULL;
+ return static_cast<const ListValue*>(value);
+}
+
+void PrefService::AddPrefObserver(const char* path,
+ NotificationObserver* obs) {
+ pref_notifier_->AddPrefObserver(path, obs);
+}
+
+void PrefService::RemovePrefObserver(const char* path,
+ NotificationObserver* obs) {
+ pref_notifier_->RemovePrefObserver(path, obs);
+}
+
+void PrefService::RegisterPreference(Preference* pref) {
+ DCHECK(CalledOnValidThread());
+
+ if (FindPreference(pref->name().c_str())) {
+ NOTREACHED() << "Tried to register duplicate pref " << pref->name();
+ delete pref;
+ return;
+ }
+ prefs_.insert(pref);
+}
+
+void PrefService::ClearPref(const char* path) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to clear an unregistered pref: " << path;
+ return;
+ }
+ Value* value;
+ bool has_old_value = pref_value_store_->GetValue(path, &value);
+ pref_value_store_->RemoveUserPrefValue(path);
+
+ // TODO(pamg): Removing the user value when there's a higher-priority setting
+ // doesn't change the value and shouldn't fire observers.
+ if (has_old_value)
+ pref_notifier_->FireObservers(path);
+}
+
+void PrefService::Set(const char* path, const Value& value) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to write an unregistered pref: " << path;
+ return;
+ }
+
+ // Allow dictionary and list types to be set to null.
+ if (value.GetType() == Value::TYPE_NULL &&
+ (pref->type() == Value::TYPE_DICTIONARY ||
+ pref->type() == Value::TYPE_LIST)) {
+ scoped_ptr<Value> old_value(GetPrefCopy(path));
+ if (!old_value->Equals(&value)) {
+ pref_value_store_->RemoveUserPrefValue(path);
+ pref_notifier_->FireObservers(path);
+ }
+ return;
+ }
+
+ if (pref->type() != value.GetType()) {
+ NOTREACHED() << "Wrong type for Set: " << path;
+ }
+
+ scoped_ptr<Value> old_value(GetPrefCopy(path));
+ pref_value_store_->SetUserPrefValue(path, value.DeepCopy());
+
+ pref_notifier_->OnUserPreferenceSet(path, old_value.get());
+}
+
+void PrefService::SetBoolean(const char* path, bool value) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to write an unregistered pref: " << path;
+ return;
+ }
+ if (pref->IsManaged()) {
+ NOTREACHED() << "Preference is managed: " << path;
+ return;
+ }
+ if (pref->type() != Value::TYPE_BOOLEAN) {
+ NOTREACHED() << "Wrong type for SetBoolean: " << path;
+ return;
+ }
+
+ scoped_ptr<Value> old_value(GetPrefCopy(path));
+ Value* new_value = Value::CreateBooleanValue(value);
+ pref_value_store_->SetUserPrefValue(path, new_value);
+
+ pref_notifier_->OnUserPreferenceSet(path, old_value.get());
+}
+
+void PrefService::SetInteger(const char* path, int value) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to write an unregistered pref: " << path;
+ return;
+ }
+ if (pref->IsManaged()) {
+ NOTREACHED() << "Preference is managed: " << path;
+ return;
+ }
+ if (pref->type() != Value::TYPE_INTEGER) {
+ NOTREACHED() << "Wrong type for SetInteger: " << path;
+ return;
+ }
+
+ scoped_ptr<Value> old_value(GetPrefCopy(path));
+ Value* new_value = Value::CreateIntegerValue(value);
+ pref_value_store_->SetUserPrefValue(path, new_value);
+
+ pref_notifier_->OnUserPreferenceSet(path, old_value.get());
+}
+
+void PrefService::SetReal(const char* path, double value) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to write an unregistered pref: " << path;
+ return;
+ }
+ if (pref->IsManaged()) {
+ NOTREACHED() << "Preference is managed: " << path;
+ return;
+ }
+ if (pref->type() != Value::TYPE_REAL) {
+ NOTREACHED() << "Wrong type for SetReal: " << path;
+ return;
+ }
+
+ scoped_ptr<Value> old_value(GetPrefCopy(path));
+ Value* new_value = Value::CreateRealValue(value);
+ pref_value_store_->SetUserPrefValue(path, new_value);
+
+ pref_notifier_->OnUserPreferenceSet(path, old_value.get());
+}
+
+void PrefService::SetString(const char* path, const std::string& value) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to write an unregistered pref: " << path;
+ return;
+ }
+ if (pref->IsManaged()) {
+ NOTREACHED() << "Preference is managed: " << path;
+ return;
+ }
+ if (pref->type() != Value::TYPE_STRING) {
+ NOTREACHED() << "Wrong type for SetString: " << path;
+ return;
+ }
+
+ scoped_ptr<Value> old_value(GetPrefCopy(path));
+ Value* new_value = Value::CreateStringValue(value);
+ pref_value_store_->SetUserPrefValue(path, new_value);
+
+ pref_notifier_->OnUserPreferenceSet(path, old_value.get());
+}
+
+void PrefService::SetFilePath(const char* path, const FilePath& value) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to write an unregistered pref: " << path;
+ return;
+ }
+ if (pref->IsManaged()) {
+ NOTREACHED() << "Preference is managed: " << path;
+ return;
+ }
+ if (pref->type() != Value::TYPE_STRING) {
+ NOTREACHED() << "Wrong type for SetFilePath: " << path;
+ return;
+ }
+
+ scoped_ptr<Value> old_value(GetPrefCopy(path));
+#if defined(OS_POSIX)
+ // Value::SetString only knows about UTF8 strings, so convert the path from
+ // the system native value to UTF8.
+ std::string path_utf8 = WideToUTF8(base::SysNativeMBToWide(value.value()));
+ Value* new_value = Value::CreateStringValue(path_utf8);
+ pref_value_store_->SetUserPrefValue(path, new_value);
+#else
+ Value* new_value = Value::CreateStringValue(value.value());
+ pref_value_store_->SetUserPrefValue(path, new_value);
+#endif
+
+ pref_notifier_->OnUserPreferenceSet(path, old_value.get());
+}
+
+void PrefService::SetInt64(const char* path, int64 value) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to write an unregistered pref: " << path;
+ return;
+ }
+ if (pref->IsManaged()) {
+ NOTREACHED() << "Preference is managed: " << path;
+ return;
+ }
+ if (pref->type() != Value::TYPE_STRING) {
+ NOTREACHED() << "Wrong type for SetInt64: " << path;
+ return;
+ }
+
+ scoped_ptr<Value> old_value(GetPrefCopy(path));
+ Value* new_value = Value::CreateStringValue(base::Int64ToString(value));
+ pref_value_store_->SetUserPrefValue(path, new_value);
+
+ pref_notifier_->OnUserPreferenceSet(path, old_value.get());
+}
+
+int64 PrefService::GetInt64(const char* path) const {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to read an unregistered pref: " << path;
+ return 0;
+ }
+ std::string result("0");
+ bool rv = pref->GetValue()->GetAsString(&result);
+ DCHECK(rv);
+
+ int64 val;
+ base::StringToInt64(result, &val);
+ return val;
+}
+
+void PrefService::RegisterInt64Pref(const char* path, int64 default_value) {
+ Preference* pref = new Preference(pref_value_store_.get(), path,
+ Value::CreateStringValue(base::Int64ToString(default_value)));
+ RegisterPreference(pref);
+}
+
+DictionaryValue* PrefService::GetMutableDictionary(const char* path) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to get an unregistered pref: " << path;
+ return NULL;
+ }
+ if (pref->type() != Value::TYPE_DICTIONARY) {
+ NOTREACHED() << "Wrong type for GetMutableDictionary: " << path;
+ return NULL;
+ }
+
+ DictionaryValue* dict = NULL;
+ Value* tmp_value = NULL;
+ if (!pref_value_store_->GetValue(path, &tmp_value) ||
+ !tmp_value->IsType(Value::TYPE_DICTIONARY)) {
+ dict = new DictionaryValue;
+ pref_value_store_->SetUserPrefValue(path, dict);
+ } else {
+ dict = static_cast<DictionaryValue*>(tmp_value);
+ }
+ return dict;
+}
+
+ListValue* PrefService::GetMutableList(const char* path) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ if (!pref) {
+ NOTREACHED() << "Trying to get an unregistered pref: " << path;
+ return NULL;
+ }
+ if (pref->type() != Value::TYPE_LIST) {
+ NOTREACHED() << "Wrong type for GetMutableList: " << path;
+ return NULL;
+ }
+
+ ListValue* list = NULL;
+ Value* tmp_value = NULL;
+ if (!pref_value_store_->GetValue(path, &tmp_value)) {
+ list = new ListValue;
+ pref_value_store_->SetUserPrefValue(path, list);
+ } else {
+ list = static_cast<ListValue*>(tmp_value);
+ }
+ return list;
+}
+
+Value* PrefService::GetPrefCopy(const char* path) {
+ DCHECK(CalledOnValidThread());
+
+ const Preference* pref = FindPreference(path);
+ DCHECK(pref);
+ return pref->GetValue()->DeepCopy();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// PrefService::Preference
+
+PrefService::Preference::Preference(PrefValueStore* pref_value_store,
+ const char* name,
+ Value* default_value)
+ : type_(Value::TYPE_NULL),
+ name_(name),
+ default_value_(default_value),
+ pref_value_store_(pref_value_store) {
+ DCHECK(name);
+
+ if (default_value) {
+ type_ = default_value->GetType();
+ DCHECK(type_ != Value::TYPE_NULL && type_ != Value::TYPE_BINARY) <<
+ "invalid preference type: " << type_;
+ }
+
+ // We set the default value of lists and dictionaries to be null so it's
+ // easier for callers to check for empty list/dict prefs.
+ if (Value::TYPE_LIST == type_ || Value::TYPE_DICTIONARY == type_)
+ default_value_.reset(Value::CreateNullValue());
+}
+
+const Value* PrefService::Preference::GetValue() const {
+ DCHECK(NULL != pref_value_store_) <<
+ "Must register pref before getting its value";
+
+ Value* temp_value = NULL;
+ if (pref_value_store_->GetValue(name_, &temp_value) &&
+ temp_value->GetType() == type_) {
+ return temp_value;
+ }
+
+ // Pref not found, just return the app default.
+ return default_value_.get();
+}
+
+bool PrefService::Preference::IsDefaultValue() const {
+ DCHECK(default_value_.get());
+ return default_value_->Equals(GetValue());
+}
+
+bool PrefService::Preference::IsManaged() const {
+ return pref_value_store_->PrefValueInManagedStore(name_.c_str());
+}
+
+bool PrefService::Preference::HasExtensionSetting() const {
+ return pref_value_store_->PrefValueInExtensionStore(name_.c_str());
+}
+
+bool PrefService::Preference::HasUserSetting() const {
+ return pref_value_store_->PrefValueInUserStore(name_.c_str());
+}
+
+bool PrefService::Preference::IsExtensionControlled() const {
+ return pref_value_store_->PrefValueFromExtensionStore(name_.c_str());
+}
+
+bool PrefService::Preference::IsUserControlled() const {
+ return pref_value_store_->PrefValueFromUserStore(name_.c_str());
+}
+
+bool PrefService::Preference::IsUserModifiable() const {
+ return pref_value_store_->PrefValueUserModifiable(name_.c_str());
+}
diff --git a/chrome/browser/prefs/pref_service.h b/chrome/browser/prefs/pref_service.h
new file mode 100644
index 0000000..07fa5e5
--- /dev/null
+++ b/chrome/browser/prefs/pref_service.h
@@ -0,0 +1,254 @@
+// 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.
+
+// This provides a way to access the application's current preferences.
+
+#ifndef CHROME_BROWSER_PREFS_PREF_SERVICE_H_
+#define CHROME_BROWSER_PREFS_PREF_SERVICE_H_
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "base/non_thread_safe.h"
+#include "base/scoped_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/prefs/pref_value_store.h"
+#include "chrome/common/pref_store.h"
+
+class FilePath;
+class NotificationObserver;
+class PrefNotifier;
+class Profile;
+
+class PrefService : public NonThreadSafe {
+ public:
+ // A helper class to store all the information associated with a preference.
+ class Preference {
+ public:
+
+ // The type of the preference is determined by the type of |default_value|.
+ // Therefore, the type needs to be a boolean, integer, real, string,
+ // dictionary (a branch), or list. You shouldn't need to construct this on
+ // your own, use the PrefService::Register*Pref methods instead.
+ // |default_value| will be owned by the Preference object.
+ Preference(PrefValueStore* pref_value_store,
+ const char* name,
+ Value* default_value);
+ ~Preference() {}
+
+ Value::ValueType type() const { return type_; }
+
+ // Returns the name of the Preference (i.e., the key, e.g.,
+ // browser.window_placement).
+ const std::string name() const { return name_; }
+
+ // Returns the value of the Preference. If there is no user specified
+ // value, it returns the default value.
+ const Value* GetValue() const;
+
+ // Returns true if the current value matches the default value.
+ bool IsDefaultValue() 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 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 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;
+
+ private:
+ friend class PrefService;
+
+ Value::ValueType type_;
+ std::string name_;
+ scoped_ptr<Value> default_value_;
+
+ // A reference to the pref service's pref_value_store_.
+ PrefValueStore* pref_value_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(Preference);
+ };
+
+ // Factory method that creates a new instance of a PrefService with
+ // a PrefValueStore containing all platform-applicable PrefStores.
+ // The |pref_filename| points to the user preference file. The |profile| is
+ // the one to which these preferences apply; it may be NULL if we're dealing
+ // with the local state. This is the usual way to create a new PrefService.
+ static PrefService* CreatePrefService(const FilePath& pref_filename,
+ Profile* profile);
+
+ // Convenience factory method for use in unit tests. Creates a new
+ // PrefService that uses a PrefValueStore with user preferences at the given
+ // |pref_filename|, and no other PrefStores (i.e., no other types of
+ // preferences).
+ static PrefService* CreateUserPrefService(const FilePath& pref_filename);
+
+ // This constructor is primarily used by tests. The |pref_value_store|
+ // provides preference values.
+ explicit PrefService(PrefValueStore* pref_value_store);
+
+ virtual ~PrefService();
+
+ // Reloads the data from file. This should only be called when the importer
+ // is running during first run, and the main process may not change pref
+ // values while the importer process is running. Returns true on success.
+ bool ReloadPersistentPrefs();
+
+ // Returns true if the preference for the given preference name is available
+ // and is managed.
+ bool IsManagedPreference(const char* pref_name) const;
+
+ // Writes the data to disk. The return value only reflects whether
+ // serialization was successful; we don't know whether the data actually made
+ // it on disk (since it's on a different thread). This should only be used if
+ // we need to save immediately (basically, during shutdown). Otherwise, you
+ // should use ScheduleSavePersistentPrefs.
+ bool SavePersistentPrefs();
+
+ // Serializes the data and schedules save using ImportantFileWriter.
+ void ScheduleSavePersistentPrefs();
+
+ // Make the PrefService aware of a pref.
+ void RegisterBooleanPref(const char* path, bool default_value);
+ void RegisterIntegerPref(const char* path, int default_value);
+ void RegisterRealPref(const char* path, double default_value);
+ void RegisterStringPref(const char* path, const std::string& default_value);
+ void RegisterFilePathPref(const char* path, const FilePath& default_value);
+ void RegisterListPref(const char* path);
+ void RegisterDictionaryPref(const char* path);
+
+ // These varients use a default value from the locale dll instead.
+ void RegisterLocalizedBooleanPref(const char* path,
+ int locale_default_message_id);
+ void RegisterLocalizedIntegerPref(const char* path,
+ int locale_default_message_id);
+ void RegisterLocalizedRealPref(const char* path,
+ int locale_default_message_id);
+ void RegisterLocalizedStringPref(const char* path,
+ int locale_default_message_id);
+
+ // 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 char* path) const;
+ int GetInteger(const char* path) const;
+ double GetReal(const char* path) const;
+ std::string GetString(const char* path) const;
+ FilePath GetFilePath(const char* path) const;
+
+ // Returns the branch if it exists. If it's not a branch or the branch does
+ // not exist, returns NULL.
+ const DictionaryValue* GetDictionary(const char* path) const;
+ const ListValue* GetList(const char* path) const;
+
+ // If the pref at the given path changes, we call the observer's Observe
+ // method with NOTIFY_PREF_CHANGED.
+ virtual void AddPrefObserver(const char* path, NotificationObserver* obs);
+ void RemovePrefObserver(const char* path, NotificationObserver* obs);
+
+ // Removes a user pref and restores the pref to its default value.
+ void ClearPref(const char* path);
+
+ // If the path is valid (i.e., registered), update the pref value in the user
+ // prefs.
+ void Set(const char* path, const Value& value);
+ void SetBoolean(const char* path, bool value);
+ void SetInteger(const char* path, int value);
+ void SetReal(const char* path, double value);
+ void SetString(const char* path, const std::string& value);
+ void SetFilePath(const char* path, const 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 char* path, int64 value);
+ int64 GetInt64(const char* path) const;
+ void RegisterInt64Pref(const char* path, int64 default_value);
+
+ // Used to set the value of dictionary or list values in the pref tree. This
+ // will create a dictionary or list if one does not exist in the pref tree.
+ // This method returns NULL only if you're requesting an unregistered pref or
+ // a non-dict/non-list pref.
+ // WARNING: Changes to the dictionary or list will not automatically notify
+ // pref observers.
+ // Use a ScopedPrefUpdate to update observers on changes.
+ DictionaryValue* GetMutableDictionary(const char* path);
+ ListValue* GetMutableList(const char* path);
+
+ // 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 char* path) const;
+
+ class PreferencePathComparator {
+ public:
+ bool operator() (Preference* lhs, Preference* rhs) const {
+ return lhs->name() < rhs->name();
+ }
+ };
+ typedef std::set<Preference*, PreferencePathComparator> PreferenceSet;
+ const PreferenceSet& preference_set() const { return prefs_; }
+
+ // A helper method to quickly look up a preference. Returns NULL if the
+ // preference is not registered.
+ const Preference* FindPreference(const char* pref_name) const;
+
+ bool read_only() const { return pref_value_store_->ReadOnly(); }
+
+ PrefNotifier* pref_notifier() const { return pref_notifier_.get(); }
+
+ 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<PrefNotifier> pref_notifier_;
+
+ private:
+ // Add a preference to the PreferenceMap. If the pref already exists, return
+ // false. This method takes ownership of |pref|.
+ void RegisterPreference(Preference* pref);
+
+ // Returns a copy of the current pref value. The caller is responsible for
+ // deleting the returned object.
+ Value* GetPrefCopy(const char* pref_name);
+
+ // Load from disk. Returns a non-zero error code on failure.
+ PrefStore::PrefReadError LoadPersistentPrefs();
+
+ // Load preferences from storage, attempting to diagnose and handle errors.
+ // This should only be called from the constructor.
+ void InitFromStorage();
+
+ // The PrefValueStore provides prioritized preference values. It is created
+ // and owned by this PrefService. Subclasses may access it for unit testing.
+ scoped_refptr<PrefValueStore> pref_value_store_;
+
+ // A set of all the registered Preference objects.
+ PreferenceSet prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefService);
+};
+
+#endif // CHROME_BROWSER_PREFS_PREF_SERVICE_H_
diff --git a/chrome/browser/prefs/pref_service_uitest.cc b/chrome/browser/prefs/pref_service_uitest.cc
new file mode 100644
index 0000000..9712481
--- /dev/null
+++ b/chrome/browser/prefs/pref_service_uitest.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2009 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/command_line.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/test/test_file_util.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/json_value_serializer.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/window_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+#include "gfx/rect.h"
+
+class PreferenceServiceTest : public UITest {
+ public:
+ void SetUp() {
+ PathService::Get(base::DIR_TEMP, &tmp_profile_);
+ tmp_profile_ = tmp_profile_.AppendASCII("tmp_profile");
+
+ // Create a fresh, empty copy of this directory.
+ file_util::Delete(tmp_profile_, true);
+ file_util::CreateDirectory(tmp_profile_);
+
+ FilePath reference_pref_file =
+ test_data_directory_
+ .AppendASCII("profiles")
+ .AppendASCII("window_placement")
+ .Append(chrome::kLocalStateFilename);
+
+ tmp_pref_file_ = tmp_profile_.Append(chrome::kLocalStateFilename);
+
+ ASSERT_TRUE(file_util::PathExists(reference_pref_file));
+
+ // Copy only the Local State file, the rest will be automatically created
+ ASSERT_TRUE(file_util::CopyFile(reference_pref_file, tmp_pref_file_));
+
+#if defined(OS_WIN)
+ // Make the copy writable. On POSIX we assume the umask allows files
+ // we create to be writable.
+ ASSERT_TRUE(::SetFileAttributesW(tmp_pref_file_.value().c_str(),
+ FILE_ATTRIBUTE_NORMAL));
+#endif
+
+ launch_arguments_.AppendSwitchPath(switches::kUserDataDir, tmp_profile_);
+ }
+
+ bool LaunchAppWithProfile() {
+ if (!file_util::PathExists(tmp_pref_file_))
+ return false;
+ UITest::SetUp();
+ return true;
+ }
+
+ void TearDown() {
+ UITest::TearDown();
+
+ EXPECT_TRUE(file_util::DieFileDie(tmp_profile_, true));
+ }
+
+ public:
+ FilePath tmp_pref_file_;
+ FilePath tmp_profile_;
+};
+
+#if defined(OS_WIN)
+// This test verifies that the window position from the prefs file is restored
+// when the app restores. This doesn't really make sense on Linux, where
+// the window manager might fight with you over positioning. However, we
+// might be able to make this work on buildbots.
+// Also, not sure what should happen on the mac. In any case, the code below
+// (minus the Windows bits) compiles fine on my Linux box now.
+// TODO(port): revisit this.
+TEST_F(PreferenceServiceTest, PreservedWindowPlacementIsLoaded) {
+ // The window should open with the reference profile
+ ASSERT_TRUE(LaunchAppWithProfile());
+
+ ASSERT_TRUE(file_util::PathExists(tmp_pref_file_));
+
+ JSONFileValueSerializer deserializer(tmp_pref_file_);
+ scoped_ptr<Value> root(deserializer.Deserialize(NULL, NULL));
+
+ ASSERT_TRUE(root.get());
+ ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+
+ DictionaryValue* root_dict = static_cast<DictionaryValue*>(root.get());
+
+ // Retrieve the screen rect for the launched window
+ scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser.get());
+ scoped_refptr<WindowProxy> window(browser->GetWindow());
+ ASSERT_TRUE(window.get());
+
+ gfx::Rect bounds;
+ ASSERT_TRUE(window->GetBounds(&bounds));
+
+ // Retrieve the expected rect values from "Preferences"
+ int bottom = 0;
+ std::string kBrowserWindowPlacement(prefs::kBrowserWindowPlacement);
+ EXPECT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + ".bottom",
+ &bottom));
+ EXPECT_EQ(bottom, bounds.y() + bounds.height());
+
+ int top = 0;
+ EXPECT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + ".top",
+ &top));
+ EXPECT_EQ(top, bounds.y());
+
+ int left = 0;
+ EXPECT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + ".left",
+ &left));
+ EXPECT_EQ(left, bounds.x());
+
+ int right = 0;
+ EXPECT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + ".right",
+ &right));
+ EXPECT_EQ(right, bounds.x() + bounds.width());
+
+ // Find if launched window is maximized.
+ bool is_window_maximized = false;
+ ASSERT_TRUE(window->IsMaximized(&is_window_maximized));
+ bool is_maximized = false;
+ EXPECT_TRUE(root_dict->GetBoolean(kBrowserWindowPlacement + ".maximized",
+ &is_maximized));
+ EXPECT_EQ(is_maximized, is_window_maximized);
+}
+#endif
diff --git a/chrome/browser/prefs/pref_service_unittest.cc b/chrome/browser/prefs/pref_service_unittest.cc
new file mode 100644
index 0000000..b6dd531
--- /dev/null
+++ b/chrome/browser/prefs/pref_service_unittest.cc
@@ -0,0 +1,289 @@
+// 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 <string>
+
+#include "app/test/data/resource.h"
+#include "base/scoped_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/prefs/dummy_pref_store.h"
+#include "chrome/browser/prefs/pref_value_store.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/notification_observer_mock.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Mock;
+using testing::Pointee;
+using testing::Property;
+
+class TestPrefObserver : public NotificationObserver {
+ public:
+ TestPrefObserver(const PrefService* prefs,
+ const std::string& pref_name,
+ const std::string& new_pref_value)
+ : observer_fired_(false),
+ prefs_(prefs),
+ pref_name_(pref_name),
+ new_pref_value_(new_pref_value) {
+ }
+ virtual ~TestPrefObserver() {}
+
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ EXPECT_EQ(type.value, NotificationType::PREF_CHANGED);
+ PrefService* prefs_in = Source<PrefService>(source).ptr();
+ EXPECT_EQ(prefs_in, prefs_);
+ std::string* pref_name_in = Details<std::string>(details).ptr();
+ EXPECT_EQ(*pref_name_in, pref_name_);
+ EXPECT_EQ(new_pref_value_, prefs_in->GetString("homepage"));
+ observer_fired_ = true;
+ }
+
+ bool observer_fired() { return observer_fired_; }
+
+ void Reset(const std::string& new_pref_value) {
+ observer_fired_ = false;
+ new_pref_value_ = new_pref_value;
+ }
+
+ private:
+ bool observer_fired_;
+ const PrefService* prefs_;
+ const std::string pref_name_;
+ std::string new_pref_value_;
+};
+
+// TODO(port): port this test to POSIX.
+#if defined(OS_WIN)
+TEST(PrefServiceTest, LocalizedPrefs) {
+ TestingPrefService prefs;
+ const char kBoolean[] = "boolean";
+ const char kInteger[] = "integer";
+ const char kString[] = "string";
+ prefs.RegisterLocalizedBooleanPref(kBoolean, IDS_LOCALE_BOOL);
+ prefs.RegisterLocalizedIntegerPref(kInteger, IDS_LOCALE_INT);
+ prefs.RegisterLocalizedStringPref(kString, IDS_LOCALE_STRING);
+
+ // The locale default should take preference over the user default.
+ EXPECT_FALSE(prefs.GetBoolean(kBoolean));
+ EXPECT_EQ(1, prefs.GetInteger(kInteger));
+ EXPECT_EQ("hello", prefs.GetString(kString));
+
+ prefs.SetBoolean(kBoolean, true);
+ EXPECT_TRUE(prefs.GetBoolean(kBoolean));
+ prefs.SetInteger(kInteger, 5);
+ EXPECT_EQ(5, prefs.GetInteger(kInteger));
+ prefs.SetString(kString, "foo");
+ EXPECT_EQ("foo", prefs.GetString(kString));
+}
+#endif
+
+TEST(PrefServiceTest, NoObserverFire) {
+ TestingPrefService prefs;
+
+ const char pref_name[] = "homepage";
+ prefs.RegisterStringPref(pref_name, "");
+
+ const std::string new_pref_value("http://www.google.com/");
+ TestPrefObserver obs(&prefs, pref_name, new_pref_value);
+ prefs.AddPrefObserver(pref_name, &obs);
+ // This should fire the checks in TestPrefObserver::Observe.
+ prefs.SetString(pref_name, new_pref_value);
+
+ // Make sure the observer was actually fired.
+ EXPECT_TRUE(obs.observer_fired());
+
+ // Setting the pref to the same value should not set the pref value a second
+ // time.
+ obs.Reset(new_pref_value);
+ prefs.SetString(pref_name, new_pref_value);
+ EXPECT_FALSE(obs.observer_fired());
+
+ // Clearing the pref should cause the pref to fire.
+ obs.Reset("");
+ prefs.ClearPref(pref_name);
+ EXPECT_TRUE(obs.observer_fired());
+
+ // Clearing the pref again should not cause the pref to fire.
+ obs.Reset("");
+ prefs.ClearPref(pref_name);
+ EXPECT_FALSE(obs.observer_fired());
+
+ // Ok, clean up.
+ prefs.RemovePrefObserver(pref_name, &obs);
+}
+
+TEST(PrefServiceTest, HasPrefPath) {
+ TestingPrefService 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.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";
+
+ TestingPrefService prefs;
+ prefs.SetUserPref(pref_name, Value::CreateStringValue("http://www.cnn.com"));
+ prefs.RegisterStringPref(pref_name, "");
+
+ const std::string new_pref_value("http://www.google.com/");
+ TestPrefObserver obs(&prefs, pref_name, new_pref_value);
+ prefs.AddPrefObserver(pref_name, &obs);
+ // This should fire the checks in TestPrefObserver::Observe.
+ prefs.SetString(pref_name, new_pref_value);
+
+ // Make sure the tests were actually run.
+ EXPECT_TRUE(obs.observer_fired());
+
+ // Now try adding a second pref observer.
+ const std::string new_pref_value2("http://www.youtube.com/");
+ obs.Reset(new_pref_value2);
+ TestPrefObserver obs2(&prefs, pref_name, new_pref_value2);
+ prefs.AddPrefObserver(pref_name, &obs2);
+ // This should fire the checks in obs and obs2.
+ prefs.SetString(pref_name, new_pref_value2);
+ EXPECT_TRUE(obs.observer_fired());
+ EXPECT_TRUE(obs2.observer_fired());
+
+ // Make sure obs2 still works after removing obs.
+ prefs.RemovePrefObserver(pref_name, &obs);
+ obs.Reset("");
+ obs2.Reset(new_pref_value);
+ // This should only fire the observer in obs2.
+ prefs.SetString(pref_name, new_pref_value);
+ EXPECT_FALSE(obs.observer_fired());
+ EXPECT_TRUE(obs2.observer_fired());
+
+ // Ok, clean up.
+ prefs.RemovePrefObserver(pref_name, &obs2);
+}
+
+class PrefServiceSetValueTest : public testing::Test {
+ protected:
+ static const char name_[];
+ static const char value_[];
+
+ PrefServiceSetValueTest()
+ : name_string_(name_),
+ null_value_(Value::CreateNullValue()) {}
+
+ void SetExpectNoNotification() {
+ EXPECT_CALL(observer_, Observe(_, _, _)).Times(0);
+ }
+
+ void SetExpectPrefChanged() {
+ EXPECT_CALL(observer_,
+ Observe(NotificationType(NotificationType::PREF_CHANGED), _,
+ Property(&Details<std::string>::ptr,
+ Pointee(name_string_))));
+ }
+
+ TestingPrefService prefs_;
+ std::string name_string_;
+ scoped_ptr<Value> null_value_;
+ NotificationObserverMock observer_;
+};
+
+const char PrefServiceSetValueTest::name_[] = "name";
+const char PrefServiceSetValueTest::value_[] = "value";
+
+TEST_F(PrefServiceSetValueTest, SetStringValue) {
+ const char default_string[] = "default";
+ scoped_ptr<Value> default_value(Value::CreateStringValue(default_string));
+ prefs_.RegisterStringPref(name_, default_string);
+ prefs_.AddPrefObserver(name_, &observer_);
+ SetExpectNoNotification();
+ prefs_.Set(name_, *default_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ scoped_ptr<Value> new_value(Value::CreateStringValue(value_));
+ SetExpectPrefChanged();
+ prefs_.Set(name_, *new_value);
+ EXPECT_EQ(value_, prefs_.GetString(name_));
+
+ prefs_.RemovePrefObserver(name_, &observer_);
+}
+
+TEST_F(PrefServiceSetValueTest, SetDictionaryValue) {
+ prefs_.RegisterDictionaryPref(name_);
+ prefs_.AddPrefObserver(name_, &observer_);
+
+ SetExpectNoNotification();
+ prefs_.Set(name_, *null_value_);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ DictionaryValue new_value;
+ new_value.SetString(name_, value_);
+ SetExpectPrefChanged();
+ prefs_.Set(name_, new_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+ DictionaryValue* dict = prefs_.GetMutableDictionary(name_);
+ EXPECT_EQ(1U, dict->size());
+ std::string out_value;
+ dict->GetString(name_, &out_value);
+ EXPECT_EQ(value_, out_value);
+
+ SetExpectNoNotification();
+ prefs_.Set(name_, new_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ SetExpectPrefChanged();
+ prefs_.Set(name_, *null_value_);
+ Mock::VerifyAndClearExpectations(&observer_);
+ dict = prefs_.GetMutableDictionary(name_);
+ EXPECT_EQ(0U, dict->size());
+
+ prefs_.RemovePrefObserver(name_, &observer_);
+}
+
+TEST_F(PrefServiceSetValueTest, SetListValue) {
+ prefs_.RegisterListPref(name_);
+ prefs_.AddPrefObserver(name_, &observer_);
+
+ SetExpectNoNotification();
+ prefs_.Set(name_, *null_value_);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ ListValue new_value;
+ new_value.Append(Value::CreateStringValue(value_));
+ SetExpectPrefChanged();
+ prefs_.Set(name_, new_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+ ListValue* list = prefs_.GetMutableList(name_);
+ ASSERT_EQ(1U, list->GetSize());
+ std::string out_value;
+ list->GetString(0, &out_value);
+ EXPECT_EQ(value_, out_value);
+
+ SetExpectNoNotification();
+ prefs_.Set(name_, new_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ SetExpectPrefChanged();
+ prefs_.Set(name_, *null_value_);
+ Mock::VerifyAndClearExpectations(&observer_);
+ list = prefs_.GetMutableList(name_);
+ EXPECT_EQ(0U, list->GetSize());
+
+ prefs_.RemovePrefObserver(name_, &observer_);
+}
diff --git a/chrome/browser/prefs/pref_set_observer.cc b/chrome/browser/prefs/pref_set_observer.cc
new file mode 100644
index 0000000..0f41500
--- /dev/null
+++ b/chrome/browser/prefs/pref_set_observer.cc
@@ -0,0 +1,66 @@
+// 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 "chrome/browser/prefs/pref_set_observer.h"
+
+#include "chrome/common/notification_type.h"
+#include "chrome/common/pref_names.h"
+
+PrefSetObserver::PrefSetObserver(PrefService* pref_service,
+ NotificationObserver* observer)
+ : pref_service_(pref_service),
+ observer_(observer) {
+}
+
+PrefSetObserver::~PrefSetObserver() {
+ for (PrefSet::const_iterator i(prefs_.begin()); i != prefs_.end(); ++i)
+ pref_service_->RemovePrefObserver(i->c_str(), this);
+}
+
+void PrefSetObserver::AddPref(const std::string& pref) {
+ if (!prefs_.count(pref) && pref_service_->FindPreference(pref.c_str())) {
+ prefs_.insert(pref);
+ pref_service_->AddPrefObserver(pref.c_str(), this);
+ }
+}
+
+void PrefSetObserver::RemovePref(const std::string& pref) {
+ if (prefs_.erase(pref))
+ pref_service_->RemovePrefObserver(pref.c_str(), this);
+}
+
+bool PrefSetObserver::IsObserved(const std::string& pref) {
+ return prefs_.count(pref) > 0;
+}
+
+bool PrefSetObserver::IsManaged() {
+ for (PrefSet::const_iterator i(prefs_.begin()); i != prefs_.end(); ++i) {
+ const PrefService::Preference* pref =
+ pref_service_->FindPreference(i->c_str());
+ if (pref && pref->IsManaged())
+ return true;
+ }
+ return false;
+}
+
+// static
+PrefSetObserver* PrefSetObserver::CreateProxyPrefSetObserver(
+ PrefService* pref_service,
+ NotificationObserver* observer) {
+ PrefSetObserver* pref_set = new PrefSetObserver(pref_service, observer);
+ pref_set->AddPref(prefs::kNoProxyServer);
+ pref_set->AddPref(prefs::kProxyAutoDetect);
+ pref_set->AddPref(prefs::kProxyServer);
+ pref_set->AddPref(prefs::kProxyPacUrl);
+ pref_set->AddPref(prefs::kProxyBypassList);
+
+ return pref_set;
+}
+
+void PrefSetObserver::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (observer_)
+ observer_->Observe(type, source, details);
+}
diff --git a/chrome/browser/prefs/pref_set_observer.h b/chrome/browser/prefs/pref_set_observer.h
new file mode 100644
index 0000000..038a3f5
--- /dev/null
+++ b/chrome/browser/prefs/pref_set_observer.h
@@ -0,0 +1,54 @@
+// 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 CHROME_BROWSER_PREFS_PREF_SET_OBSERVER_H_
+#define CHROME_BROWSER_PREFS_PREF_SET_OBSERVER_H_
+#pragma once
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/common/notification_observer.h"
+
+// Observes the state of a set of preferences and allows to query their combined
+// managed bits.
+class PrefSetObserver : public NotificationObserver {
+ public:
+ // Initialize with an empty set of preferences.
+ PrefSetObserver(PrefService* pref_service,
+ NotificationObserver* observer);
+ virtual ~PrefSetObserver();
+
+ // Add a |pref| to the set of preferences to observe.
+ void AddPref(const std::string& pref);
+ // Remove |pref| from the set of observed peferences.
+ void RemovePref(const std::string& pref);
+
+ // Check whether |pref| is in the set of observed preferences.
+ bool IsObserved(const std::string& pref);
+ // Check whether any of the observed preferences has the managed bit set.
+ bool IsManaged();
+
+ // Create a pref set observer for all preferences relavant to proxies.
+ static PrefSetObserver* CreateProxyPrefSetObserver(
+ PrefService* pref_service,
+ NotificationObserver* observer);
+
+ private:
+ // Overridden from NotificationObserver.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ typedef std::set<std::string> PrefSet;
+ PrefSet prefs_;
+
+ PrefService* pref_service_;
+ NotificationObserver* observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefSetObserver);
+};
+
+#endif // CHROME_BROWSER_PREFS_PREF_SET_OBSERVER_H_
diff --git a/chrome/browser/prefs/pref_set_observer_unittest.cc b/chrome/browser/prefs/pref_set_observer_unittest.cc
new file mode 100644
index 0000000..f77bb6c
--- /dev/null
+++ b/chrome/browser/prefs/pref_set_observer_unittest.cc
@@ -0,0 +1,89 @@
+// 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 "chrome/browser/prefs/pref_set_observer.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_observer_mock.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Unit tests for PrefSetObserver.
+class PrefSetObserverTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ pref_service_.reset(new TestingPrefService);
+ pref_service_->RegisterStringPref(prefs::kHomePage, "http://google.com");
+ pref_service_->RegisterBooleanPref(prefs::kHomePageIsNewTabPage, false);
+ pref_service_->RegisterStringPref(prefs::kApplicationLocale, "");
+ }
+
+ PrefSetObserver* CreatePrefSetObserver(NotificationObserver* observer) {
+ PrefSetObserver* pref_set =
+ new PrefSetObserver(pref_service_.get(), observer);
+ pref_set->AddPref(prefs::kHomePage);
+ pref_set->AddPref(prefs::kHomePageIsNewTabPage);
+ return pref_set;
+ }
+
+ scoped_ptr<TestingPrefService> pref_service_;
+};
+
+TEST_F(PrefSetObserverTest, IsObserved) {
+ scoped_ptr<PrefSetObserver> pref_set(CreatePrefSetObserver(NULL));
+ EXPECT_TRUE(pref_set->IsObserved(prefs::kHomePage));
+ EXPECT_TRUE(pref_set->IsObserved(prefs::kHomePageIsNewTabPage));
+ EXPECT_FALSE(pref_set->IsObserved(prefs::kApplicationLocale));
+}
+
+TEST_F(PrefSetObserverTest, IsManaged) {
+ scoped_ptr<PrefSetObserver> pref_set(CreatePrefSetObserver(NULL));
+ EXPECT_FALSE(pref_set->IsManaged());
+ pref_service_->SetManagedPref(prefs::kHomePage,
+ Value::CreateStringValue("http://crbug.com"));
+ EXPECT_TRUE(pref_set->IsManaged());
+ pref_service_->SetManagedPref(prefs::kHomePageIsNewTabPage,
+ Value::CreateBooleanValue(true));
+ EXPECT_TRUE(pref_set->IsManaged());
+ pref_service_->RemoveManagedPref(prefs::kHomePage);
+ EXPECT_TRUE(pref_set->IsManaged());
+ pref_service_->RemoveManagedPref(prefs::kHomePageIsNewTabPage);
+ EXPECT_FALSE(pref_set->IsManaged());
+}
+
+MATCHER_P(PrefNameDetails, name, "details references named preference") {
+ std::string* pstr = reinterpret_cast<const Details<std::string>&>(arg).ptr();
+ return pstr && *pstr == name;
+}
+
+TEST_F(PrefSetObserverTest, Observe) {
+ using testing::_;
+ using testing::Mock;
+
+ NotificationObserverMock observer;
+ scoped_ptr<PrefSetObserver> pref_set(CreatePrefSetObserver(&observer));
+
+ EXPECT_CALL(observer,
+ Observe(NotificationType(NotificationType::PREF_CHANGED),
+ Source<PrefService>(pref_service_.get()),
+ PrefNameDetails(prefs::kHomePage)));
+ pref_service_->SetUserPref(prefs::kHomePage,
+ Value::CreateStringValue("http://crbug.com"));
+ Mock::VerifyAndClearExpectations(&observer);
+
+ EXPECT_CALL(observer,
+ Observe(NotificationType(NotificationType::PREF_CHANGED),
+ Source<PrefService>(pref_service_.get()),
+ PrefNameDetails(prefs::kHomePageIsNewTabPage)));
+ pref_service_->SetUserPref(prefs::kHomePageIsNewTabPage,
+ Value::CreateBooleanValue(true));
+ Mock::VerifyAndClearExpectations(&observer);
+
+ EXPECT_CALL(observer, Observe(_, _, _)).Times(0);
+ pref_service_->SetUserPref(prefs::kApplicationLocale,
+ Value::CreateStringValue("en_US.utf8"));
+ Mock::VerifyAndClearExpectations(&observer);
+}
diff --git a/chrome/browser/prefs/pref_value_store.cc b/chrome/browser/prefs/pref_value_store.cc
new file mode 100644
index 0000000..6ab4b8f
--- /dev/null
+++ b/chrome/browser/prefs/pref_value_store.cc
@@ -0,0 +1,299 @@
+// 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 "chrome/browser/prefs/pref_value_store.h"
+
+#include "base/values.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/extensions/extension_pref_store.h"
+#include "chrome/browser/policy/configuration_policy_pref_store.h"
+#include "chrome/browser/prefs/command_line_pref_store.h"
+#include "chrome/common/json_pref_store.h"
+#include "chrome/common/notification_service.h"
+
+// static
+PrefValueStore* PrefValueStore::CreatePrefValueStore(
+ const FilePath& pref_filename,
+ Profile* profile,
+ bool user_only) {
+ ConfigurationPolicyPrefStore* managed = NULL;
+ ExtensionPrefStore* extension = NULL;
+ CommandLinePrefStore* command_line = NULL;
+ JsonPrefStore* user = NULL;
+ ConfigurationPolicyPrefStore* recommended = NULL;
+
+ if (!pref_filename.empty()) {
+ user = new JsonPrefStore(
+ pref_filename,
+ ChromeThread::GetMessageLoopProxyForThread(ChromeThread::FILE));
+ }
+
+ if (!user_only) {
+ managed = ConfigurationPolicyPrefStore::CreateManagedPolicyPrefStore();
+ extension = new ExtensionPrefStore(profile, PrefNotifier::EXTENSION_STORE);
+ command_line = new CommandLinePrefStore(CommandLine::ForCurrentProcess());
+ recommended =
+ ConfigurationPolicyPrefStore::CreateRecommendedPolicyPrefStore();
+ }
+ return new PrefValueStore(managed, extension, command_line, user,
+ recommended);
+}
+
+PrefValueStore::~PrefValueStore() {}
+
+bool PrefValueStore::GetValue(const std::string& name,
+ Value** out_value) const {
+ // Check the |PrefStore|s in order of their priority from highest to lowest
+ // to find the value of the preference described by the given preference name.
+ for (size_t i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) {
+ if (pref_stores_[i].get() &&
+ pref_stores_[i]->prefs()->Get(name.c_str(), out_value)) {
+ return true;
+ }
+ }
+ // No value found for the given preference name, set the return false.
+ *out_value = NULL;
+ return false;
+}
+
+bool PrefValueStore::WritePrefs() {
+ bool success = true;
+ for (size_t i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) {
+ if (pref_stores_[i].get())
+ success = pref_stores_[i]->WritePrefs() && success;
+ }
+ return success;
+}
+
+void PrefValueStore::ScheduleWritePrefs() {
+ for (size_t i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) {
+ if (pref_stores_[i].get())
+ pref_stores_[i]->ScheduleWritePrefs();
+ }
+}
+
+PrefStore::PrefReadError PrefValueStore::ReadPrefs() {
+ PrefStore::PrefReadError result = PrefStore::PREF_READ_ERROR_NONE;
+ for (size_t i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) {
+ if (pref_stores_[i].get()) {
+ PrefStore::PrefReadError this_error = pref_stores_[i]->ReadPrefs();
+ if (result == PrefStore::PREF_READ_ERROR_NONE)
+ result = this_error;
+ }
+ }
+ // TODO(markusheintz): Return a better error status: maybe a struct with
+ // the error status of all PrefStores.
+ return result;
+}
+
+bool PrefValueStore::HasPrefPath(const char* path) const {
+ Value* tmp_value = NULL;
+ const std::string name(path);
+ bool rv = GetValue(name, &tmp_value);
+ return rv;
+}
+
+bool PrefValueStore::PrefHasChanged(const char* path,
+ PrefNotifier::PrefStoreType new_store,
+ const Value* old_value) {
+ DCHECK(new_store != PrefNotifier::INVALID_STORE);
+ // Replying that the pref has changed may cause extra work, but it should
+ // not be actively harmful.
+ if (new_store == PrefNotifier::INVALID_STORE)
+ return true;
+
+ Value* new_value = NULL;
+ GetValue(path, &new_value);
+ // Some unit tests have no values for certain prefs.
+ if (!new_value || !old_value->Equals(new_value))
+ return true;
+
+ // If there's a value in a store with lower priority than the |new_store|,
+ // and no value in a store with higher priority, assume the |new_store| just
+ // took control of the pref. (This assumption is wrong if the new value
+ // and store are both the same as the old one, but that situation should be
+ // rare, and reporting a change when none happened should not be harmful.)
+ if (PrefValueInStoreRange(path, new_store, false) &&
+ !PrefValueInStoreRange(path, new_store, true))
+ return true;
+
+ return false;
+}
+
+// Note the |DictionaryValue| referenced by the |PrefStore| user_prefs_
+// (returned by the method prefs()) takes the ownership of the Value referenced
+// by in_value.
+void PrefValueStore::SetUserPrefValue(const char* name, Value* in_value) {
+ pref_stores_[PrefNotifier::USER_STORE]->prefs()->Set(name, in_value);
+}
+
+bool PrefValueStore::ReadOnly() {
+ return pref_stores_[PrefNotifier::USER_STORE]->ReadOnly();
+}
+
+void PrefValueStore::RemoveUserPrefValue(const char* name) {
+ if (pref_stores_[PrefNotifier::USER_STORE].get()) {
+ pref_stores_[PrefNotifier::USER_STORE]->prefs()->Remove(name, NULL);
+ }
+}
+
+bool PrefValueStore::PrefValueInManagedStore(const char* name) {
+ return PrefValueInStore(name, PrefNotifier::MANAGED_STORE);
+}
+
+bool PrefValueStore::PrefValueInExtensionStore(const char* name) {
+ return PrefValueInStore(name, PrefNotifier::EXTENSION_STORE);
+}
+
+bool PrefValueStore::PrefValueInUserStore(const char* name) {
+ return PrefValueInStore(name, PrefNotifier::USER_STORE);
+}
+
+bool PrefValueStore::PrefValueFromExtensionStore(const char* name) {
+ return ControllingPrefStoreForPref(name) == PrefNotifier::EXTENSION_STORE;
+}
+
+bool PrefValueStore::PrefValueFromUserStore(const char* name) {
+ return ControllingPrefStoreForPref(name) == PrefNotifier::USER_STORE;
+}
+
+bool PrefValueStore::PrefValueUserModifiable(const char* name) {
+ PrefNotifier::PrefStoreType effective_store =
+ ControllingPrefStoreForPref(name);
+ return effective_store >= PrefNotifier::USER_STORE ||
+ effective_store == PrefNotifier::INVALID_STORE;
+}
+
+bool PrefValueStore::PrefValueInStore(const char* name,
+ PrefNotifier::PrefStoreType type) {
+ if (!pref_stores_[type].get()) {
+ // No store of that type set, so this pref can't be in it.
+ return false;
+ }
+ Value* tmp_value;
+ return pref_stores_[type]->prefs()->Get(name, &tmp_value);
+}
+
+bool PrefValueStore::PrefValueInStoreRange(const char* name,
+ PrefNotifier::PrefStoreType boundary,
+ bool higher_priority) {
+ // Higher priorities are lower PrefStoreType values. The range is
+ // non-inclusive of the boundary.
+ int start = higher_priority ? 0 : boundary + 1;
+ int end = higher_priority ? boundary - 1 :
+ PrefNotifier::PREF_STORE_TYPE_MAX;
+
+ for (int i = start; i <= end ; ++i) {
+ if (PrefValueInStore(name, static_cast<PrefNotifier::PrefStoreType>(i)))
+ return true;
+ }
+ return false;
+}
+
+
+PrefNotifier::PrefStoreType PrefValueStore::ControllingPrefStoreForPref(
+ const char* name) {
+ for (int i = 0; i <= PrefNotifier::PREF_STORE_TYPE_MAX; ++i) {
+ if (PrefValueInStore(name, static_cast<PrefNotifier::PrefStoreType>(i)))
+ return static_cast<PrefNotifier::PrefStoreType>(i);
+ }
+ return PrefNotifier::INVALID_STORE;
+}
+
+void PrefValueStore::RefreshPolicyPrefsCompletion(
+ PrefStore* new_managed_pref_store,
+ PrefStore* new_recommended_pref_store,
+ AfterRefreshCallback* callback_pointer) {
+ scoped_ptr<AfterRefreshCallback> callback(callback_pointer);
+ DictionaryValue* managed_prefs_before(
+ pref_stores_[PrefNotifier::MANAGED_STORE]->prefs());
+ DictionaryValue* managed_prefs_after(new_managed_pref_store->prefs());
+ DictionaryValue* recommended_prefs_before(
+ pref_stores_[PrefNotifier::RECOMMENDED_STORE]->prefs());
+ DictionaryValue* recommended_prefs_after(new_recommended_pref_store->prefs());
+
+ std::vector<std::string> changed_managed_paths;
+ managed_prefs_before->GetDifferingPaths(managed_prefs_after,
+ &changed_managed_paths);
+
+ std::vector<std::string> changed_recommended_paths;
+ recommended_prefs_before->GetDifferingPaths(recommended_prefs_after,
+ &changed_recommended_paths);
+
+ std::vector<std::string> changed_paths(changed_managed_paths.size() +
+ changed_recommended_paths.size());
+ std::vector<std::string>::iterator last_insert =
+ std::merge(changed_managed_paths.begin(),
+ changed_managed_paths.end(),
+ changed_recommended_paths.begin(),
+ changed_recommended_paths.end(),
+ changed_paths.begin());
+ changed_paths.resize(last_insert - changed_paths.begin());
+
+ pref_stores_[PrefNotifier::MANAGED_STORE].reset(new_managed_pref_store);
+ pref_stores_[PrefNotifier::RECOMMENDED_STORE].reset(
+ new_recommended_pref_store);
+ callback->Run(changed_paths);
+}
+
+void PrefValueStore::RefreshPolicyPrefsOnFileThread(
+ ChromeThread::ID calling_thread_id,
+ PrefStore* new_managed_pref_store,
+ PrefStore* new_recommended_pref_store,
+ AfterRefreshCallback* callback_pointer) {
+ scoped_ptr<AfterRefreshCallback> callback(callback_pointer);
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
+ scoped_ptr<PrefStore> managed_pref_store(new_managed_pref_store);
+ scoped_ptr<PrefStore> recommended_pref_store(new_recommended_pref_store);
+
+ PrefStore::PrefReadError read_error = new_managed_pref_store->ReadPrefs();
+ if (read_error != PrefStore::PREF_READ_ERROR_NONE) {
+ LOG(ERROR) << "refresh of managed policy failed: PrefReadError = "
+ << read_error;
+ return;
+ }
+
+ read_error = new_recommended_pref_store->ReadPrefs();
+ if (read_error != PrefStore::PREF_READ_ERROR_NONE) {
+ LOG(ERROR) << "refresh of recommended policy failed: PrefReadError = "
+ << read_error;
+ return;
+ }
+
+ ChromeThread::PostTask(
+ calling_thread_id, FROM_HERE,
+ NewRunnableMethod(this,
+ &PrefValueStore::RefreshPolicyPrefsCompletion,
+ managed_pref_store.release(),
+ recommended_pref_store.release(),
+ callback.release()));
+}
+
+void PrefValueStore::RefreshPolicyPrefs(
+ PrefStore* new_managed_pref_store,
+ PrefStore* new_recommended_pref_store,
+ AfterRefreshCallback* callback) {
+ ChromeThread::ID current_thread_id;
+ CHECK(ChromeThread::GetCurrentThreadIdentifier(&current_thread_id));
+ ChromeThread::PostTask(
+ ChromeThread::FILE, FROM_HERE,
+ NewRunnableMethod(this,
+ &PrefValueStore::RefreshPolicyPrefsOnFileThread,
+ current_thread_id,
+ new_managed_pref_store,
+ new_recommended_pref_store,
+ callback));
+}
+
+PrefValueStore::PrefValueStore(PrefStore* managed_prefs,
+ PrefStore* extension_prefs,
+ PrefStore* command_line_prefs,
+ PrefStore* user_prefs,
+ PrefStore* recommended_prefs) {
+ pref_stores_[PrefNotifier::MANAGED_STORE].reset(managed_prefs);
+ pref_stores_[PrefNotifier::EXTENSION_STORE].reset(extension_prefs);
+ pref_stores_[PrefNotifier::COMMAND_LINE_STORE].reset(command_line_prefs);
+ pref_stores_[PrefNotifier::USER_STORE].reset(user_prefs);
+ pref_stores_[PrefNotifier::RECOMMENDED_STORE].reset(recommended_prefs);
+}
diff --git a/chrome/browser/prefs/pref_value_store.h b/chrome/browser/prefs/pref_value_store.h
new file mode 100644
index 0000000..fb1cb21
--- /dev/null
+++ b/chrome/browser/prefs/pref_value_store.h
@@ -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.
+
+#ifndef CHROME_BROWSER_PREFS_PREF_VALUE_STORE_H_
+#define CHROME_BROWSER_PREFS_PREF_VALUE_STORE_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/gtest_prod_util.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/prefs/pref_notifier.h"
+#include "chrome/common/pref_store.h"
+
+class FilePath;
+class PrefStore;
+class Profile;
+class Value;
+
+// 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.
+// See PrefNotifier for a list of the available preference sources (PrefStores)
+// and their descriptions.
+//
+// Unless otherwise explicitly noted, all of the methods of this class must
+// be called on the UI thread.
+class PrefValueStore : public base::RefCountedThreadSafe<PrefValueStore> {
+ public:
+ // Returns a new PrefValueStore with all applicable PrefStores. The
+ // |pref_filename| points to the user preference file. The |profile| is the
+ // one to which these preferences apply; it may be NULL if we're dealing
+ // with the local state. If |pref_filename| is empty, the user PrefStore will
+ // not be created. If |user_only| is true, no PrefStores will be created
+ // other than the user PrefStore (if |pref_filename| is not empty). This
+ // should not normally be called directly: the usual way to create a
+ // PrefValueStore is by creating a PrefService.
+ static PrefValueStore* CreatePrefValueStore(const FilePath& pref_filename,
+ Profile* profile,
+ bool user_only);
+
+ ~PrefValueStore();
+
+ // Get the preference value for the given preference name.
+ // Return true if a value for the given preference name was found.
+ bool GetValue(const std::string& name, Value** out_value) const;
+
+ // Read preference values into the three PrefStores so that they are available
+ // through the GetValue method. Return the first error that occurs (but
+ // continue reading the remaining PrefStores).
+ PrefStore::PrefReadError ReadPrefs();
+
+ // Persists prefs (to disk or elsewhere). Returns true if writing values was
+ // successful. In practice, only the user prefs are expected to be written
+ // out.
+ bool WritePrefs();
+
+ // Calls the method ScheduleWritePrefs on the PrefStores. In practice, only
+ // the user prefs are expected to be written out.
+ void ScheduleWritePrefs();
+
+ // Returns true if the PrefValueStore contains the given preference.
+ bool HasPrefPath(const char* name) const;
+
+ // Returns true if the effective value of the preference has changed from its
+ // |old_value| (which should be the effective value of the preference as
+ // reported by GetValue() or the PrefService before the PrefStore changed it),
+ // or if the store controlling the pref has changed. Virtual so it can be
+ // mocked for a unit test.
+ // TODO(pamg): If we're setting the same value as we already had, into the
+ // same store that was controlling it before, and there's also a value set in
+ // a lower-priority store, *and* we're not the highest-priority store, then
+ // this will return true when it shouldn't. Fix that if it causes problems.
+ virtual bool PrefHasChanged(const char* path,
+ PrefNotifier::PrefStoreType new_store,
+ const Value* old_value);
+
+ // Returns true if the PrefValueStore is read-only.
+ // Because the managed and recommended PrefStores are always read-only, the
+ // PrefValueStore as a whole is read-only if the PrefStore containing the user
+ // preferences is read-only.
+ bool ReadOnly();
+
+ // Alters the user-defined value of a preference. Even if the preference is
+ // managed this method allows the user-defined value of the preference to be
+ // set. But GetValue calls will not return this value as long as the
+ // preference is managed. Instead GetValue will return the managed value
+ // of the preference. Note that the PrefValueStore takes the ownership of
+ // the value referenced by |in_value|. It is an error to call this when no
+ // user PrefStore has been set.
+ void SetUserPrefValue(const char* name, Value* in_value);
+
+ // Removes a value from the PrefValueStore. If a preference is managed
+ // or recommended this function should have no effect.
+ void RemoveUserPrefValue(const char* name);
+
+ // 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 char* name);
+ bool PrefValueInExtensionStore(const char* name);
+ bool PrefValueInUserStore(const char* name);
+
+ // 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 char* name);
+ bool PrefValueFromUserStore(const char* name);
+
+ // Check whether a Preference value is modifiable by the user, i.e. whether
+ // there is no higher-priority source controlling it.
+ bool PrefValueUserModifiable(const char* name);
+
+ // Signature of callback triggered after policy refresh. Parameter is not
+ // passed as reference to prevent passing along a pointer to a set whose
+ // lifecycle is managed in another thread.
+ typedef Callback1<std::vector<std::string> >::Type AfterRefreshCallback;
+
+ // Called as a result of a notification of policy change. Triggers a
+ // reload of managed preferences from policy. Caller must pass in
+ // new, uninitialized managed and recommended PrefStores in
+ // |managed_pref_store| and |recommended_pref_store| respectively, since
+ // PrefValueStore doesn't know about policy-specific PrefStores.
+ // |callback| is called with the set of preferences changed by the policy
+ // refresh. |callback| is called on the caller's thread as a Task
+ // after RefreshPolicyPrefs has returned. RefreshPolicyPrefs takes ownership
+ // of the |callback| object.
+ void RefreshPolicyPrefs(PrefStore* managed_pref_store,
+ PrefStore* recommended_pref_store,
+ AfterRefreshCallback* callback);
+
+ protected:
+ // In decreasing order of precedence:
+ // |managed_prefs| contains all managed (policy) preference values.
+ // |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 recommended (policy) preference values.
+ //
+ // This constructor should only be used internally, or by subclasses in
+ // testing. The usual way to create a PrefValueStore is by creating a
+ // PrefService.
+ PrefValueStore(PrefStore* managed_prefs,
+ PrefStore* extension_prefs,
+ PrefStore* command_line_prefs,
+ PrefStore* user_prefs,
+ PrefStore* recommended_prefs);
+
+ private:
+ friend class PrefValueStoreTest;
+ FRIEND_TEST_ALL_PREFIXES(PrefValueStoreTest,
+ TestRefreshPolicyPrefsCompletion);
+
+ scoped_ptr<PrefStore> pref_stores_[PrefNotifier::PREF_STORE_TYPE_MAX + 1];
+
+ bool PrefValueInStore(const char* name, PrefNotifier::PrefStoreType type);
+
+ // Returns true if the preference |name| is found in any PrefStore starting
+ // just beyond the |boundary|, non-inclusive, and checking either
+ // higher-priority stores (if |higher_priority| is true) or lower-priority
+ // stores.
+ bool PrefValueInStoreRange(const char* name,
+ PrefNotifier::PrefStoreType boundary,
+ bool higher_priority);
+
+ // Returns the pref store type identifying the source that controls the
+ // Preference identified by |name|. If none of the sources has a value,
+ // INVALID is returned.
+ PrefNotifier::PrefStoreType ControllingPrefStoreForPref(const char* name);
+
+ // Called during policy refresh after ReadPrefs completes on the thread
+ // that initiated the policy refresh. RefreshPolicyPrefsCompletion takes
+ // ownership of the |callback| object.
+ void RefreshPolicyPrefsCompletion(
+ PrefStore* new_managed_pref_store,
+ PrefStore* new_recommended_pref_store,
+ AfterRefreshCallback* callback);
+
+ // Called during policy refresh to do the ReadPrefs on the FILE thread.
+ // RefreshPolicyPrefsOnFileThread takes ownership of the |callback| object.
+ void RefreshPolicyPrefsOnFileThread(
+ ChromeThread::ID calling_thread_id,
+ PrefStore* new_managed_pref_store,
+ PrefStore* new_recommended_pref_store,
+ AfterRefreshCallback* callback);
+
+ DISALLOW_COPY_AND_ASSIGN(PrefValueStore);
+};
+
+#endif // CHROME_BROWSER_PREFS_PREF_VALUE_STORE_H_
diff --git a/chrome/browser/prefs/pref_value_store_unittest.cc b/chrome/browser/prefs/pref_value_store_unittest.cc
new file mode 100644
index 0000000..96af616
--- /dev/null
+++ b/chrome/browser/prefs/pref_value_store_unittest.cc
@@ -0,0 +1,663 @@
+// 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/scoped_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/policy/configuration_policy_pref_store.h"
+#include "chrome/browser/prefs/dummy_pref_store.h"
+#include "chrome/browser/prefs/pref_value_store.h"
+#include "chrome/test/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Mock;
+
+namespace {
+
+class MockPolicyRefreshCallback {
+ public:
+ MockPolicyRefreshCallback() {}
+ MOCK_METHOD1(DoCallback, void(const std::vector<std::string>));
+};
+
+} // namespace
+
+// Names of the preferences used in this test program.
+namespace prefs {
+ const char kCurrentThemeID[] = "extensions.theme.id";
+ const char kDeleteCache[] = "browser.clear_data.cache";
+ const char kHomepage[] = "homepage";
+ const char kMaxTabs[] = "tabs.max_tabs";
+ const char kMissingPref[] = "this.pref.does_not_exist";
+ const char kRecommendedPref[] = "this.pref.recommended_value_only";
+ const char kSampleDict[] = "sample.dict";
+ const char kSampleList[] = "sample.list";
+
+ // This must match the actual pref name so the command-line store knows about
+ // it.
+ const char kApplicationLocale[] = "intl.app_locale";
+}
+
+// Potentially expected values of all preferences used in this test program.
+// The "user" namespace is defined globally in an ARM system header, so we need
+// something different here.
+namespace user_pref {
+ const int kMaxTabsValue = 31;
+ const bool kDeleteCacheValue = true;
+ const char kCurrentThemeIDValue[] = "abcdefg";
+ const char kHomepageValue[] = "http://www.google.com";
+ const char kApplicationLocaleValue[] = "is-WRONG";
+}
+
+namespace enforced_pref {
+ const std::string kHomepageValue = "http://www.topeka.com";
+}
+
+namespace extension_pref {
+ const char kCurrentThemeIDValue[] = "set by extension";
+ const char kHomepageValue[] = "http://www.chromium.org";
+}
+
+namespace command_line_pref {
+ const char kApplicationLocaleValue[] = "hi-MOM";
+ const char kCurrentThemeIDValue[] = "zyxwvut";
+ const char kHomepageValue[] = "http://www.ferretcentral.org";
+}
+
+namespace recommended_pref {
+ const int kMaxTabsValue = 10;
+ const bool kRecommendedPrefValue = true;
+}
+
+class PrefValueStoreTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Create dummy user preferences.
+ enforced_prefs_= CreateEnforcedPrefs();
+ extension_prefs_ = CreateExtensionPrefs();
+ command_line_prefs_ = CreateCommandLinePrefs();
+ user_prefs_ = CreateUserPrefs();
+ recommended_prefs_ = CreateRecommendedPrefs();
+
+ // Create |DummyPrefStore|s.
+ enforced_pref_store_ = new DummyPrefStore();
+ enforced_pref_store_->set_prefs(enforced_prefs_);
+ extension_pref_store_ = new DummyPrefStore();
+ extension_pref_store_->set_prefs(extension_prefs_);
+ command_line_pref_store_ = new DummyPrefStore();
+ command_line_pref_store_->set_prefs(command_line_prefs_);
+ user_pref_store_ = new DummyPrefStore();
+ user_pref_store_->set_read_only(false);
+ user_pref_store_->set_prefs(user_prefs_);
+ recommended_pref_store_ = new DummyPrefStore();
+ recommended_pref_store_->set_prefs(recommended_prefs_);
+
+ // Create a new pref-value-store.
+ pref_value_store_ = new TestingPrefService::TestingPrefValueStore(
+ enforced_pref_store_,
+ extension_pref_store_,
+ command_line_pref_store_,
+ user_pref_store_,
+ recommended_pref_store_);
+
+ ui_thread_.reset(new ChromeThread(ChromeThread::UI, &loop_));
+ file_thread_.reset(new ChromeThread(ChromeThread::FILE, &loop_));
+ }
+
+ // Creates a new dictionary and stores some sample user preferences
+ // in it.
+ DictionaryValue* CreateUserPrefs() {
+ DictionaryValue* user_prefs = new DictionaryValue();
+ user_prefs->SetBoolean(prefs::kDeleteCache, user_pref::kDeleteCacheValue);
+ user_prefs->SetInteger(prefs::kMaxTabs, user_pref::kMaxTabsValue);
+ user_prefs->SetString(prefs::kCurrentThemeID,
+ user_pref::kCurrentThemeIDValue);
+ user_prefs->SetString(prefs::kApplicationLocale,
+ user_pref::kApplicationLocaleValue);
+ user_prefs->SetString(prefs::kHomepage, user_pref::kHomepageValue);
+ return user_prefs;
+ }
+
+ DictionaryValue* CreateEnforcedPrefs() {
+ DictionaryValue* enforced_prefs = new DictionaryValue();
+ enforced_prefs->SetString(prefs::kHomepage, enforced_pref::kHomepageValue);
+ expected_differing_paths_.push_back(prefs::kHomepage);
+ return enforced_prefs;
+ }
+
+ DictionaryValue* CreateExtensionPrefs() {
+ DictionaryValue* extension_prefs = new DictionaryValue();
+ extension_prefs->SetString(prefs::kCurrentThemeID,
+ extension_pref::kCurrentThemeIDValue);
+ extension_prefs->SetString(prefs::kHomepage,
+ extension_pref::kHomepageValue);
+ return extension_prefs;
+ }
+
+ DictionaryValue* CreateCommandLinePrefs() {
+ DictionaryValue* command_line_prefs = new DictionaryValue();
+ command_line_prefs->SetString(prefs::kCurrentThemeID,
+ command_line_pref::kCurrentThemeIDValue);
+ command_line_prefs->SetString(prefs::kApplicationLocale,
+ command_line_pref::kApplicationLocaleValue);
+ command_line_prefs->SetString(prefs::kHomepage,
+ command_line_pref::kHomepageValue);
+ return command_line_prefs;
+ }
+
+ DictionaryValue* CreateRecommendedPrefs() {
+ DictionaryValue* recommended_prefs = new DictionaryValue();
+ recommended_prefs->SetInteger(prefs::kMaxTabs,
+ recommended_pref::kMaxTabsValue);
+ recommended_prefs->SetBoolean(
+ prefs::kRecommendedPref,
+ recommended_pref::kRecommendedPrefValue);
+
+ // Expected differing paths must be added in lexicographic order
+ // to work properly
+ expected_differing_paths_.push_back("tabs");
+ expected_differing_paths_.push_back(prefs::kMaxTabs);
+ expected_differing_paths_.push_back("this");
+ expected_differing_paths_.push_back("this.pref");
+ expected_differing_paths_.push_back(prefs::kRecommendedPref);
+ return recommended_prefs; }
+
+ DictionaryValue* CreateSampleDictValue() {
+ DictionaryValue* sample_dict = new DictionaryValue();
+ sample_dict->SetBoolean("issample", true);
+ sample_dict->SetInteger("value", 4);
+ sample_dict->SetString("descr", "Sample Test Dictionary");
+ return sample_dict;
+ }
+
+ ListValue* CreateSampleListValue() {
+ ListValue* sample_list = new ListValue();
+ sample_list->Set(0, Value::CreateIntegerValue(0));
+ sample_list->Set(1, Value::CreateIntegerValue(1));
+ sample_list->Set(2, Value::CreateIntegerValue(2));
+ sample_list->Set(3, Value::CreateIntegerValue(3));
+ return sample_list;
+ }
+
+ virtual void TearDown() {
+ loop_.RunAllPending();
+ }
+
+ MessageLoop loop_;
+
+ scoped_refptr<TestingPrefService::TestingPrefValueStore> pref_value_store_;
+
+ // |PrefStore|s are owned by the |PrefValueStore|.
+ DummyPrefStore* enforced_pref_store_;
+ DummyPrefStore* extension_pref_store_;
+ DummyPrefStore* command_line_pref_store_;
+ DummyPrefStore* recommended_pref_store_;
+ DummyPrefStore* user_pref_store_;
+
+ // A vector of the preferences paths in the managed and recommended
+ // PrefStores that are set at the beginning of a test. Can be modified
+ // by the test to track changes that it makes to the preferences
+ // stored in the managed and recommended PrefStores.
+ std::vector<std::string> expected_differing_paths_;
+
+ // Preferences are owned by the individual |DummyPrefStores|.
+ DictionaryValue* enforced_prefs_;
+ DictionaryValue* extension_prefs_;
+ DictionaryValue* command_line_prefs_;
+ DictionaryValue* user_prefs_;
+ DictionaryValue* recommended_prefs_;
+
+ private:
+ scoped_ptr<ChromeThread> ui_thread_;
+ scoped_ptr<ChromeThread> file_thread_;
+};
+
+TEST_F(PrefValueStoreTest, IsReadOnly) {
+ enforced_pref_store_->set_read_only(true);
+ extension_pref_store_->set_read_only(true);
+ command_line_pref_store_->set_read_only(true);
+ user_pref_store_->set_read_only(true);
+ recommended_pref_store_->set_read_only(true);
+ EXPECT_TRUE(pref_value_store_->ReadOnly());
+
+ user_pref_store_->set_read_only(false);
+ EXPECT_FALSE(pref_value_store_->ReadOnly());
+}
+
+TEST_F(PrefValueStoreTest, GetValue) {
+ Value* value;
+
+ // Test getting an enforced value overwriting a user-defined and
+ // extension-defined value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kHomepage, &value));
+ std::string actual_str_value;
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(enforced_pref::kHomepageValue, actual_str_value);
+
+ // Test getting an extension value overwriting a user-defined and
+ // command-line-defined value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kCurrentThemeID, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(extension_pref::kCurrentThemeIDValue, actual_str_value);
+
+ // Test getting a command-line value overwriting a user-defined value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kApplicationLocale, &value));
+ EXPECT_TRUE(value->GetAsString(&actual_str_value));
+ EXPECT_EQ(command_line_pref::kApplicationLocaleValue, actual_str_value);
+
+ // Test getting a user-set value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kDeleteCache, &value));
+ bool actual_bool_value = false;
+ EXPECT_TRUE(value->GetAsBoolean(&actual_bool_value));
+ EXPECT_EQ(user_pref::kDeleteCacheValue, actual_bool_value);
+
+ // Test getting a user set value overwriting a recommended value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kMaxTabs, &value));
+ int actual_int_value = -1;
+ EXPECT_TRUE(value->GetAsInteger(&actual_int_value));
+ EXPECT_EQ(user_pref::kMaxTabsValue, actual_int_value);
+
+ // Test getting a recommended value.
+ value = NULL;
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kRecommendedPref, &value));
+ actual_bool_value = false;
+ EXPECT_TRUE(value->GetAsBoolean(&actual_bool_value));
+ EXPECT_EQ(recommended_pref::kRecommendedPrefValue, actual_bool_value);
+
+ // Test getting a preference value that the |PrefValueStore|
+ // does not contain.
+ FundamentalValue tmp_dummy_value(true);
+ Value* v_null = &tmp_dummy_value;
+ ASSERT_FALSE(pref_value_store_->GetValue(prefs::kMissingPref, &v_null));
+ ASSERT_TRUE(v_null == NULL);
+}
+
+TEST_F(PrefValueStoreTest, HasPrefPath) {
+ // Enforced preference
+ EXPECT_TRUE(pref_value_store_->HasPrefPath(prefs::kHomepage));
+ // User preference
+ EXPECT_TRUE(pref_value_store_->HasPrefPath(prefs::kDeleteCache));
+ // Recommended preference
+ EXPECT_TRUE(pref_value_store_->HasPrefPath(prefs::kRecommendedPref));
+ // Unknown preference
+ EXPECT_FALSE(pref_value_store_->HasPrefPath(prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefHasChanged) {
+ // Pref controlled by highest-priority store, set to same value in same store.
+ const char managed_pref_path[] = "managed_pref";
+ const char same_str[] = "same value";
+ scoped_ptr<Value> same_value(Value::CreateStringValue(same_str));
+ enforced_pref_store_->prefs()->SetString(managed_pref_path, same_str);
+ EXPECT_FALSE(pref_value_store_->PrefHasChanged(managed_pref_path,
+ static_cast<PrefNotifier::PrefStoreType>(0), same_value.get()));
+
+ // Pref controlled by highest-priority store, set to different value in
+ // same store.
+ const char other_str[] = "other value";
+ scoped_ptr<Value> other_value(Value::CreateStringValue(other_str));
+ EXPECT_TRUE(pref_value_store_->PrefHasChanged(managed_pref_path,
+ static_cast<PrefNotifier::PrefStoreType>(0), other_value.get()));
+
+ // Pref controlled by user store, set to same value in user store, no lower
+ // store has a value.
+ const char user_pref_path[] = "user_pref";
+ user_pref_store_->prefs()->SetString(user_pref_path, same_str);
+ EXPECT_FALSE(pref_value_store_->PrefHasChanged(user_pref_path,
+ PrefNotifier::USER_STORE, same_value.get()));
+
+ // Pref controlled by user store, set to new value in user store, no lower
+ // store has a value.
+ EXPECT_TRUE(pref_value_store_->PrefHasChanged(user_pref_path,
+ PrefNotifier::USER_STORE, other_value.get()));
+
+ // Pref controlled by user store, set to same value in user store, some lower
+ // store has a value.
+ const char third_str[] = "third value";
+ recommended_pref_store_->prefs()->SetString(user_pref_path, third_str);
+ // This is not necessarily the correct behavior, but it is the current
+ // behavior. See comments in pref_value_store.h and .cc.
+ EXPECT_TRUE(pref_value_store_->PrefHasChanged(user_pref_path,
+ PrefNotifier::USER_STORE, same_value.get()));
+
+ // Pref controlled by user store, set to new value in user store, some lower
+ // store has a value.
+ EXPECT_TRUE(pref_value_store_->PrefHasChanged(user_pref_path,
+ PrefNotifier::USER_STORE, other_value.get()));
+
+ // Pref controlled by user store, set to same value in managed store.
+ EXPECT_TRUE(pref_value_store_->PrefHasChanged(user_pref_path,
+ PrefNotifier::MANAGED_STORE, same_value.get()));
+
+ // Pref controlled by user store, set to new value in managed store.
+ EXPECT_TRUE(pref_value_store_->PrefHasChanged(user_pref_path,
+ PrefNotifier::MANAGED_STORE, other_value.get()));
+
+ // Pref controlled by highest-priority store, set to any value in user store.
+ EXPECT_FALSE(pref_value_store_->PrefHasChanged(managed_pref_path,
+ PrefNotifier::USER_STORE, same_value.get()));
+
+ // Pref controlled by lowest-priority store, set to same value in same store.
+ const char recommended_pref_path[] = "recommended_pref";
+ recommended_pref_store_->prefs()->SetString(recommended_pref_path, same_str);
+ EXPECT_FALSE(pref_value_store_->PrefHasChanged(recommended_pref_path,
+ PrefNotifier::PREF_STORE_TYPE_MAX, same_value.get()));
+
+ // Pref controlled by lowest-priority store, set to different value in same
+ // store.
+ EXPECT_TRUE(pref_value_store_->PrefHasChanged(recommended_pref_path,
+ PrefNotifier::PREF_STORE_TYPE_MAX, other_value.get()));
+}
+
+TEST_F(PrefValueStoreTest, ReadPrefs) {
+ pref_value_store_->ReadPrefs();
+ // The ReadPrefs method of the |DummyPrefStore| deletes the |pref_store|s
+ // internal dictionary and creates a new empty dictionary. Hence this
+ // dictionary does not contain any of the preloaded preferences.
+ // This shows that the ReadPrefs method of the |DummyPrefStore| was called.
+ EXPECT_FALSE(pref_value_store_->HasPrefPath(prefs::kDeleteCache));
+}
+
+TEST_F(PrefValueStoreTest, WritePrefs) {
+ user_pref_store_->set_prefs_written(false);
+ pref_value_store_->WritePrefs();
+ ASSERT_TRUE(user_pref_store_->get_prefs_written());
+}
+
+TEST_F(PrefValueStoreTest, SetUserPrefValue) {
+ Value* new_value = NULL;
+ Value* actual_value = NULL;
+
+ // Test that enforced values can not be set.
+ ASSERT_TRUE(pref_value_store_->PrefValueInManagedStore(prefs::kHomepage));
+ // The Ownership is tranfered to |PrefValueStore|.
+ new_value = Value::CreateStringValue("http://www.youtube.com");
+ pref_value_store_->SetUserPrefValue(prefs::kHomepage, new_value);
+
+ ASSERT_TRUE(pref_value_store_->GetValue(prefs::kHomepage, &actual_value));
+ std::string value_str;
+ actual_value->GetAsString(&value_str);
+ ASSERT_EQ(enforced_pref::kHomepageValue, value_str);
+
+ // User preferences values can be set
+ ASSERT_FALSE(pref_value_store_->PrefValueInManagedStore(prefs::kMaxTabs));
+ actual_value = NULL;
+ pref_value_store_->GetValue(prefs::kMaxTabs, &actual_value);
+ int int_value;
+ EXPECT_TRUE(actual_value->GetAsInteger(&int_value));
+ EXPECT_EQ(user_pref::kMaxTabsValue, int_value);
+
+ new_value = Value::CreateIntegerValue(1);
+ pref_value_store_->SetUserPrefValue(prefs::kMaxTabs, new_value);
+ actual_value = NULL;
+ pref_value_store_->GetValue(prefs::kMaxTabs, &actual_value);
+ EXPECT_TRUE(new_value->Equals(actual_value));
+
+ // Set and Get |DictionaryValue|
+ DictionaryValue* expected_dict_value = CreateSampleDictValue();
+ pref_value_store_->SetUserPrefValue(prefs::kSampleDict, expected_dict_value);
+
+ actual_value = NULL;
+ std::string key(prefs::kSampleDict);
+ pref_value_store_->GetValue(key , &actual_value);
+
+ ASSERT_EQ(expected_dict_value, actual_value);
+ ASSERT_TRUE(expected_dict_value->Equals(actual_value));
+
+ // Set and Get a |ListValue|
+ ListValue* expected_list_value = CreateSampleListValue();
+ pref_value_store_->SetUserPrefValue(prefs::kSampleList, expected_list_value);
+
+ actual_value = NULL;
+ key = prefs::kSampleList;
+ pref_value_store_->GetValue(key, &actual_value);
+
+ ASSERT_EQ(expected_list_value, actual_value);
+ ASSERT_TRUE(expected_list_value->Equals(actual_value));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueInManagedStore) {
+ // Test an enforced preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kHomepage));
+ EXPECT_TRUE(pref_value_store_->PrefValueInManagedStore(prefs::kHomepage));
+
+ // Test an extension preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kCurrentThemeID));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+ prefs::kCurrentThemeID));
+
+ // Test a command-line preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kApplicationLocale));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+ prefs::kApplicationLocale));
+
+ // Test a user preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kMaxTabs));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(prefs::kMaxTabs));
+
+ // Test a preference from the recommended pref store.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kRecommendedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(
+ prefs::kRecommendedPref));
+
+ // Test a preference for which the PrefValueStore does not contain a value.
+ ASSERT_FALSE(pref_value_store_->HasPrefPath(prefs::kMissingPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInManagedStore(prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueInExtensionStore) {
+ // Test an enforced preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kHomepage));
+ EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore(prefs::kHomepage));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kHomepage));
+
+ // Test an extension preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kCurrentThemeID));
+ EXPECT_TRUE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kCurrentThemeID));
+ EXPECT_TRUE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kCurrentThemeID));
+
+ // Test a command-line preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kApplicationLocale));
+ EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kApplicationLocale));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kApplicationLocale));
+
+ // Test a user preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kMaxTabs));
+ EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(prefs::kMaxTabs));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(prefs::kMaxTabs));
+
+ // Test a preference from the recommended pref store.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kRecommendedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kRecommendedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kRecommendedPref));
+
+ // Test a preference for which the PrefValueStore does not contain a value.
+ ASSERT_FALSE(pref_value_store_->HasPrefPath(prefs::kMissingPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInExtensionStore(
+ prefs::kMissingPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromExtensionStore(
+ prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, PrefValueInUserStore) {
+ // Test an enforced preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kHomepage));
+ EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(prefs::kHomepage));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(prefs::kHomepage));
+
+ // Test an extension preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kCurrentThemeID));
+ EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+ prefs::kCurrentThemeID));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+ prefs::kCurrentThemeID));
+
+ // Test a command-line preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kApplicationLocale));
+ EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(
+ prefs::kApplicationLocale));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+ prefs::kApplicationLocale));
+
+ // Test a user preference.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kMaxTabs));
+ EXPECT_TRUE(pref_value_store_->PrefValueInUserStore(prefs::kMaxTabs));
+ EXPECT_TRUE(pref_value_store_->PrefValueFromUserStore(prefs::kMaxTabs));
+
+ // Test a preference from the recommended pref store.
+ ASSERT_TRUE(pref_value_store_->HasPrefPath(prefs::kRecommendedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInUserStore(
+ prefs::kRecommendedPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(
+ prefs::kRecommendedPref));
+
+ // Test a preference for which the PrefValueStore does not contain a value.
+ ASSERT_FALSE(pref_value_store_->HasPrefPath(prefs::kMissingPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueInUserStore(prefs::kMissingPref));
+ EXPECT_FALSE(pref_value_store_->PrefValueFromUserStore(prefs::kMissingPref));
+}
+
+TEST_F(PrefValueStoreTest, TestPolicyRefresh) {
+ // pref_value_store_ is initialized by PrefValueStoreTest to have values
+ // in both it's managed and recommended store. By replacing them with
+ // dummy stores, all of the paths of the prefs originally managed and
+ // recommended stores should change.
+ MockPolicyRefreshCallback callback;
+ EXPECT_CALL(callback, DoCallback(_)).Times(0);
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ pref_value_store_.get(),
+ &PrefValueStore::RefreshPolicyPrefs,
+ new DummyPrefStore(),
+ new DummyPrefStore(),
+ NewCallback(&callback,
+ &MockPolicyRefreshCallback::DoCallback)));
+ Mock::VerifyAndClearExpectations(&callback);
+ EXPECT_CALL(callback, DoCallback(expected_differing_paths_)).Times(1);
+ loop_.RunAllPending();
+}
+
+TEST_F(PrefValueStoreTest, TestRefreshPolicyPrefsCompletion) {
+ // Test changed preferences in managed store and removed
+ // preferences in the recommended store. In addition
+ // to "homepage", the other prefs that are set by default in
+ // the test class are removed by the DummyStore
+ scoped_ptr<DummyPrefStore> new_managed_store(new DummyPrefStore());
+ DictionaryValue* dict = new DictionaryValue();
+ dict->SetString("homepage", "some other changed homepage");
+ new_managed_store->set_prefs(dict);
+ MockPolicyRefreshCallback callback;
+ EXPECT_CALL(callback, DoCallback(expected_differing_paths_)).Times(1);
+ pref_value_store_->RefreshPolicyPrefsCompletion(
+ new_managed_store.release(),
+ new DummyPrefStore(),
+ NewCallback(&callback,
+ &MockPolicyRefreshCallback::DoCallback));
+
+ // Test properties that have been removed from the managed store.
+ // Homepage is still set in managed prefs.
+ expected_differing_paths_.clear();
+ expected_differing_paths_.push_back(std::string("homepage"));
+ MockPolicyRefreshCallback callback2;
+ EXPECT_CALL(callback2, DoCallback(expected_differing_paths_)).Times(1);
+ pref_value_store_->RefreshPolicyPrefsCompletion(
+ new DummyPrefStore(),
+ new DummyPrefStore(),
+ NewCallback(&callback2,
+ &MockPolicyRefreshCallback::DoCallback));
+
+ // Test properties that are added to the recommended store.
+ scoped_ptr<DummyPrefStore> new_recommended_store(new DummyPrefStore());
+ dict = new DictionaryValue();
+ dict->SetString("homepage", "some other changed homepage 2");
+ new_recommended_store->set_prefs(dict);
+ expected_differing_paths_.clear();
+ expected_differing_paths_.push_back(std::string("homepage"));
+ MockPolicyRefreshCallback callback3;
+ EXPECT_CALL(callback3, DoCallback(expected_differing_paths_)).Times(1);
+ pref_value_store_->RefreshPolicyPrefsCompletion(
+ new DummyPrefStore(),
+ new_recommended_store.release(),
+ NewCallback(&callback3,
+ &MockPolicyRefreshCallback::DoCallback));
+
+ // Test adding a multi-key path.
+ new_managed_store.reset(new DummyPrefStore());
+ dict = new DictionaryValue();
+ dict->SetString("segment1.segment2", "value");
+ new_managed_store->set_prefs(dict);
+ expected_differing_paths_.clear();
+ expected_differing_paths_.push_back(std::string("homepage"));
+ expected_differing_paths_.push_back(std::string("segment1"));
+ expected_differing_paths_.push_back(std::string("segment1.segment2"));
+ MockPolicyRefreshCallback callback4;
+ EXPECT_CALL(callback4, DoCallback(expected_differing_paths_)).Times(1);
+ pref_value_store_->RefreshPolicyPrefsCompletion(
+ new_managed_store.release(),
+ new DummyPrefStore(),
+ NewCallback(&callback4,
+ &MockPolicyRefreshCallback::DoCallback));
+}
+
+TEST_F(PrefValueStoreTest, TestConcurrentPolicyRefresh) {
+ MockPolicyRefreshCallback callback1;
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ pref_value_store_.get(),
+ &PrefValueStore::RefreshPolicyPrefs,
+ new DummyPrefStore(),
+ new DummyPrefStore(),
+ NewCallback(&callback1,
+ &MockPolicyRefreshCallback::DoCallback)));
+ EXPECT_CALL(callback1, DoCallback(_)).Times(0);
+
+ MockPolicyRefreshCallback callback2;
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ pref_value_store_.get(),
+ &PrefValueStore::RefreshPolicyPrefs,
+ new DummyPrefStore(),
+ new DummyPrefStore(),
+ NewCallback(&callback2,
+ &MockPolicyRefreshCallback::DoCallback)));
+ EXPECT_CALL(callback2, DoCallback(_)).Times(0);
+
+ MockPolicyRefreshCallback callback3;
+ ChromeThread::PostTask(
+ ChromeThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ pref_value_store_.get(),
+ &PrefValueStore::RefreshPolicyPrefs,
+ new DummyPrefStore(),
+ new DummyPrefStore(),
+ NewCallback(&callback3,
+ &MockPolicyRefreshCallback::DoCallback)));
+ EXPECT_CALL(callback3, DoCallback(_)).Times(0);
+ Mock::VerifyAndClearExpectations(&callback1);
+ Mock::VerifyAndClearExpectations(&callback2);
+ Mock::VerifyAndClearExpectations(&callback3);
+
+ EXPECT_CALL(callback1, DoCallback(expected_differing_paths_)).Times(1);
+ std::vector<std::string> no_differing_paths;
+ EXPECT_CALL(callback2, DoCallback(no_differing_paths)).Times(1);
+ EXPECT_CALL(callback3, DoCallback(no_differing_paths)).Times(1);
+ loop_.RunAllPending();
+}
diff --git a/chrome/browser/prefs/scoped_pref_update.cc b/chrome/browser/prefs/scoped_pref_update.cc
new file mode 100644
index 0000000..de48b4d
--- /dev/null
+++ b/chrome/browser/prefs/scoped_pref_update.cc
@@ -0,0 +1,20 @@
+// 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/utf_string_conversions.h" // TODO(viettrungluu): remove
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/prefs/scoped_pref_update.h"
+
+ScopedPrefUpdate::ScopedPrefUpdate(PrefService* service, const char* path)
+ : service_(service),
+ path_(path) {}
+
+// TODO(viettrungluu): deprecate and remove #include
+ScopedPrefUpdate::ScopedPrefUpdate(PrefService* service, const wchar_t* path)
+ : service_(service),
+ path_(WideToUTF8(path)) {}
+
+ScopedPrefUpdate::~ScopedPrefUpdate() {
+ service_->pref_notifier()->FireObservers(path_.c_str());
+}
diff --git a/chrome/browser/prefs/scoped_pref_update.h b/chrome/browser/prefs/scoped_pref_update.h
new file mode 100644
index 0000000..af84866
--- /dev/null
+++ b/chrome/browser/prefs/scoped_pref_update.h
@@ -0,0 +1,26 @@
+// 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.
+//
+// A helper class that assists preferences in firing notifications when lists
+// are changed.
+
+#ifndef CHROME_BROWSER_PREFS_SCOPED_PREF_UPDATE_H_
+#define CHROME_BROWSER_PREFS_SCOPED_PREF_UPDATE_H_
+#pragma once
+
+#include "chrome/browser/prefs/pref_service.h"
+
+class ScopedPrefUpdate {
+ public:
+ ScopedPrefUpdate(PrefService* service, const char* path);
+ // TODO(viettrungluu): deprecate:
+ ScopedPrefUpdate(PrefService* service, const wchar_t* path);
+ ~ScopedPrefUpdate();
+
+ private:
+ PrefService* service_;
+ std::string path_;
+};
+
+#endif // CHROME_BROWSER_PREFS_SCOPED_PREF_UPDATE_H_
diff --git a/chrome/browser/prefs/session_startup_pref.cc b/chrome/browser/prefs/session_startup_pref.cc
new file mode 100644
index 0000000..827af01
--- /dev/null
+++ b/chrome/browser/prefs/session_startup_pref.cc
@@ -0,0 +1,107 @@
+// 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 "chrome/browser/prefs/session_startup_pref.h"
+
+#include <string>
+
+#include "base/string_piece.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/defaults.h"
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/browser/prefs/scoped_pref_update.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/pref_names.h"
+
+namespace {
+
+// For historical reasons the enum and value registered in the prefs don't line
+// up. These are the values registered in prefs.
+const int kPrefValueDefault = 0;
+const int kPrefValueLast = 1;
+const int kPrefValueURLs = 4;
+
+// Converts a SessionStartupPref::Type to an integer written to prefs.
+int TypeToPrefValue(SessionStartupPref::Type type) {
+ switch (type) {
+ case SessionStartupPref::LAST: return kPrefValueLast;
+ case SessionStartupPref::URLS: return kPrefValueURLs;
+ default: return kPrefValueDefault;
+ }
+}
+
+// Converts an integer pref value to a SessionStartupPref::Type.
+SessionStartupPref::Type PrefValueToType(int pref_value) {
+ switch (pref_value) {
+ case kPrefValueLast: return SessionStartupPref::LAST;
+ case kPrefValueURLs: return SessionStartupPref::URLS;
+ default: return SessionStartupPref::DEFAULT;
+ }
+}
+
+} // namespace
+
+// static
+void SessionStartupPref::RegisterUserPrefs(PrefService* prefs) {
+ prefs->RegisterIntegerPref(prefs::kRestoreOnStartup,
+ TypeToPrefValue(browser_defaults::kDefaultSessionStartupType));
+ prefs->RegisterListPref(prefs::kURLsToRestoreOnStartup);
+}
+
+// static
+void SessionStartupPref::SetStartupPref(
+ Profile* profile,
+ const SessionStartupPref& pref) {
+ DCHECK(profile);
+ SetStartupPref(profile->GetPrefs(), pref);
+}
+
+// static
+void SessionStartupPref::SetStartupPref(PrefService* prefs,
+ const SessionStartupPref& pref) {
+ DCHECK(prefs);
+ prefs->SetInteger(prefs::kRestoreOnStartup, TypeToPrefValue(pref.type));
+
+ // Always save the URLs, that way the UI can remain consistent even if the
+ // user changes the startup type pref.
+ // Ownership of the ListValue retains with the pref service.
+ ScopedPrefUpdate update(prefs, prefs::kURLsToRestoreOnStartup);
+ ListValue* url_pref_list =
+ prefs->GetMutableList(prefs::kURLsToRestoreOnStartup);
+ DCHECK(url_pref_list);
+ url_pref_list->Clear();
+ for (size_t i = 0; i < pref.urls.size(); ++i) {
+ url_pref_list->Set(static_cast<int>(i),
+ new StringValue(pref.urls[i].spec()));
+ }
+}
+
+// static
+SessionStartupPref SessionStartupPref::GetStartupPref(Profile* profile) {
+ DCHECK(profile);
+ return GetStartupPref(profile->GetPrefs());
+}
+
+// static
+SessionStartupPref SessionStartupPref::GetStartupPref(PrefService* prefs) {
+ DCHECK(prefs);
+ SessionStartupPref pref(
+ PrefValueToType(prefs->GetInteger(prefs::kRestoreOnStartup)));
+
+ // Always load the urls, even if the pref type isn't URLS. This way the
+ // preferenes panels can show the user their last choice.
+ ListValue* url_pref_list = prefs->GetMutableList(
+ prefs::kURLsToRestoreOnStartup);
+ DCHECK(url_pref_list);
+ for (size_t i = 0; i < url_pref_list->GetSize(); ++i) {
+ Value* value = NULL;
+ if (url_pref_list->Get(i, &value)) {
+ std::string url_text;
+ if (value->GetAsString(&url_text))
+ pref.urls.push_back(GURL(url_text));
+ }
+ }
+
+ return pref;
+}
diff --git a/chrome/browser/prefs/session_startup_pref.h b/chrome/browser/prefs/session_startup_pref.h
new file mode 100644
index 0000000..a2a1660
--- /dev/null
+++ b/chrome/browser/prefs/session_startup_pref.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PREFS_SESSION_STARTUP_PREF_H__
+#define CHROME_BROWSER_PREFS_SESSION_STARTUP_PREF_H__
+#pragma once
+
+#include <vector>
+
+#include "googleurl/src/gurl.h"
+
+class PrefService;
+class Profile;
+
+// StartupPref specifies what should happen at startup for a specified profile.
+// StartupPref is stored in the preferences for a particular profile.
+struct SessionStartupPref {
+ enum Type {
+ // Indicates the user doesn't want to restore a previous session.
+ DEFAULT,
+
+ // Indicates the user wants to restore the last session.
+ LAST,
+
+ // Indicates the user wants to restore a specific set of URLs. The URLs
+ // are contained in urls.
+ URLS
+ };
+
+ static void RegisterUserPrefs(PrefService* prefs);
+
+ // What should happen on startup for the specified profile.
+ static void SetStartupPref(Profile* profile, const SessionStartupPref& pref);
+ static void SetStartupPref(PrefService* prefs,
+ const SessionStartupPref& pref);
+ static SessionStartupPref GetStartupPref(Profile* profile);
+ static SessionStartupPref GetStartupPref(PrefService* prefs);
+
+ SessionStartupPref() : type(DEFAULT) {}
+
+ explicit SessionStartupPref(Type type) : type(type) {}
+
+ // What to do on startup.
+ Type type;
+
+ // The URLs to restore. Only used if type == URLS.
+ std::vector<GURL> urls;
+};
+
+#endif // CHROME_BROWSER_PREFS_SESSION_STARTUP_PREF_H__