diff options
author | stevenjb@chromium.org <stevenjb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-11 20:04:58 +0000 |
---|---|---|
committer | stevenjb@chromium.org <stevenjb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-11 20:04:58 +0000 |
commit | 3bc627156b7c1594df0ae95407e6b9d18f5b7343 (patch) | |
tree | b7261fda0637408bd3d404b048227310dd6f2f02 | |
parent | bcbb9b4e583a1e068871ef591ae7f8590707eca7 (diff) | |
download | chromium_src-3bc627156b7c1594df0ae95407e6b9d18f5b7343.zip chromium_src-3bc627156b7c1594df0ae95407e6b9d18f5b7343.tar.gz chromium_src-3bc627156b7c1594df0ae95407e6b9d18f5b7343.tar.bz2 |
Add AppListSpecifics .proto definition and support code
This CL introduces the AppList sync type.
As of patchset #7 this CL depends on https://codereview.chromium.org/106033003/
BUG=313376
Review URL: https://codereview.chromium.org/78773004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@240162 0039d316-1c4b-4281-b951-d872f2087c98
28 files changed, 557 insertions, 13 deletions
diff --git a/chrome/browser/sync/glue/model_association_manager.cc b/chrome/browser/sync/glue/model_association_manager.cc index 1dbdc85..2d5f084 100644 --- a/chrome/browser/sync/glue/model_association_manager.cc +++ b/chrome/browser/sync/glue/model_association_manager.cc @@ -37,6 +37,7 @@ static const syncer::ModelType kStartOrder[] = { syncer::PRIORITY_PREFERENCES, syncer::EXTENSIONS, syncer::APPS, + syncer::APP_LIST, syncer::THEMES, syncer::SEARCH_ENGINES, syncer::SESSIONS, diff --git a/chrome/browser/sync/profile_sync_components_factory_impl.cc b/chrome/browser/sync/profile_sync_components_factory_impl.cc index 0026d70..f8b5eb4 100644 --- a/chrome/browser/sync/profile_sync_components_factory_impl.cc +++ b/chrome/browser/sync/profile_sync_components_factory_impl.cc @@ -57,6 +57,8 @@ #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/themes/theme_service_factory.h" #include "chrome/browser/themes/theme_syncable_service.h" +#include "chrome/browser/ui/app_list/app_list_syncable_service.h" +#include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h" #include "chrome/browser/webdata/autocomplete_syncable_service.h" #include "chrome/browser/webdata/autofill_profile_syncable_service.h" #include "chrome/browser/webdata/web_data_service_factory.h" @@ -290,6 +292,14 @@ void ProfileSyncComponentsFactoryImpl::RegisterDesktopDataTypes( syncer::APP_SETTINGS, this, profile_, pss)); } +#if defined(ENABLE_APP_LIST) + // App List sync is disabled by default. Register only if enabled. + if (command_line_->HasSwitch(switches::kEnableSyncAppList)) { + pss->RegisterDataTypeController( + new UIDataTypeController(syncer::APP_LIST, this, profile_, pss)); + } +#endif + // Synced Notifications are enabled by default. pss->RegisterDataTypeController( new UIDataTypeController( @@ -379,6 +389,11 @@ base::WeakPtr<syncer::SyncableService> ProfileSyncComponentsFactoryImpl:: case syncer::EXTENSION_SETTINGS: return extension_system_->extension_service()->settings_frontend()-> GetBackendForSync(type)->AsWeakPtr(); +#if defined(ENABLE_APP_LIST) + case syncer::APP_LIST: + return app_list::AppListSyncableServiceFactory::GetForProfile(profile_)-> + AsWeakPtr(); +#endif #if defined(ENABLE_THEMES) case syncer::THEMES: return ThemeServiceFactory::GetForProfile(profile_)-> diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc index 89f5b0d9..fd81590 100644 --- a/chrome/browser/sync/profile_sync_service.cc +++ b/chrome/browser/sync/profile_sync_service.cc @@ -1618,7 +1618,7 @@ const browser_sync::user_selectable_type::UserSelectableSyncType browser_sync::user_selectable_type::PROXY_TABS }; - COMPILE_ASSERT(29 == syncer::MODEL_TYPE_COUNT, UpdateCustomConfigHistogram); + COMPILE_ASSERT(30 == syncer::MODEL_TYPE_COUNT, UpdateCustomConfigHistogram); if (!sync_everything) { const syncer::ModelTypeSet current_types = GetPreferredDataTypes(); diff --git a/chrome/browser/sync/sync_prefs.cc b/chrome/browser/sync/sync_prefs.cc index 6512949..57b68a4 100644 --- a/chrome/browser/sync/sync_prefs.cc +++ b/chrome/browser/sync/sync_prefs.cc @@ -126,6 +126,7 @@ void SyncPrefs::RegisterProfilePrefs( model_set.Put(syncer::NIGORI); model_set.Put(syncer::SEARCH_ENGINES); model_set.Put(syncer::APPS); + model_set.Put(syncer::APP_LIST); model_set.Put(syncer::TYPED_URLS); model_set.Put(syncer::SESSIONS); model_set.Put(syncer::ARTICLES); @@ -308,6 +309,8 @@ const char* SyncPrefs::GetPrefNameForDataType(syncer::ModelType data_type) { return prefs::kSyncExtensionSettings; case syncer::EXTENSIONS: return prefs::kSyncExtensions; + case syncer::APP_LIST: + return prefs::kSyncAppList; case syncer::APP_SETTINGS: return prefs::kSyncAppSettings; case syncer::APPS: @@ -391,6 +394,7 @@ syncer::ModelTypeSet SyncPrefs::GetAcknowledgeSyncedTypesForTest() const { void SyncPrefs::RegisterPrefGroups() { pref_groups_[syncer::APPS].Put(syncer::APP_NOTIFICATIONS); pref_groups_[syncer::APPS].Put(syncer::APP_SETTINGS); + pref_groups_[syncer::APPS].Put(syncer::APP_LIST); pref_groups_[syncer::AUTOFILL].Put(syncer::AUTOFILL_PROFILE); diff --git a/chrome/browser/sync/sync_prefs_unittest.cc b/chrome/browser/sync/sync_prefs_unittest.cc index 357ae27..6356ebe 100644 --- a/chrome/browser/sync/sync_prefs_unittest.cc +++ b/chrome/browser/sync/sync_prefs_unittest.cc @@ -147,6 +147,7 @@ TEST_F(SyncPrefsTest, PreferredTypesNotKeepEverythingSynced) { expected_preferred_types.Put(syncer::SEARCH_ENGINES); } if (it.Get() == syncer::APPS) { + expected_preferred_types.Put(syncer::APP_LIST); expected_preferred_types.Put(syncer::APP_NOTIFICATIONS); expected_preferred_types.Put(syncer::APP_SETTINGS); } diff --git a/chrome/browser/sync/user_selectable_sync_type.h b/chrome/browser/sync/user_selectable_sync_type.h index be854f0..5c02372 100644 --- a/chrome/browser/sync/user_selectable_sync_type.h +++ b/chrome/browser/sync/user_selectable_sync_type.h @@ -40,6 +40,7 @@ enum UserSelectableSyncType { // NIGORI, // DICTIONARY // SEARCH_ENGINES, + // APP_LIST, // APP_SETTINGS, // EXTENSION_SETTINGS, // APP_NOTIFICATIONS, diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc index fd058da..2d0023a 100644 --- a/chrome/browser/ui/app_list/app_list_syncable_service.cc +++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc @@ -11,10 +11,82 @@ #include "chrome/browser/ui/app_list/extension_app_model_builder.h" #include "chrome/browser/ui/host_desktop.h" #include "content/public/browser/notification_source.h" +#include "sync/api/sync_change_processor.h" +#include "sync/api/sync_data.h" +#include "sync/api/sync_merge_result.h" +#include "sync/protocol/sync.pb.h" +#include "ui/app_list/app_list_item_model.h" #include "ui/app_list/app_list_model.h" +using syncer::SyncChange; + namespace app_list { +namespace { + +void UpdateSyncItemFromSync(const sync_pb::AppListSpecifics& specifics, + AppListSyncableService::SyncItem* item) { + DCHECK_EQ(item->item_id, specifics.item_id()); + item->item_type = specifics.item_type(); + item->item_name = specifics.item_name(); + item->parent_id = specifics.parent_id(); + if (!specifics.page_ordinal().empty()) + item->page_ordinal = syncer::StringOrdinal(specifics.page_ordinal()); + if (!specifics.item_ordinal().empty()) + item->item_ordinal = syncer::StringOrdinal(specifics.item_ordinal()); +} + +bool UpdateSyncItemFromAppItem(const AppListItemModel* app_item, + AppListSyncableService::SyncItem* sync_item) { + DCHECK_EQ(sync_item->item_id, app_item->id()); + bool changed = false; + if (!sync_item->item_ordinal.IsValid() || + !app_item->position().Equals(sync_item->item_ordinal)) { + sync_item->item_ordinal = app_item->position(); + changed = true; + } + // TODO(stevenjb): Set parent_id and page_ordinal. + return changed; +} + +void GetSyncSpecificsFromSyncItem(const AppListSyncableService::SyncItem* item, + sync_pb::AppListSpecifics* specifics) { + DCHECK(specifics); + specifics->set_item_id(item->item_id); + specifics->set_item_type(item->item_type); + specifics->set_item_name(item->item_name); + specifics->set_parent_id(item->parent_id); + if (item->page_ordinal.IsValid()) + specifics->set_page_ordinal(item->page_ordinal.ToInternalValue()); + if (item->item_ordinal.IsValid()) + specifics->set_item_ordinal(item->item_ordinal.ToInternalValue()); +} + +syncer::SyncData GetSyncDataFromSyncItem( + const AppListSyncableService::SyncItem* item) { + sync_pb::EntitySpecifics specifics; + GetSyncSpecificsFromSyncItem(item, specifics.mutable_app_list()); + return syncer::SyncData::CreateLocalData(item->item_id, + item->item_id, + specifics); +} + +} // namespace + +// AppListSyncableService::SyncItem + +AppListSyncableService::SyncItem::SyncItem( + const std::string& id, + sync_pb::AppListSpecifics::AppListItemType type) + : item_id(id), + item_type(type) { +} + +AppListSyncableService::SyncItem::~SyncItem() { +} + +// AppListSyncableService + AppListSyncableService::AppListSyncableService( Profile* profile, ExtensionService* extension_service) @@ -31,6 +103,7 @@ AppListSyncableService::AppListSyncableService( } AppListSyncableService::~AppListSyncableService() { + STLDeleteContainerPairSecondPointers(sync_items_.begin(), sync_items_.end()); } void AppListSyncableService::BuildModel() { @@ -46,7 +119,7 @@ void AppListSyncableService::BuildModel() { apps_builder_.reset( new ExtensionAppModelBuilder(profile_, model_.get(), controller)); DCHECK(profile_); - VLOG(1) << "AppListSyncableService Created."; + DVLOG(1) << "AppListSyncableService Created."; } void AppListSyncableService::Observe( @@ -59,4 +132,225 @@ void AppListSyncableService::Observe( BuildModel(); } +void AppListSyncableService::RemoveItem(const std::string& id) { + SyncItemMap::iterator iter = sync_items_.find(id); + if (iter == sync_items_.end()) + return; + SyncItem* sync_item = iter->second; + if (SyncStarted()) { + SyncChange sync_change(FROM_HERE, SyncChange::ACTION_DELETE, + GetSyncDataFromSyncItem(sync_item)); + sync_processor_->ProcessSyncChanges( + FROM_HERE, syncer::SyncChangeList(1, sync_change)); + } + delete sync_item; + sync_items_.erase(iter); + model_->item_list()->DeleteItem(id); +} + +// AppListSyncableService syncer::SyncableService + +syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing( + syncer::ModelType type, + const syncer::SyncDataList& initial_sync_data, + scoped_ptr<syncer::SyncChangeProcessor> sync_processor, + scoped_ptr<syncer::SyncErrorFactory> error_handler) { + DCHECK(!sync_processor_.get()); + DCHECK(sync_processor.get()); + DCHECK(error_handler.get()); + + sync_processor_ = sync_processor.Pass(); + sync_error_handler_ = error_handler.Pass(); + + syncer::SyncMergeResult result = syncer::SyncMergeResult(type); + result.set_num_items_before_association(sync_items_.size()); + + // Copy all sync items to |unsynced_items|. + std::set<std::string> unsynced_items; + for (SyncItemMap::const_iterator iter = sync_items_.begin(); + iter != sync_items_.end(); ++iter) { + unsynced_items.insert(iter->first); + } + + // Create SyncItem entries for initial_sync_data. + size_t new_items = 0, updated_items = 0; + DVLOG(1) << "MergeDataAndStartSyncing: " << initial_sync_data.size(); + for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin(); + iter != initial_sync_data.end(); ++iter) { + const syncer::SyncData& data = *iter; + DCHECK_EQ(syncer::APP_LIST, data.GetDataType()); + if (CreateOrUpdateSyncItem(data.GetSpecifics().app_list())) + ++new_items; + else + ++updated_items; + unsynced_items.erase(data.GetSpecifics().app_list().item_id()); + } + + result.set_num_items_after_association(sync_items_.size()); + result.set_num_items_added(new_items); + result.set_num_items_deleted(0); + result.set_num_items_modified(updated_items); + + // Send unsynced items. Does not affect |result|. + syncer::SyncChangeList change_list; + for (std::set<std::string>::iterator iter = unsynced_items.begin(); + iter != unsynced_items.end(); ++iter) { + SyncItem* sync_item = FindSyncItem(*iter); + change_list.push_back(SyncChange(FROM_HERE, SyncChange::ACTION_ADD, + GetSyncDataFromSyncItem(sync_item))); + } + sync_processor_->ProcessSyncChanges(FROM_HERE, change_list); + + return result; +} + +void AppListSyncableService::StopSyncing(syncer::ModelType type) { + DCHECK_EQ(type, syncer::APP_LIST); + + sync_processor_.reset(); + sync_error_handler_.reset(); +} + +syncer::SyncDataList AppListSyncableService::GetAllSyncData( + syncer::ModelType type) const { + DCHECK_EQ(syncer::APP_LIST, type); + + syncer::SyncDataList list; + for (SyncItemMap::const_iterator iter = sync_items_.begin(); + iter != sync_items_.end(); ++iter) { + list.push_back(GetSyncDataFromSyncItem(iter->second)); + } + return list; +} + +syncer::SyncError AppListSyncableService::ProcessSyncChanges( + const tracked_objects::Location& from_here, + const syncer::SyncChangeList& change_list) { + if (!sync_processor_.get()) { + return syncer::SyncError(FROM_HERE, + syncer::SyncError::DATATYPE_ERROR, + "App List syncable service is not started.", + syncer::APP_LIST); + } + + DVLOG(1) << "ProcessSyncChanges: " << change_list.size(); + for (syncer::SyncChangeList::const_iterator iter = change_list.begin(); + iter != change_list.end(); ++iter) { + const SyncChange& change = *iter; + if (change.change_type() == SyncChange::ACTION_ADD || + change.change_type() == SyncChange::ACTION_UPDATE) { + CreateOrUpdateSyncItem(change.sync_data().GetSpecifics().app_list()); + } else if (change.change_type() == SyncChange::ACTION_DELETE) { + DeleteSyncItem(change.sync_data().GetSpecifics().app_list()); + } else { + LOG(WARNING) << "Invalid sync change"; + } + } + return syncer::SyncError(); +} + +// AppListSyncableService private + +bool AppListSyncableService::SyncStarted() { + if (sync_processor_.get()) + return true; + if (flare_.is_null()) { + flare_ = sync_start_util::GetFlareForSyncableService(profile_->GetPath()); + flare_.Run(syncer::APP_LIST); + } + return false; +} + +AppListSyncableService::SyncItem* AppListSyncableService::AddItem( + sync_pb::AppListSpecifics::AppListItemType type, + AppListItemModel* app_item) { + const std::string& item_id = app_item->id(); + if (item_id.empty()) { + LOG(ERROR) << "AppListItemModel item with empty ID"; + return NULL; + } + bool new_item = false; + SyncItem* sync_item = FindOrCreateSyncItem(item_id, type, &new_item); + DVLOG(1) << "Add AppListItemModel: " << item_id << " New: " << new_item; + UpdateSyncItemFromAppItem(app_item, sync_item); + if (SyncStarted()) { + SyncChange sync_change(FROM_HERE, SyncChange::ACTION_ADD, + GetSyncDataFromSyncItem(sync_item)); + sync_processor_->ProcessSyncChanges( + FROM_HERE, syncer::SyncChangeList(1, sync_change)); + } + return sync_item; +} + +void AppListSyncableService::UpdateItem(AppListItemModel* item) { + SyncItemMap::iterator iter = sync_items_.find(item->id()); + if (iter == sync_items_.end()) { + LOG(ERROR) << "UpdateItem: no sync item: " << item->id(); + return; + } + SyncItem* sync_item = iter->second; + if (!UpdateSyncItemFromAppItem(item, sync_item)) + return; // No change. + if (!SyncStarted()) + return; + SyncChange sync_change(FROM_HERE, SyncChange::ACTION_UPDATE, + GetSyncDataFromSyncItem(sync_item)); + sync_processor_->ProcessSyncChanges( + FROM_HERE, syncer::SyncChangeList(1, sync_change)); +} + +AppListSyncableService::SyncItem* +AppListSyncableService::FindSyncItem(const std::string& item_id) { + SyncItemMap::iterator iter = sync_items_.find(item_id); + if (iter == sync_items_.end()) + return NULL; + return iter->second; +} + +AppListSyncableService::SyncItem* AppListSyncableService::FindOrCreateSyncItem( + const std::string& item_id, + sync_pb::AppListSpecifics::AppListItemType type, + bool* new_item) { + SyncItem* item = FindSyncItem(item_id); + if (item) { + DCHECK(type == item->item_type); + *new_item = false; + return item; + } + + item = new SyncItem(item_id, type); + sync_items_[item_id] = item; + *new_item = true; + return item; +} + +bool AppListSyncableService::CreateOrUpdateSyncItem( + const sync_pb::AppListSpecifics& specifics) { + const std::string& item_id = specifics.item_id(); + if (item_id.empty()) { + LOG(ERROR) << "CreateOrUpdate AppList item with empty ID"; + return false; + } + bool new_item = false; + SyncItem* sync_item = + FindOrCreateSyncItem(item_id, specifics.item_type(), &new_item); + UpdateSyncItemFromSync(specifics, sync_item); + // TODO(stevenjb): Add or update AppItem item in model. + return new_item; +} + +void AppListSyncableService::DeleteSyncItem( + const sync_pb::AppListSpecifics& specifics) { + const std::string& item_id = specifics.item_id(); + if (item_id.empty()) { + LOG(ERROR) << "Delete AppList item with empty ID"; + return; + } + SyncItemMap::iterator iter = sync_items_.find(item_id); + if (iter == sync_items_.end()) + return; + delete iter->second; + sync_items_.erase(iter); +} + } // namespace app_list diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.h b/chrome/browser/ui/app_list/app_list_syncable_service.h index 52ec423..30d3d5a 100644 --- a/chrome/browser/ui/app_list/app_list_syncable_service.h +++ b/chrome/browser/ui/app_list/app_list_syncable_service.h @@ -5,45 +5,124 @@ #ifndef CHROME_BROWSER_UI_APP_LIST_APP_LIST_SYNCABLE_SERVICE_H_ #define CHROME_BROWSER_UI_APP_LIST_APP_LIST_SYNCABLE_SERVICE_H_ +#include <map> + #include "base/memory/scoped_ptr.h" +#include "chrome/browser/sync/glue/sync_start_util.h" #include "components/browser_context_keyed_service/browser_context_keyed_service.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" +#include "sync/api/string_ordinal.h" +#include "sync/api/sync_change.h" +#include "sync/api/sync_change_processor.h" +#include "sync/api/sync_error_factory.h" +#include "sync/api/syncable_service.h" +#include "sync/protocol/app_list_specifics.pb.h" class ExtensionAppModelBuilder; class ExtensionService; class Profile; +namespace sync_pb { +class AppListSpecifics; +} + namespace app_list { class AppListModel; +class AppListItemModel; // Keyed Service that owns, stores, and syncs an AppListModel for an // ExtensionSystem and corresponding profile. -// TODO(stevenjb): Integrate with Sync. crbug.com/305024. -class AppListSyncableService : public BrowserContextKeyedService, +class AppListSyncableService : public syncer::SyncableService, + public BrowserContextKeyedService, public content::NotificationObserver { public: + struct SyncItem { + SyncItem(const std::string& id, + sync_pb::AppListSpecifics::AppListItemType type); + ~SyncItem(); + const std::string item_id; + sync_pb::AppListSpecifics::AppListItemType item_type; + std::string item_name; + std::string parent_id; + syncer::StringOrdinal page_ordinal; + syncer::StringOrdinal item_ordinal; + }; + // Create an empty model. Then, if |extension_service| is non-NULL and ready, // populate it. Otherwise populate the model once extensions become ready. AppListSyncableService(Profile* profile, ExtensionService* extension_service); virtual ~AppListSyncableService(); + // TODO(stevenjb): Implement specific Add and Update methods for + // ExtensionAppItem, etc. + + // Removes sync item matching |id|. + void RemoveItem(const std::string& id); + AppListModel* model() { return model_.get(); } + // syncer::SyncableService + virtual syncer::SyncMergeResult MergeDataAndStartSyncing( + syncer::ModelType type, + const syncer::SyncDataList& initial_sync_data, + scoped_ptr<syncer::SyncChangeProcessor> sync_processor, + scoped_ptr<syncer::SyncErrorFactory> error_handler) OVERRIDE; + virtual void StopSyncing(syncer::ModelType type) OVERRIDE; + virtual syncer::SyncDataList GetAllSyncData( + syncer::ModelType type) const OVERRIDE; + virtual syncer::SyncError ProcessSyncChanges( + const tracked_objects::Location& from_here, + const syncer::SyncChangeList& change_list) OVERRIDE; + private: - void BuildModel(); + typedef std::map<std::string, SyncItem*> SyncItemMap; - // content::NotificationObserver: + // content::NotificationObserver virtual void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE; + // Builds the model once ExtensionService is ready. + void BuildModel(); + + // Returns true if sync has started, otherwise creates and runs |flare_|. + bool SyncStarted(); + + // Common functionality for adding items. + SyncItem* AddItem(sync_pb::AppListSpecifics::AppListItemType type, + AppListItemModel* item); + + // Common functionality for updating items. + void UpdateItem(AppListItemModel* item); + + // Returns an existing SyncItem corresponding to |item_id| or NULL. + SyncItem* FindSyncItem(const std::string& item_id); + + // Returns a SyncItem corresponding to |item_id|. Sets |new_item| if an item + // was created. + SyncItem* FindOrCreateSyncItem( + const std::string& item_id, + sync_pb::AppListSpecifics::AppListItemType type, + bool* new_item); + + // Creates or updates a SyncItem from |specifics|. Returns true if an item + // was created. + bool CreateOrUpdateSyncItem(const sync_pb::AppListSpecifics& specifics); + + // Deletes a SyncItem matching |specifics|. + void DeleteSyncItem(const sync_pb::AppListSpecifics& specifics); + Profile* profile_; content::NotificationRegistrar registrar_; scoped_ptr<AppListModel> model_; scoped_ptr<ExtensionAppModelBuilder> apps_builder_; + scoped_ptr<syncer::SyncChangeProcessor> sync_processor_; + scoped_ptr<syncer::SyncErrorFactory> sync_error_handler_; + SyncItemMap sync_items_; + syncer::SyncableService::StartSyncFlare flare_; DISALLOW_COPY_AND_ASSIGN(AppListSyncableService); }; diff --git a/chrome/browser/ui/webui/sync_setup_handler.cc b/chrome/browser/ui/webui/sync_setup_handler.cc index 8d595ec..c9cab92 100644 --- a/chrome/browser/ui/webui/sync_setup_handler.cc +++ b/chrome/browser/ui/webui/sync_setup_handler.cc @@ -95,7 +95,7 @@ const char* kDataTypeNames[] = { "tabs" }; -COMPILE_ASSERT(29 == syncer::MODEL_TYPE_COUNT, +COMPILE_ASSERT(30 == syncer::MODEL_TYPE_COUNT, update_kDataTypeNames_to_match_UserSelectableTypes); typedef std::map<syncer::ModelType, const char*> ModelTypeNameMap; @@ -752,7 +752,7 @@ void SyncSetupHandler::HandleShowSetupUI(const ListValue* args) { // On ChromeOS, we need to sign out the user session to fix an auth error, so // the user goes through the real signin flow to generate a new auth token. void SyncSetupHandler::HandleDoSignOutOnAuthError(const ListValue* args) { - DLOG(INFO) << "Signing out the user to fix a sync error."; + DVLOG(1) << "Signing out the user to fix a sync error."; chrome::AttemptUserExit(); } #endif diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 3d18344..f1d3e95 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -762,8 +762,11 @@ const char kEnableSyncSyncedNotifications[] = // Enables sync/API based session sync implementation (in favor of legacy). const char kEnableSyncSessionsV2[] = "enable-sync-sessions-v2"; +// Enables syncing of the app list. +const char kEnableSyncAppList[] = "enable-sync-app-list"; + // Enables synced articles. -const char kEnableSyncArticles[] = "enable-sync-articles"; +const char kEnableSyncArticles[] = "enable-sync-articles"; // Enables context menu for selecting groups of tabs. const char kEnableTabGroupsContextMenu[] = "enable-tab-groups-context-menu"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 6caacba..a9ac5f8 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -216,6 +216,7 @@ extern const char kEnableSpellingFeedbackFieldTrial[]; extern const char kEnableStackedTabStrip[]; extern const char kEnableStreamlinedHostedApps[]; extern const char kEnableSuggestionsTabPage[]; +extern const char kEnableSyncAppList[]; extern const char kEnableSyncArticles[]; extern const char kEnableSyncSyncedNotifications[]; extern const char kEnableSyncSessionsV2[]; diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 6d16bfd..62b23ba 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -1827,6 +1827,7 @@ const char kSyncKeepEverythingSynced[] = "sync.keep_everything_synced"; // Booleans specifying whether the user has selected to sync the following // datatypes. +const char kSyncAppList[] = "sync.app_list"; const char kSyncAppNotifications[] = "sync.app_notifications"; const char kSyncAppSettings[] = "sync.app_settings"; const char kSyncApps[] = "sync.apps"; diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index b3f3651..0249ece 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -609,6 +609,7 @@ extern const char kSyncHasAuthError[]; extern const char kSyncHasSetupCompleted[]; extern const char kSyncKeepEverythingSynced[]; +extern const char kSyncAppList[]; extern const char kSyncAppNotifications[]; extern const char kSyncAppSettings[]; extern const char kSyncApps[]; diff --git a/sync/internal_api/public/base/model_type.h b/sync/internal_api/public/base/model_type.h index 4f512b4..c618c450 100644 --- a/sync/internal_api/public/base/model_type.h +++ b/sync/internal_api/public/base/model_type.h @@ -101,6 +101,8 @@ enum ModelType { MANAGED_USERS, // Distilled articles. ARTICLES, + // App List items + APP_LIST, // ---- Proxy types ---- // Proxy types are excluded from the sync protocol, but are still considered diff --git a/sync/protocol/app_list_specifics.proto b/sync/protocol/app_list_specifics.proto new file mode 100644 index 0000000..c677a4b --- /dev/null +++ b/sync/protocol/app_list_specifics.proto @@ -0,0 +1,49 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Sync protocol datatype extension for the app list (aka app launcher). + +// Update proto_{value,enum}_conversions{.h,.cc,_unittest.cc} if you change +// any fields in this file. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; +option retain_unknown_fields = true; + +package sync_pb; + +// Properties of app list objects. +message AppListSpecifics { + // Unique identifier for the item: + // * TYPE_FOLDER: Folder id (generated) + // * TYPE_APP: App Id + // * TYPE_URL: Url + optional string item_id = 1; + + // What type of item this is. + enum AppListItemType { + // An extension app. + TYPE_APP = 1; + // A request to remove any matching default installed apps. + TYPE_REMOVE_DEFAULT_APP = 2; + // A folder containing entries whose |parent_id| matches |item_id|. + TYPE_FOLDER = 3; + // A URL shortcut (functionally equivalent to a bookmark). + TYPE_URL = 4; + } + optional AppListItemType item_type = 2; + + // Item name (FOLDER or URL). + optional string item_name = 3; + + // Id of the parent (folder) item. + optional string parent_id = 4; + + // Which page this item will appear on in the app list. + optional string page_ordinal = 5; + + // Where on a page this item will appear. + optional string item_ordinal = 6; +} diff --git a/sync/protocol/nigori_specifics.proto b/sync/protocol/nigori_specifics.proto index 662b94a..87e6c37 100644 --- a/sync/protocol/nigori_specifics.proto +++ b/sync/protocol/nigori_specifics.proto @@ -123,5 +123,8 @@ message NigoriSpecifics { // Boolean corresponding to whether articles should be encrypted. optional bool encrypt_articles = 37; + + // Boolean corresponding to whether app list items should be encrypted. + optional bool encrypt_app_list = 38; } diff --git a/sync/protocol/proto_enum_conversions.cc b/sync/protocol/proto_enum_conversions.cc index 3183633d..af95822 100644 --- a/sync/protocol/proto_enum_conversions.cc +++ b/sync/protocol/proto_enum_conversions.cc @@ -20,6 +20,20 @@ namespace syncer { #define ENUM_CASE(enum_parent, enum_value) \ case enum_parent::enum_value: return #enum_value +const char* GetAppListItemTypeString( + sync_pb::AppListSpecifics::AppListItemType item_type) { + ASSERT_ENUM_BOUNDS(sync_pb::AppListSpecifics, AppListItemType, + TYPE_APP, TYPE_URL); + switch (item_type) { + ENUM_CASE(sync_pb::AppListSpecifics, TYPE_APP); + ENUM_CASE(sync_pb::AppListSpecifics, TYPE_REMOVE_DEFAULT_APP); + ENUM_CASE(sync_pb::AppListSpecifics, TYPE_FOLDER); + ENUM_CASE(sync_pb::AppListSpecifics, TYPE_URL); + } + NOTREACHED(); + return ""; +} + const char* GetBrowserTypeString( sync_pb::SessionWindow::BrowserType browser_type) { ASSERT_ENUM_BOUNDS(sync_pb::SessionWindow, BrowserType, diff --git a/sync/protocol/proto_enum_conversions.h b/sync/protocol/proto_enum_conversions.h index b6d84fa..6812cb7 100644 --- a/sync/protocol/proto_enum_conversions.h +++ b/sync/protocol/proto_enum_conversions.h @@ -8,6 +8,7 @@ // Keep this file in sync with the .proto files in this directory. #include "sync/base/sync_export.h" +#include "sync/protocol/app_list_specifics.pb.h" #include "sync/protocol/client_debug_info.pb.h" #include "sync/protocol/session_specifics.pb.h" #include "sync/protocol/sync.pb.h" @@ -20,6 +21,9 @@ namespace syncer { // The returned strings (which don't have to be freed) are in ASCII. // The result of passing in an invalid enum value is undefined. +SYNC_EXPORT_PRIVATE const char* GetAppListItemTypeString( + sync_pb::AppListSpecifics::AppListItemType item_type); + SYNC_EXPORT_PRIVATE const char* GetBrowserTypeString( sync_pb::SessionWindow::BrowserType browser_type); diff --git a/sync/protocol/proto_enum_conversions_unittest.cc b/sync/protocol/proto_enum_conversions_unittest.cc index f66d379..7b323a5 100644 --- a/sync/protocol/proto_enum_conversions_unittest.cc +++ b/sync/protocol/proto_enum_conversions_unittest.cc @@ -25,6 +25,13 @@ void TestEnumStringFunction(const char* (*enum_string_fn)(T), } } +TEST_F(ProtoEnumConversionsTest, GetAppListItemTypeString) { + TestEnumStringFunction( + GetAppListItemTypeString, + sync_pb::AppListSpecifics::AppListItemType_MIN, + sync_pb::AppListSpecifics::AppListItemType_MAX); +} + TEST_F(ProtoEnumConversionsTest, GetBrowserTypeString) { TestEnumStringFunction( GetBrowserTypeString, diff --git a/sync/protocol/proto_value_conversions.cc b/sync/protocol/proto_value_conversions.cc index cdb999e..5c8a7ab 100644 --- a/sync/protocol/proto_value_conversions.cc +++ b/sync/protocol/proto_value_conversions.cc @@ -14,6 +14,7 @@ #include "base/strings/string_number_conversions.h" #include "base/values.h" #include "sync/internal_api/public/base/unique_position.h" +#include "sync/protocol/app_list_specifics.pb.h" #include "sync/protocol/app_notification_specifics.pb.h" #include "sync/protocol/app_setting_specifics.pb.h" #include "sync/protocol/app_specifics.pb.h" @@ -347,6 +348,19 @@ base::DictionaryValue* CoalescedNotificationToValue( return value; } +base::DictionaryValue* AppListSpecificsToValue( + const sync_pb::AppListSpecifics& proto) { + base::DictionaryValue* value = new base::DictionaryValue(); + SET_STR(item_id); + SET_ENUM(item_type, GetAppListItemTypeString); + SET_STR(item_name); + SET_STR(parent_id); + SET_STR(page_ordinal); + SET_STR(item_ordinal); + + return value; +} + base::DictionaryValue* AppNotificationToValue( const sync_pb::AppNotification& proto) { base::DictionaryValue* value = new base::DictionaryValue(); @@ -572,6 +586,7 @@ base::DictionaryValue* NigoriSpecificsToValue( SET_BOOL(encrypt_search_engines); SET_BOOL(encrypt_dictionary); SET_BOOL(encrypt_articles); + SET_BOOL(encrypt_app_list); SET_BOOL(encrypt_everything); SET_BOOL(sync_tab_favicons); SET_ENUM(passphrase_type, PassphraseTypeString); @@ -694,6 +709,7 @@ base::DictionaryValue* EntitySpecificsToValue( const sync_pb::EntitySpecifics& specifics) { base::DictionaryValue* value = new base::DictionaryValue(); SET_FIELD(app, AppSpecificsToValue); + SET_FIELD(app_list, AppListSpecificsToValue); SET_FIELD(app_notification, AppNotificationToValue); SET_FIELD(app_setting, AppSettingSpecificsToValue); SET_FIELD(article, ArticleSpecificsToValue); diff --git a/sync/protocol/proto_value_conversions.h b/sync/protocol/proto_value_conversions.h index ee40e2b..9bf45e32 100644 --- a/sync/protocol/proto_value_conversions.h +++ b/sync/protocol/proto_value_conversions.h @@ -14,6 +14,7 @@ class DictionaryValue; } namespace sync_pb { +class AppListSpecifics; class AppNotification; class AppNotificationSettings; class AppSettingSpecifics; @@ -91,6 +92,10 @@ namespace syncer { SYNC_EXPORT_PRIVATE base::DictionaryValue* EncryptedDataToValue( const sync_pb::EncryptedData& encrypted_data); +// Sub-protocol of AppListSpecifics. +SYNC_EXPORT_PRIVATE base::DictionaryValue* AppListSpecificsToValue( + const sync_pb::AppListSpecifics& proto); + // Sub-protocol of AppSpecifics. SYNC_EXPORT_PRIVATE base::DictionaryValue* AppSettingsToValue( const sync_pb::AppNotificationSettings& app_notification_settings); diff --git a/sync/protocol/proto_value_conversions_unittest.cc b/sync/protocol/proto_value_conversions_unittest.cc index 86eee98..1366dd5 100644 --- a/sync/protocol/proto_value_conversions_unittest.cc +++ b/sync/protocol/proto_value_conversions_unittest.cc @@ -53,7 +53,7 @@ TEST_F(ProtoValueConversionsTest, ProtoChangeCheck) { // If this number changes, that means we added or removed a data // type. Don't forget to add a unit test for {New // type}SpecificsToValue below. - EXPECT_EQ(29, MODEL_TYPE_COUNT); + EXPECT_EQ(30, MODEL_TYPE_COUNT); // We'd also like to check if we changed any field in our messages. // However, that's hard to do: sizeof could work, but it's @@ -93,6 +93,10 @@ TEST_F(ProtoValueConversionsTest, PasswordSpecificsData) { EXPECT_EQ("<redacted>", password_value); } +TEST_F(ProtoValueConversionsTest, AppListSpecificsToValue) { + TestSpecificsToValue(AppListSpecificsToValue); +} + TEST_F(ProtoValueConversionsTest, AppNotificationToValue) { TestSpecificsToValue(AppNotificationToValue); } @@ -255,6 +259,7 @@ TEST_F(ProtoValueConversionsTest, EntitySpecificsToValue) { #define SET_FIELD(key) (void)specifics.mutable_##key() SET_FIELD(app); + SET_FIELD(app_list); SET_FIELD(app_notification); SET_FIELD(app_setting); SET_FIELD(article); diff --git a/sync/protocol/sync.proto b/sync/protocol/sync.proto index 5b0c175..ecaceef 100644 --- a/sync/protocol/sync.proto +++ b/sync/protocol/sync.proto @@ -14,6 +14,7 @@ option retain_unknown_fields = true; package sync_pb; +import "app_list_specifics.proto"; import "app_notification_specifics.proto"; import "app_setting_specifics.proto"; import "app_specifics.proto"; @@ -120,6 +121,7 @@ message EntitySpecifics { optional ManagedUserSettingSpecifics managed_user_setting = 186662; optional ManagedUserSpecifics managed_user = 194582; optional ArticleSpecifics article = 223759; + optional AppListSpecifics app_list = 229170; } message SyncEntity { diff --git a/sync/sync_proto.gypi b/sync/sync_proto.gypi index 4b90525..968ee9dd 100644 --- a/sync/sync_proto.gypi +++ b/sync/sync_proto.gypi @@ -13,6 +13,7 @@ 'protocol/app_notification_specifics.proto', 'protocol/app_setting_specifics.proto', 'protocol/app_specifics.proto', + 'protocol/app_list_specifics.proto', 'protocol/article_specifics.proto', 'protocol/autofill_specifics.proto', 'protocol/bookmark_specifics.proto', diff --git a/sync/syncable/model_type.cc b/sync/syncable/model_type.cc index aac057fd..fa33118 100644 --- a/sync/syncable/model_type.cc +++ b/sync/syncable/model_type.cc @@ -68,6 +68,9 @@ void AddDefaultFieldValue(ModelType datatype, case APPS: specifics->mutable_app(); break; + case APP_LIST: + specifics->mutable_app_list(); + break; case APP_SETTINGS: specifics->mutable_app_setting(); break; @@ -167,6 +170,9 @@ int GetSpecificsFieldNumberFromModelType(ModelType model_type) { case APPS: return sync_pb::EntitySpecifics::kAppFieldNumber; break; + case APP_LIST: + return sync_pb::EntitySpecifics::kAppListFieldNumber; + break; case APP_SETTINGS: return sync_pb::EntitySpecifics::kAppSettingFieldNumber; break; @@ -276,6 +282,9 @@ ModelType GetModelTypeFromSpecifics(const sync_pb::EntitySpecifics& specifics) { if (specifics.has_app()) return APPS; + if (specifics.has_app_list()) + return APP_LIST; + if (specifics.has_search_engine()) return SEARCH_ENGINES; @@ -461,6 +470,8 @@ const char* ModelTypeToString(ModelType model_type) { return "Sessions"; case APPS: return "Apps"; + case APP_LIST: + return "App List"; case AUTOFILL_PROFILE: return "Autofill Profiles"; case APP_SETTINGS: @@ -564,6 +575,8 @@ int ModelTypeToHistogramInt(ModelType model_type) { return 27; case ARTICLES: return 28; + case APP_LIST: + return 29; // Silence a compiler warning. case MODEL_TYPE_COUNT: return 0; @@ -623,6 +636,8 @@ ModelType ModelTypeFromString(const std::string& model_type_string) { return SESSIONS; else if (model_type_string == "Apps") return APPS; + else if (model_type_string == "App List") + return APP_LIST; else if (model_type_string == "App settings") return APP_SETTINGS; else if (model_type_string == "Extension settings") @@ -715,6 +730,8 @@ std::string ModelTypeToRootTag(ModelType type) { return "google_chrome_sessions"; case APPS: return "google_chrome_apps"; + case APP_LIST: + return "google_chrome_app_list"; case AUTOFILL_PROFILE: return "google_chrome_autofill_profiles"; case APP_SETTINGS: @@ -769,6 +786,7 @@ const char kExtensionSettingNotificationType[] = "EXTENSION_SETTING"; const char kNigoriNotificationType[] = "NIGORI"; const char kAppSettingNotificationType[] = "APP_SETTING"; const char kAppNotificationType[] = "APP"; +const char kAppListNotificationType[] = "APP_LIST"; const char kSearchEngineNotificationType[] = "SEARCH_ENGINE"; const char kSessionNotificationType[] = "SESSION"; const char kAutofillProfileNotificationType[] = "AUTOFILL_PROFILE"; @@ -820,6 +838,9 @@ bool RealModelTypeToNotificationType(ModelType model_type, case APPS: *notification_type = kAppNotificationType; return true; + case APP_LIST: + *notification_type = kAppListNotificationType; + return true; case SEARCH_ENGINES: *notification_type = kSearchEngineNotificationType; return true; @@ -904,6 +925,9 @@ bool NotificationTypeToRealModelType(const std::string& notification_type, } else if (notification_type == kAppNotificationType) { *model_type = APPS; return true; + } else if (notification_type == kAppListNotificationType) { + *model_type = APP_LIST; + return true; } else if (notification_type == kSearchEngineNotificationType) { *model_type = SEARCH_ENGINES; return true; diff --git a/sync/syncable/nigori_util.cc b/sync/syncable/nigori_util.cc index fbdd9a5..107a68f 100644 --- a/sync/syncable/nigori_util.cc +++ b/sync/syncable/nigori_util.cc @@ -242,7 +242,7 @@ void UpdateNigoriFromEncryptedTypes(ModelTypeSet encrypted_types, bool encrypt_everything, sync_pb::NigoriSpecifics* nigori) { nigori->set_encrypt_everything(encrypt_everything); - COMPILE_ASSERT(29 == MODEL_TYPE_COUNT, UpdateEncryptedTypes); + COMPILE_ASSERT(30 == MODEL_TYPE_COUNT, UpdateEncryptedTypes); nigori->set_encrypt_bookmarks( encrypted_types.Has(BOOKMARKS)); nigori->set_encrypt_preferences( @@ -269,6 +269,7 @@ void UpdateNigoriFromEncryptedTypes(ModelTypeSet encrypted_types, nigori->set_encrypt_favicon_images(encrypted_types.Has(FAVICON_IMAGES)); nigori->set_encrypt_favicon_tracking(encrypted_types.Has(FAVICON_TRACKING)); nigori->set_encrypt_articles(encrypted_types.Has(ARTICLES)); + nigori->set_encrypt_app_list(encrypted_types.Has(APP_LIST)); } ModelTypeSet GetEncryptedTypesFromNigori( @@ -277,7 +278,7 @@ ModelTypeSet GetEncryptedTypesFromNigori( return ModelTypeSet::All(); ModelTypeSet encrypted_types; - COMPILE_ASSERT(29 == MODEL_TYPE_COUNT, UpdateEncryptedTypes); + COMPILE_ASSERT(30 == MODEL_TYPE_COUNT, UpdateEncryptedTypes); if (nigori.encrypt_bookmarks()) encrypted_types.Put(BOOKMARKS); if (nigori.encrypt_preferences()) @@ -312,6 +313,8 @@ ModelTypeSet GetEncryptedTypesFromNigori( encrypted_types.Put(FAVICON_TRACKING); if (nigori.encrypt_articles()) encrypted_types.Put(ARTICLES); + if (nigori.encrypt_app_list()) + encrypted_types.Put(APP_LIST); return encrypted_types; } diff --git a/sync/tools/testserver/chromiumsync.py b/sync/tools/testserver/chromiumsync.py index 8b48113..496cb6a 100644 --- a/sync/tools/testserver/chromiumsync.py +++ b/sync/tools/testserver/chromiumsync.py @@ -23,6 +23,7 @@ import time import urlparse import uuid +import app_list_specifics_pb2 import app_notification_specifics_pb2 import app_setting_specifics_pb2 import app_specifics_pb2 @@ -60,6 +61,7 @@ import typed_url_specifics_pb2 ALL_TYPES = ( TOP_LEVEL, # The type of the 'Google Chrome' folder. APPS, + APP_LIST, APP_NOTIFICATION, APP_SETTINGS, ARTICLE, @@ -84,7 +86,7 @@ ALL_TYPES = ( TYPED_URL, EXTENSION_SETTINGS, FAVICON_IMAGES, - FAVICON_TRACKING) = range(27) + FAVICON_TRACKING) = range(28) # An enumeration on the frequency at which the server should send errors # to the client. This would be specified by the url that triggers the error. @@ -101,6 +103,7 @@ TOP_LEVEL_FOLDER_TAG = 'google_chrome' # to that datatype. Note that TOP_LEVEL has no such token. SYNC_TYPE_FIELDS = sync_pb2.EntitySpecifics.DESCRIPTOR.fields_by_name SYNC_TYPE_TO_DESCRIPTOR = { + APP_LIST: SYNC_TYPE_FIELDS['app_list'], APP_NOTIFICATION: SYNC_TYPE_FIELDS['app_notification'], APP_SETTINGS: SYNC_TYPE_FIELDS['app_setting'], APPS: SYNC_TYPE_FIELDS['app'], @@ -476,6 +479,8 @@ class SyncDataModel(object): _PERMANENT_ITEM_SPECS = [ PermanentItem('google_chrome_apps', name='Apps', parent_tag=ROOT_ID, sync_type=APPS), + PermanentItem('google_chrome_app_list', name='App List', + parent_tag=ROOT_ID, sync_type=APP_LIST), PermanentItem('google_chrome_app_notifications', name='App Notifications', parent_tag=ROOT_ID, sync_type=APP_NOTIFICATION), PermanentItem('google_chrome_app_settings', diff --git a/sync/util/data_type_histogram.h b/sync/util/data_type_histogram.h index 01a2ba0..e3a8d6f 100644 --- a/sync/util/data_type_histogram.h +++ b/sync/util/data_type_histogram.h @@ -72,6 +72,9 @@ case ::syncer::APPS: \ PER_DATA_TYPE_MACRO("Apps"); \ break; \ + case ::syncer::APP_LIST: \ + PER_DATA_TYPE_MACRO("AppList"); \ + break; \ case ::syncer::APP_SETTINGS: \ PER_DATA_TYPE_MACRO("AppSettings"); \ break; \ |