summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authoralbertb@google.com <albertb@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-12 22:27:57 +0000
committeralbertb@google.com <albertb@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-12 22:27:57 +0000
commit51133f91c7f0e35e302eabcfcfed105310fdd898 (patch)
tree84115e3bc436448133178441287d107dab82ec40 /chrome
parente5518c46e8e6bcd6eb23682655b77db823e3ae92 (diff)
downloadchromium_src-51133f91c7f0e35e302eabcfcfed105310fdd898.zip
chromium_src-51133f91c7f0e35e302eabcfcfed105310fdd898.tar.gz
chromium_src-51133f91c7f0e35e302eabcfcfed105310fdd898.tar.bz2
Change processor and model associator for preferences.
Review URL: http://codereview.chromium.org/437028 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@36056 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/sync/glue/preference_change_processor.cc155
-rw-r--r--chrome/browser/sync/glue/preference_change_processor.h69
-rw-r--r--chrome/browser/sync/glue/preference_model_associator.cc174
-rw-r--r--chrome/browser/sync/glue/preference_model_associator.h114
-rwxr-xr-xchrome/chrome_browser.gypi4
5 files changed, 516 insertions, 0 deletions
diff --git a/chrome/browser/sync/glue/preference_change_processor.cc b/chrome/browser/sync/glue/preference_change_processor.cc
new file mode 100644
index 0000000..f85ec5c
--- /dev/null
+++ b/chrome/browser/sync/glue/preference_change_processor.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2006-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 "chrome/browser/sync/glue/preference_change_processor.h"
+
+#include "base/string_util.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/pref_names.h"
+
+namespace browser_sync {
+
+PreferenceChangeProcessor::PreferenceChangeProcessor(
+ UnrecoverableErrorHandler* error_handler)
+ : ChangeProcessor(error_handler),
+ pref_service_(NULL),
+ model_associator_(NULL) {
+}
+
+void PreferenceChangeProcessor::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(running());
+ DCHECK(NotificationType::PREF_CHANGED == type);
+ DCHECK_EQ(pref_service_, Source<PrefService>(source).ptr());
+
+ std::wstring* name = Details<std::wstring>(details).ptr();
+ const PrefService::Preference* preference =
+ pref_service_->FindPreference((*name).c_str());
+ DCHECK(preference);
+
+ sync_api::WriteTransaction trans(share_handle());
+ sync_api::WriteNode sync_node(&trans);
+
+ int64 sync_id = model_associator_->GetSyncIdForChromeId(*name);
+ if (sync_api::kInvalidId == sync_id) {
+ sync_api::ReadNode root_node(&trans);
+ if (!root_node.InitByTagLookup(kPreferencesTag)) {
+ LOG(ERROR) << "Preference root node lookup failed.";
+ error_handler()->OnUnrecoverableError();
+ return;
+ }
+ if (!sync_node.InitByCreation(root_node, NULL)) {
+ LOG(ERROR) << "Preference node creation failed.";
+ error_handler()->OnUnrecoverableError();
+ return;
+ }
+ } else {
+ if (!sync_node.InitByIdLookup(sync_id)) {
+ LOG(ERROR) << "Preference node lookup failed.";
+ error_handler()->OnUnrecoverableError();
+ return;
+ }
+ }
+
+ if (!WritePreference(&sync_node, WideToUTF8(*name),
+ preference->GetValue())) {
+ LOG(ERROR) << "Failed to update preference node.";
+ error_handler()->OnUnrecoverableError();
+ return;
+ }
+ if (sync_api::kInvalidId == sync_id) {
+ model_associator_->Associate(*name, sync_node.GetId());
+ }
+}
+
+void PreferenceChangeProcessor::ApplyChangesFromSyncModel(
+ const sync_api::BaseTransaction* trans,
+ const sync_api::SyncManager::ChangeRecord* changes,
+ int change_count) {
+ if (!running())
+ return;
+ StopObserving();
+
+ // TODO(sync): Remove this once per-datatype filtering is working.
+ sync_api::ReadNode root_node(trans);
+ if (!root_node.InitByTagLookup(kPreferencesTag)) {
+ LOG(ERROR) << "Preference root node lookup failed.";
+ error_handler()->OnUnrecoverableError();
+ return;
+ }
+
+ sync_api::ReadNode sync_node(trans);
+ std::string name;
+ for (int i = 0; i < change_count; ++i) {
+ if (!sync_node.InitByIdLookup(changes[i].id)) {
+ LOG(ERROR) << "Preference node lookup failed.";
+ error_handler()->OnUnrecoverableError();
+ return;
+ }
+
+ // TODO(sync): Remove this once per-datatype filtering is working.
+ // Check that the changed node is a child of the preferences folder.
+ if (root_node.GetId() != sync_node.GetParentId()) continue;
+
+ scoped_ptr<Value> value(ReadPreference(&sync_node, &name));
+ if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
+ changes[i].action) {
+ pref_service_->ClearPref(UTF8ToWide(name).c_str());
+ } else {
+ if (pref_service_->HasPrefPath(UTF8ToWide(name).c_str())) {
+ pref_service_->Set(UTF8ToWide(name).c_str(), *value);
+ }
+ }
+ }
+ StartObserving();
+}
+
+bool PreferenceChangeProcessor::WritePreference(
+ sync_api::WriteNode* node,
+ const std::string& name,
+ const Value* value) {
+ // TODO(albertb): Implement me!
+ return true;
+}
+
+Value* PreferenceChangeProcessor::ReadPreference(
+ sync_api::ReadNode* node,
+ std::string* name) {
+ // TODO(albertb): Implement me!
+ return NULL;
+}
+
+void PreferenceChangeProcessor::StartImpl(Profile* profile) {
+ pref_service_ = profile->GetPrefs();
+ StartObserving();
+}
+
+void PreferenceChangeProcessor::StopImpl() {
+ StopObserving();
+ pref_service_ = NULL;
+}
+
+
+void PreferenceChangeProcessor::StartObserving() {
+ DCHECK(model_associator_ && pref_service_);
+ for (std::set<std::wstring>::const_iterator it =
+ model_associator_->synced_preferences().begin();
+ it != model_associator_->synced_preferences().end(); ++it) {
+ pref_service_->AddPrefObserver((*it).c_str(), this);
+ }
+}
+
+void PreferenceChangeProcessor::StopObserving() {
+ DCHECK(model_associator_ && pref_service_);
+ for (std::set<std::wstring>::const_iterator it =
+ model_associator_->synced_preferences().begin();
+ it != model_associator_->synced_preferences().end(); ++it) {
+ pref_service_->RemovePrefObserver((*it).c_str(), this);
+ }
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/preference_change_processor.h b/chrome/browser/sync/glue/preference_change_processor.h
new file mode 100644
index 0000000..e3b7a96
--- /dev/null
+++ b/chrome/browser/sync/glue/preference_change_processor.h
@@ -0,0 +1,69 @@
+// 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.
+
+#ifndef CHROME_BROWSER_SYNC_GLUE_PREFERENCE_CHANGE_PROCESSOR_H_
+#define CHROME_BROWSER_SYNC_GLUE_PREFERENCE_CHANGE_PROCESSOR_H_
+
+#include "base/scoped_ptr.h"
+#include "chrome/browser/sync/engine/syncapi.h"
+#include "chrome/browser/sync/glue/change_processor.h"
+#include "chrome/browser/sync/glue/preference_model_associator.h"
+#include "chrome/browser/sync/glue/sync_backend_host.h"
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/pref_service.h"
+
+namespace browser_sync {
+
+class UnrecoverableErrorHandler;
+
+// This class is responsible for taking changes from the PrefService and
+// applying them to the sync_api 'syncable' model, and vice versa. All
+// operations and use of this class are from the UI thread.
+class PreferenceChangeProcessor : public ChangeProcessor,
+ public NotificationObserver {
+ public:
+ explicit PreferenceChangeProcessor(UnrecoverableErrorHandler* error_handler);
+ virtual ~PreferenceChangeProcessor() {}
+
+ void set_model_associator(PreferenceModelAssociator* model_associator) {
+ model_associator_ = model_associator;
+ }
+
+ // NotificationObserver implementation.
+ // PrefService -> sync_api model change application.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ // sync_api model -> PrefService change application.
+ virtual void ApplyChangesFromSyncModel(
+ const sync_api::BaseTransaction* trans,
+ const sync_api::SyncManager::ChangeRecord* changes,
+ int change_count);
+
+ protected:
+ virtual void StartImpl(Profile* profile);
+ virtual void StopImpl();
+
+ private:
+ bool WritePreference(sync_api::WriteNode* node,
+ const std::string& name,
+ const Value* value);
+ Value* ReadPreference(sync_api::ReadNode* node, std::string* name);
+
+ void StartObserving();
+ void StopObserving();
+
+ // The model we are processing changes from. Non-NULL when |running_| is true.
+ PrefService* pref_service_;
+
+ // The two models should be associated according to this ModelAssociator.
+ PreferenceModelAssociator* model_associator_;
+
+ DISALLOW_COPY_AND_ASSIGN(PreferenceChangeProcessor);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_GLUE_PREFERENCE_CHANGE_PROCESSOR_H_
diff --git a/chrome/browser/sync/glue/preference_model_associator.cc b/chrome/browser/sync/glue/preference_model_associator.cc
new file mode 100644
index 0000000..924fcf0
--- /dev/null
+++ b/chrome/browser/sync/glue/preference_model_associator.cc
@@ -0,0 +1,174 @@
+// Copyright (c) 2006-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 "chrome/browser/sync/glue/preference_model_associator.h"
+
+#include "base/logging.h"
+#include "base/values.h"
+#include "chrome/browser/sync/engine/syncapi.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/common/json_value_serializer.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+
+namespace browser_sync {
+
+static const char kPreferenceName[] = "preference_name";
+static const char kPreferenceValue[] = "preference_value";
+
+PreferenceModelAssociator::PreferenceModelAssociator(
+ ProfileSyncService* sync_service)
+ : sync_service_(sync_service),
+ preferences_node_id_(sync_api::kInvalidId),
+ ALLOW_THIS_IN_INITIALIZER_LIST(persist_associations_(this)) {
+ DCHECK(sync_service_);
+ synced_preferences_.insert(prefs::kHomePageIsNewTabPage);
+ synced_preferences_.insert(prefs::kHomePage);
+ synced_preferences_.insert(prefs::kRestoreOnStartup);
+ synced_preferences_.insert(prefs::kURLsToRestoreOnStartup);
+ synced_preferences_.insert(prefs::kPopupWhitelistedHosts);
+ synced_preferences_.insert(prefs::kShowBookmarkBar);
+}
+
+bool PreferenceModelAssociator::AssociateModels() {
+
+ // TODO(albertb): Attempt to load the model association from storage.
+ sync_api::ReadTransaction trans(
+ sync_service()->backend()->GetUserShareHandle());
+ sync_api::ReadNode root_node(&trans);
+ if (!root_node.InitByTagLookup(kPreferencesTag)) {
+ sync_service_->OnUnrecoverableError();
+ LOG(ERROR) << "Server did not create the top-level preferences node. We "
+ << "might be running against an out-of-date server.";
+ return false;
+ }
+
+ int64 pref_id = root_node.GetFirstChildId();
+ while (sync_api::kInvalidId != pref_id) {
+ sync_api::ReadNode pref_node(&trans);
+ if (!pref_node.InitByIdLookup(pref_id)) {
+ sync_service_->OnUnrecoverableError();
+ LOG(ERROR) << "Preference node lookup failed.";
+ return false;
+ }
+ // TODO(albertb): Load the preference name and value from the node.
+ // TODO(albertb): Associate the preference name with pref_id.
+ // TODO(albertb): Add the preference to synced_preferences if needed.
+ pref_id = pref_node.GetSuccessorId();
+ }
+ return true;
+}
+
+bool PreferenceModelAssociator::DisassociateModels() {
+ id_map_.clear();
+ id_map_inverse_.clear();
+ dirty_associations_sync_ids_.clear();
+ return true;
+}
+
+bool PreferenceModelAssociator::SyncModelHasUserCreatedNodes() {
+ int64 preferences_sync_id;
+ if (!GetSyncIdForTaggedNode(kPreferencesTag, &preferences_sync_id)) {
+ sync_service()->OnUnrecoverableError();
+ LOG(ERROR) << "Server did not create the top-level preferences node. We "
+ << "might be running against an out-of-date server.";
+ return false;
+ }
+ sync_api::ReadTransaction trans(
+ sync_service()->backend()->GetUserShareHandle());
+
+ sync_api::ReadNode preferences_node(&trans);
+ if (!preferences_node.InitByIdLookup(preferences_sync_id)) {
+ sync_service()->OnUnrecoverableError();
+ LOG(ERROR) << "Server did not create the top-level preferences node. We "
+ << "might be running against an out-of-date server.";
+ }
+
+ // The sync model has user created nodes if the preferences folder has any
+ // children.
+ return sync_api::kInvalidId != preferences_node.GetFirstChildId();
+}
+
+bool PreferenceModelAssociator::ChromeModelHasUserCreatedNodes() {
+ // Assume the preferences model always have user-created nodes.
+ return true;
+}
+
+int64 PreferenceModelAssociator::GetSyncIdForChromeId(
+ const std::wstring preference_name) {
+ PreferenceNameToSyncIdMap::const_iterator iter =
+ id_map_.find(preference_name);
+ return iter == id_map_.end() ? sync_api::kInvalidId : iter->second;
+}
+
+void PreferenceModelAssociator::Associate(std::wstring preference_name,
+ int64 sync_id) {
+ DCHECK_NE(sync_api::kInvalidId, sync_id);
+ DCHECK(id_map_.find(preference_name) == id_map_.end());
+ DCHECK(id_map_inverse_.find(sync_id) == id_map_inverse_.end());
+ id_map_[preference_name] = sync_id;
+ id_map_inverse_[sync_id] = preference_name;
+ dirty_associations_sync_ids_.insert(sync_id);
+ PostPersistAssociationsTask();
+}
+
+void PreferenceModelAssociator::Disassociate(int64 sync_id) {
+ SyncIdToPreferenceNameMap::iterator iter = id_map_inverse_.find(sync_id);
+ if (iter == id_map_inverse_.end())
+ return;
+ id_map_.erase(iter->second);
+ id_map_inverse_.erase(iter);
+ dirty_associations_sync_ids_.erase(sync_id);
+}
+
+bool PreferenceModelAssociator::GetSyncIdForTaggedNode(const std::string& tag,
+ int64* sync_id) {
+ sync_api::ReadTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+ sync_api::ReadNode sync_node(&trans);
+ if (!sync_node.InitByTagLookup(tag.c_str()))
+ return false;
+ *sync_id = sync_node.GetId();
+ return true;
+}
+
+void PreferenceModelAssociator::PostPersistAssociationsTask() {
+ // No need to post a task if a task is already pending.
+ if (!persist_associations_.empty())
+ return;
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ persist_associations_.NewRunnableMethod(
+ &PreferenceModelAssociator::PersistAssociations));
+}
+
+void PreferenceModelAssociator::PersistAssociations() {
+ // If there are no dirty associations we have nothing to do. We handle this
+ // explicity instead of letting the for loop do it to avoid creating a write
+ // transaction in this case.
+ if (dirty_associations_sync_ids_.empty()) {
+ DCHECK(id_map_.empty());
+ DCHECK(id_map_inverse_.empty());
+ return;
+ }
+
+ sync_api::WriteTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+ DirtyAssociationsSyncIds::iterator iter;
+ for (iter = dirty_associations_sync_ids_.begin();
+ iter != dirty_associations_sync_ids_.end();
+ ++iter) {
+ int64 sync_id = *iter;
+ sync_api::WriteNode sync_node(&trans);
+ if (!sync_node.InitByIdLookup(sync_id)) {
+ sync_service_->OnUnrecoverableError();
+ return;
+ }
+ // TODO(sync): Make ExternalId a string?
+ // sync_node.SetExternalId(id_map_inverse_[*iter]);
+ }
+ dirty_associations_sync_ids_.clear();
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/preference_model_associator.h b/chrome/browser/sync/glue/preference_model_associator.h
new file mode 100644
index 0000000..77f846b
--- /dev/null
+++ b/chrome/browser/sync/glue/preference_model_associator.h
@@ -0,0 +1,114 @@
+// 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.
+
+#ifndef CHROME_BROWSER_SYNC_GLUE_PREFERENCE_MODEL_ASSOCIATOR_H_
+#define CHROME_BROWSER_SYNC_GLUE_PREFERENCE_MODEL_ASSOCIATOR_H_
+
+#include <map>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/task.h"
+#include "chrome/browser/sync/glue/model_associator.h"
+#include "chrome/common/pref_service.h"
+
+class ProfileSyncService;
+
+namespace browser_sync {
+
+class PreferenceChangeProcessor;
+
+static const char kPreferencesTag[] = "google_chrome_preferences";
+
+// Contains all model association related logic:
+// * Algorithm to associate preferences model and sync model.
+// * Persisting model associations and loading them back.
+class PreferenceModelAssociator
+ : public PerDataTypeAssociatorInterface<Preference, std::wstring> {
+ public:
+ explicit PreferenceModelAssociator(ProfileSyncService* sync_service);
+ virtual ~PreferenceModelAssociator() { }
+
+ // Returns the list of preference names that should be monitored for changes.
+ const std::set<std::wstring>& synced_preferences() {
+ return synced_preferences_;
+ }
+
+ // PerDataTypeAssociatorInterface implementation.
+ //
+ // Iterates through the sync model looking for matched pairs of items.
+ virtual bool AssociateModels();
+
+ // Clears all associations.
+ virtual bool DisassociateModels();
+
+ // Returns whether the sync model has nodes other than the permanent tagged
+ // nodes.
+ virtual bool SyncModelHasUserCreatedNodes();
+
+ // Returns whether the preference model has any user-defined preferences.
+ virtual bool ChromeModelHasUserCreatedNodes();
+
+ // Not implemented.
+ virtual const Preference* GetChromeNodeFromSyncId(int64 sync_id) {
+ return NULL;
+ }
+
+ // Not implemented.
+ virtual bool InitSyncNodeFromChromeId(std::wstring node_id,
+ sync_api::BaseNode* sync_node) {
+ return false;
+ }
+
+ // Returns the sync id for the given preference name, or sync_api::kInvalidId
+ // if the preference name is not associated to any sync id.
+ virtual int64 GetSyncIdForChromeId(std::wstring node_id);
+
+ // Associates the given preference name with the given sync id.
+ virtual void Associate(std::wstring node_id, int64 sync_id);
+
+ // Remove the association that corresponds to the given sync id.
+ virtual void Disassociate(int64 sync_id);
+
+ // Returns whether a node with the given permanent tag was found and update
+ // |sync_id| with that node's id.
+ bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id);
+
+ protected:
+ // Returns sync service instance.
+ ProfileSyncService* sync_service() { return sync_service_; }
+
+ private:
+ typedef std::map<std::wstring, int64> PreferenceNameToSyncIdMap;
+ typedef std::map<int64, std::wstring> SyncIdToPreferenceNameMap;
+ typedef std::set<int64> DirtyAssociationsSyncIds;
+
+ // Posts a task to persist dirty associations.
+ void PostPersistAssociationsTask();
+
+ // Persists all dirty associations.
+ void PersistAssociations();
+
+ ProfileSyncService* sync_service_;
+ std::set<std::wstring> synced_preferences_;
+ int64 preferences_node_id_;
+
+ PreferenceNameToSyncIdMap id_map_;
+ SyncIdToPreferenceNameMap id_map_inverse_;
+ // Stores the IDs of dirty associations.
+ DirtyAssociationsSyncIds dirty_associations_sync_ids_;
+
+ // Used to post PersistAssociation tasks to the current message loop and
+ // guarantees no invocations can occur if |this| has been deleted. (This
+ // allows this class to be non-refcounted).
+ ScopedRunnableMethodFactory<PreferenceModelAssociator> persist_associations_;
+
+ DISALLOW_COPY_AND_ASSIGN(PreferenceModelAssociator);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_GLUE_PREFERENCE_MODEL_ASSOCIATOR_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index d3e488b..d567984 100755
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1509,6 +1509,10 @@
'browser/sync/glue/http_bridge.cc',
'browser/sync/glue/http_bridge.h',
'browser/sync/glue/model_associator.h',
+ 'browser/sync/glue/preference_change_processor.cc',
+ 'browser/sync/glue/preference_change_processor.h',
+ 'browser/sync/glue/preference_model_associator.cc',
+ 'browser/sync/glue/preference_model_associator.h',
'browser/sync/glue/sync_backend_host.cc',
'browser/sync/glue/sync_backend_host.h',
'browser/sync/profile_sync_service.cc',