diff options
author | albertb@google.com <albertb@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-12 22:27:57 +0000 |
---|---|---|
committer | albertb@google.com <albertb@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-01-12 22:27:57 +0000 |
commit | 51133f91c7f0e35e302eabcfcfed105310fdd898 (patch) | |
tree | 84115e3bc436448133178441287d107dab82ec40 /chrome | |
parent | e5518c46e8e6bcd6eb23682655b77db823e3ae92 (diff) | |
download | chromium_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.cc | 155 | ||||
-rw-r--r-- | chrome/browser/sync/glue/preference_change_processor.h | 69 | ||||
-rw-r--r-- | chrome/browser/sync/glue/preference_model_associator.cc | 174 | ||||
-rw-r--r-- | chrome/browser/sync/glue/preference_model_associator.h | 114 | ||||
-rwxr-xr-x | chrome/chrome_browser.gypi | 4 |
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', |