| Index: chrome/browser/ui/app_list/app_list_syncable_service.cc
|
| 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 e3a5e065843e6d4323805ba671c38d088fe10afe..0d0baee923c7cab9a02d7b7195a4c972790fc35a 100644
|
| --- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
|
| +++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
|
| @@ -6,6 +6,7 @@
|
|
|
| #include "base/command_line.h"
|
| #include "chrome/browser/chrome_notification_types.h"
|
| +#include "chrome/browser/extensions/extension_prefs.h"
|
| #include "chrome/browser/extensions/extension_service.h"
|
| #include "chrome/browser/extensions/extension_system.h"
|
| #include "chrome/browser/profiles/profile.h"
|
| @@ -19,6 +20,7 @@
|
| #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_folder_item.h"
|
| #include "ui/app_list/app_list_item.h"
|
| #include "ui/app_list/app_list_model.h"
|
|
|
| @@ -44,6 +46,10 @@ bool UpdateSyncItemFromAppItem(const AppListItem* app_item,
|
| AppListSyncableService::SyncItem* sync_item) {
|
| DCHECK_EQ(sync_item->item_id, app_item->id());
|
| bool changed = false;
|
| + if (sync_item->item_name != app_item->title()) {
|
| + sync_item->item_name = app_item->title();
|
| + changed = true;
|
| + }
|
| if (!sync_item->item_ordinal.IsValid() ||
|
| !app_item->position().Equals(sync_item->item_ordinal)) {
|
| sync_item->item_ordinal = app_item->position();
|
| @@ -75,6 +81,23 @@ syncer::SyncData GetSyncDataFromSyncItem(
|
| specifics);
|
| }
|
|
|
| +bool AppIsDefault(ExtensionService* service, const std::string& id) {
|
| + return service && service->extension_prefs()->WasInstalledByDefault(id);
|
| +}
|
| +
|
| +bool AppIsPlatformApp(ExtensionService* service, const std::string& id) {
|
| + if (!service)
|
| + return false;
|
| + const extensions::Extension* app = service->GetInstalledExtension(id);
|
| + DVLOG_IF(1, !app) << "No App for ID: " << id;
|
| + return app ? app->is_platform_app() : false;
|
| +}
|
| +
|
| +void UninstallExtension(ExtensionService* service, const std::string& id) {
|
| + if (service && service->GetInstalledExtension(id))
|
| + service->UninstallExtension(id, false, NULL);
|
| +}
|
| +
|
| } // namespace
|
|
|
| // AppListSyncableService::SyncItem
|
| @@ -93,10 +116,16 @@ AppListSyncableService::SyncItem::~SyncItem() {
|
|
|
| AppListSyncableService::AppListSyncableService(
|
| Profile* profile,
|
| - ExtensionService* extension_service)
|
| + extensions::ExtensionSystem* extension_system)
|
| : profile_(profile),
|
| + extension_system_(extension_system),
|
| model_(new AppListModel) {
|
| - if (extension_service && extension_service->is_ready()) {
|
| + if (!extension_system || !extension_system->extension_service()) {
|
| + LOG(WARNING) << "AppListSyncableService created with no ExtensionService";
|
| + return;
|
| + }
|
| +
|
| + if (extension_system->extension_service()->is_ready()) {
|
| BuildModel();
|
| return;
|
| }
|
| @@ -153,26 +182,71 @@ AppListSyncableService::GetSyncItem(const std::string& id) const {
|
| return NULL;
|
| }
|
|
|
| -void AppListSyncableService::AddExtensionAppItem(ExtensionAppItem* item) {
|
| - SyncItem* sync_item = AddItem(
|
| - sync_pb::AppListSpecifics_AppListItemType_TYPE_APP, item);
|
| - if (!sync_item)
|
| - return; // Item already exists.
|
| - sync_item->item_name = item->extension_name();
|
| +void AppListSyncableService::AddItem(AppListItem* item) {
|
| + const std::string& item_id = item->id();
|
| + if (item_id.empty()) {
|
| + LOG(ERROR) << "AppListItem item with empty ID";
|
| + return;
|
| + }
|
| + sync_pb::AppListSpecifics::AppListItemType type;
|
| + const char* item_type = item->GetAppType();
|
| + if (item_type == ExtensionAppItem::kAppType) {
|
| + type = sync_pb::AppListSpecifics_AppListItemType_TYPE_APP;
|
| + } else if (item_type == AppListFolderItem::kAppType) {
|
| + type = sync_pb::AppListSpecifics_AppListItemType_TYPE_FOLDER;
|
| + } else {
|
| + LOG(ERROR) << "Unrecognized model type: " << item_type;
|
| + return;
|
| + }
|
| + SyncItem* sync_item = FindSyncItem(item_id);
|
| + if (sync_item) {
|
| + // If there is an existing, non-REMOVE_DEFAULT entry, update it.
|
| + if (sync_item->item_type !=
|
| + sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
|
| + DCHECK_EQ(sync_item->item_type, type);
|
| + DVLOG(2) << this << ": AddItem already exists: " << sync_item->ToString();
|
| + UpdateItem(item);
|
| + return;
|
| + }
|
| + // If there is an existing REMOVE_DEFAULT_APP entry, and the app is
|
| + // installed as a Default app, uninstall the app instead of adding it.
|
| + if (type == sync_pb::AppListSpecifics_AppListItemType_TYPE_APP &&
|
| + AppIsDefault(extension_system_->extension_service(), item_id)) {
|
| + DVLOG(1) << this << ": AddItem: Uninstall: " << sync_item->ToString();
|
| + UninstallExtension(extension_system_->extension_service(), item_id);
|
| + return;
|
| + }
|
| + // Otherwise, we are adding the app as a non-default app (i.e. an app that
|
| + // was installed by Default and removed is getting installed explicitly by
|
| + // the user), so delete the REMOVE_DEFAULT_APP.
|
| + if (SyncStarted()) {
|
| + DVLOG(2) << this << " -> SYNC DELETE: " << sync_item->ToString();
|
| + 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(item_id);
|
| + // Fall through. The REMOVE_DEFAULT_APP entry has been deleted, now an App
|
| + // entry can be added as usual.
|
| + }
|
| +
|
| + sync_item = CreateSyncItem(item_id, type);
|
| + UpdateSyncItemFromAppItem(item, sync_item);
|
| + model_->item_list()->AddItem(item);
|
| + DVLOG(1) << this << ": AddItem: " << sync_item->ToString() << " Default: "
|
| + << AppIsDefault(extension_system_->extension_service(), item->id());
|
| SendSyncChange(sync_item, SyncChange::ACTION_ADD);
|
| }
|
|
|
| -void AppListSyncableService::UpdateExtensionAppItem(ExtensionAppItem* item) {
|
| +void AppListSyncableService::UpdateItem(AppListItem* item) {
|
| SyncItem* sync_item = FindSyncItem(item->id());
|
| if (!sync_item) {
|
| - LOG(ERROR) << "UpdateExtensionAppItem: no sync item: " << item->id();
|
| + LOG(ERROR) << "UpdateItem: no sync item: " << item->id();
|
| return;
|
| }
|
| bool changed = UpdateSyncItemFromAppItem(item, sync_item);
|
| - if (sync_item->item_name != item->extension_name()) {
|
| - sync_item->item_name = item->extension_name();
|
| - changed = true;
|
| - }
|
| if (!changed) {
|
| DVLOG(2) << this << " - Update: SYNC NO CHANGE: " << sync_item->ToString();
|
| return;
|
| @@ -183,9 +257,34 @@ void AppListSyncableService::UpdateExtensionAppItem(ExtensionAppItem* item) {
|
| void AppListSyncableService::RemoveItem(const std::string& id) {
|
| DVLOG(2) << this << ": RemoveItem: " << id.substr(0, 8);
|
| SyncItemMap::iterator iter = sync_items_.find(id);
|
| - if (iter == sync_items_.end())
|
| + if (iter == sync_items_.end()) {
|
| + DVLOG(2) << this << " : No Sync Item.";
|
| return;
|
| + }
|
| + // Always delete the item from the model.
|
| + model_->item_list()->DeleteItem(id);
|
| +
|
| + // Check for existing RemoveDefault sync item.
|
| SyncItem* sync_item = iter->second;
|
| + if (sync_item->item_type ==
|
| + sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
|
| + // RemoveDefault item exists, just return.
|
| + DVLOG(2) << this << " : RemoveDefault Item exists.";
|
| + return;
|
| + }
|
| +
|
| + if (AppIsDefault(extension_system_->extension_service(), id)) {
|
| + // This is a Default app; update the entry to a REMOVE_DEFAULT entry. This
|
| + // will overwrite any existing entry for the item.
|
| + DVLOG(2) << this << " -> SYNC UPDATE: REMOVE_DEFAULT: "
|
| + << sync_item->item_id;
|
| + sync_item->item_type = sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP;
|
| + SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
|
| + return;
|
| + }
|
| +
|
| + // Existing entry is a normal entry, send a Delete sync change and remove
|
| + // the entry.
|
| if (SyncStarted()) {
|
| DVLOG(2) << this << " -> SYNC DELETE: " << sync_item->ToString();
|
| SyncChange sync_change(FROM_HERE, SyncChange::ACTION_DELETE,
|
| @@ -195,7 +294,6 @@ void AppListSyncableService::RemoveItem(const std::string& id) {
|
| }
|
| delete sync_item;
|
| sync_items_.erase(iter);
|
| - model_->item_list()->DeleteItem(id);
|
| }
|
|
|
| // AppListSyncableService syncer::SyncableService
|
| @@ -229,8 +327,11 @@ syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing(
|
| for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin();
|
| iter != initial_sync_data.end(); ++iter) {
|
| const syncer::SyncData& data = *iter;
|
| + DVLOG(2) << this << " Initial Sync Item: "
|
| + << data.GetSpecifics().app_list().item_id()
|
| + << " Type: " << data.GetSpecifics().app_list().item_type();
|
| DCHECK_EQ(syncer::APP_LIST, data.GetDataType());
|
| - if (CreateOrUpdateSyncItem(data.GetSpecifics().app_list()))
|
| + if (ProcessSyncItem(data.GetSpecifics().app_list()))
|
| ++new_items;
|
| else
|
| ++updated_items;
|
| @@ -267,7 +368,7 @@ syncer::SyncDataList AppListSyncableService::GetAllSyncData(
|
| syncer::ModelType type) const {
|
| DCHECK_EQ(syncer::APP_LIST, type);
|
|
|
| - DVLOG(1) << this << "GetAllSyncData: " << sync_items_.size();
|
| + DVLOG(1) << this << ": GetAllSyncData: " << sync_items_.size();
|
| syncer::SyncDataList list;
|
| for (SyncItemMap::const_iterator iter = sync_items_.begin();
|
| iter != sync_items_.end(); ++iter) {
|
| @@ -287,14 +388,16 @@ syncer::SyncError AppListSyncableService::ProcessSyncChanges(
|
| syncer::APP_LIST);
|
| }
|
|
|
| - // Process incoming changes first.
|
| DVLOG(1) << this << ": ProcessSyncChanges: " << change_list.size();
|
| for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
|
| iter != change_list.end(); ++iter) {
|
| const SyncChange& change = *iter;
|
| + DVLOG(2) << this << " Change: "
|
| + << change.sync_data().GetSpecifics().app_list().item_id()
|
| + << " (" << change.change_type() << ")";
|
| if (change.change_type() == SyncChange::ACTION_ADD ||
|
| change.change_type() == SyncChange::ACTION_UPDATE) {
|
| - CreateOrUpdateSyncItem(change.sync_data().GetSpecifics().app_list());
|
| + ProcessSyncItem(change.sync_data().GetSpecifics().app_list());
|
| } else if (change.change_type() == SyncChange::ACTION_DELETE) {
|
| DeleteSyncItem(change.sync_data().GetSpecifics().app_list());
|
| } else {
|
| @@ -306,36 +409,89 @@ syncer::SyncError AppListSyncableService::ProcessSyncChanges(
|
|
|
| // AppListSyncableService private
|
|
|
| -void AppListSyncableService::CreateAppItemFromSyncItem(SyncItem* sync_item) {
|
| - if (sync_item->item_type == sync_pb::AppListSpecifics::TYPE_APP) {
|
| - std::string extension_id = sync_item->item_id;
|
| - const ExtensionService* extension_service =
|
| - extensions::ExtensionSystem::Get(profile_)->extension_service();
|
| - const extensions::Extension* app =
|
| - extension_service->GetInstalledExtension(extension_id);
|
| - DVLOG_IF(1, !app) << this << "No App for ID: " << extension_id;
|
| - bool is_platform_app = app ? app->is_platform_app() : false;
|
| - ExtensionAppItem* app_item = new ExtensionAppItem(
|
| - profile_,
|
| - sync_item,
|
| - extension_id,
|
| - sync_item->item_name,
|
| - gfx::ImageSkia(),
|
| - is_platform_app);
|
| - model_->item_list()->AddItem(app_item);
|
| - return;
|
| +bool AppListSyncableService::ProcessSyncItem(
|
| + const sync_pb::AppListSpecifics& specifics) {
|
| + const std::string& item_id = specifics.item_id();
|
| + if (item_id.empty()) {
|
| + LOG(ERROR) << "AppList item with empty ID";
|
| + return false;
|
| }
|
| - if (sync_item->item_type ==
|
| - sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
|
| - // TODO(stevenjb): Implement
|
| + SyncItem* sync_item = FindSyncItem(item_id);
|
| + if (sync_item) {
|
| + // If an item of the same type exists, update it.
|
| + if (sync_item->item_type == specifics.item_type()) {
|
| + UpdateSyncItemFromSync(specifics, sync_item);
|
| + ProcessExistingSyncItem(sync_item);
|
| + DVLOG(2) << this << " <- SYNC UPDATE: " << sync_item->ToString();
|
| + return false;
|
| + }
|
| + // Otherwise, one of the entries should be TYPE_REMOVE_DEFAULT_APP.
|
| + if (sync_item->item_type !=
|
| + sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP &&
|
| + specifics.item_type() !=
|
| + sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
|
| + LOG(ERROR) << "Synced item type: " << specifics.item_type()
|
| + << " != existing sync item type: " << sync_item->item_type
|
| + << " Deleting item from model!";
|
| + model_->item_list()->DeleteItem(item_id);
|
| + }
|
| + DVLOG(2) << this << " - ProcessSyncItem: Delete existing entry: "
|
| + << sync_item->ToString();
|
| + delete sync_item;
|
| + sync_items_.erase(item_id);
|
| }
|
| - if (sync_item->item_type == sync_pb::AppListSpecifics::TYPE_FOLDER) {
|
| - // TODO(stevenjb): Implement
|
| +
|
| + sync_item = CreateSyncItem(item_id, specifics.item_type());
|
| + UpdateSyncItemFromSync(specifics, sync_item);
|
| + ProcessNewSyncItem(sync_item);
|
| + DVLOG(2) << this << " <- SYNC ADD: " << sync_item->ToString();
|
| + return true;
|
| +}
|
| +
|
| +void AppListSyncableService::ProcessNewSyncItem(SyncItem* sync_item) {
|
| + switch (sync_item->item_type) {
|
| + case sync_pb::AppListSpecifics::TYPE_APP: {
|
| + std::string extension_id = sync_item->item_id;
|
| + bool is_platform_app =
|
| + AppIsPlatformApp(extension_system_->extension_service(),
|
| + extension_id);
|
| + ExtensionAppItem* app_item = new ExtensionAppItem(
|
| + profile_,
|
| + sync_item,
|
| + extension_id,
|
| + sync_item->item_name,
|
| + gfx::ImageSkia(),
|
| + is_platform_app);
|
| + model_->item_list()->AddItem(app_item);
|
| + return;
|
| + }
|
| + case sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP: {
|
| + DVLOG(1) << this << ": Uninstall: " << sync_item->ToString();
|
| + UninstallExtension(extension_system_->extension_service(),
|
| + sync_item->item_id);
|
| + return;
|
| + }
|
| + case sync_pb::AppListSpecifics::TYPE_FOLDER: {
|
| + // TODO(stevenjb): Implement
|
| + LOG(WARNING) << "TYPE_FOLDER not supported";
|
| + return;
|
| + }
|
| + case sync_pb::AppListSpecifics::TYPE_URL: {
|
| + // TODO(stevenjb): Implement
|
| + LOG(WARNING) << "TYPE_URL not supported";
|
| + return;
|
| + }
|
| }
|
| - if (sync_item->item_type == sync_pb::AppListSpecifics::TYPE_URL) {
|
| - // TODO(stevenjb): Implement
|
| + NOTREACHED() << "Unrecoginized sync item type: " << sync_item->ToString();
|
| +}
|
| +
|
| +void AppListSyncableService::ProcessExistingSyncItem(SyncItem* sync_item) {
|
| + if (sync_item->item_type !=
|
| + sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
|
| + AppListItem* item = model_->item_list()->FindItem(sync_item->item_id);
|
| + if (item && !item->position().Equals(sync_item->item_ordinal))
|
| + model_->item_list()->SetItemPosition(item, sync_item->item_ordinal);
|
| }
|
| - LOG(ERROR) << "Unsupported type: " << sync_item->item_type;
|
| }
|
|
|
| bool AppListSyncableService::SyncStarted() {
|
| @@ -349,26 +505,6 @@ bool AppListSyncableService::SyncStarted() {
|
| return false;
|
| }
|
|
|
| -AppListSyncableService::SyncItem* AppListSyncableService::AddItem(
|
| - sync_pb::AppListSpecifics::AppListItemType type,
|
| - AppListItem* app_item) {
|
| - const std::string& item_id = app_item->id();
|
| - if (item_id.empty()) {
|
| - LOG(ERROR) << "AppListItem item with empty ID";
|
| - return NULL;
|
| - }
|
| - bool new_item = false;
|
| - SyncItem* sync_item = FindOrCreateSyncItem(item_id, type, &new_item);
|
| - if (!new_item) {
|
| - DVLOG(2) << this << ": AddItem already exists: " << sync_item->ToString();
|
| - return NULL; // Item already exists.
|
| - }
|
| - UpdateSyncItemFromAppItem(app_item, sync_item);
|
| - DVLOG(1) << this << ": AddItem: " << sync_item->ToString();
|
| - model_->item_list()->AddItem(app_item);
|
| - return sync_item;
|
| -}
|
| -
|
| void AppListSyncableService::SendSyncChange(
|
| SyncItem* sync_item,
|
| SyncChange::SyncChangeType sync_change_type) {
|
| @@ -395,47 +531,14 @@ AppListSyncableService::FindSyncItem(const std::string& item_id) {
|
| return iter->second;
|
| }
|
|
|
| -AppListSyncableService::SyncItem* AppListSyncableService::FindOrCreateSyncItem(
|
| +AppListSyncableService::SyncItem*
|
| +AppListSyncableService::CreateSyncItem(
|
| 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);
|
| - DVLOG(2) << this << "CreateOrUpdateSyncItem: " << sync_item->ToString()
|
| - << " New: " << new_item << " Pos: " << specifics.item_ordinal();
|
| - UpdateSyncItemFromSync(specifics, sync_item);
|
| - // Update existing item in model
|
| - AppListItem* item = model_->item_list()->FindItem(sync_item->item_id);
|
| - if (item && !item->position().Equals(sync_item->item_ordinal))
|
| - model_->item_list()->SetItemPosition(item, sync_item->item_ordinal);
|
| - if (new_item) {
|
| - CreateAppItemFromSyncItem(sync_item);
|
| - DVLOG(2) << this << " <- SYNC ADD: " << sync_item->ToString();
|
| - } else {
|
| - DVLOG(2) << this << " <- SYNC UPDATE: " << sync_item->ToString();
|
| - }
|
| - return new_item;
|
| + sync_pb::AppListSpecifics::AppListItemType item_type) {
|
| + DCHECK(!ContainsKey(sync_items_, item_id));
|
| + SyncItem* sync_item = new SyncItem(item_id, item_type);
|
| + sync_items_[item_id] = sync_item;
|
| + return sync_item;
|
| }
|
|
|
| void AppListSyncableService::DeleteSyncItem(
|
| @@ -445,18 +548,28 @@ void AppListSyncableService::DeleteSyncItem(
|
| LOG(ERROR) << "Delete AppList item with empty ID";
|
| return;
|
| }
|
| - DVLOG(2) << this << "DeleteSyncItem: " << item_id.substr(0, 8);
|
| + DVLOG(2) << this << ": DeleteSyncItem: " << item_id.substr(0, 8);
|
| SyncItemMap::iterator iter = sync_items_.find(item_id);
|
| if (iter == sync_items_.end())
|
| return;
|
| + sync_pb::AppListSpecifics::AppListItemType item_type =
|
| + iter->second->item_type;
|
| DVLOG(2) << this << " <- SYNC DELETE: " << iter->second->ToString();
|
| delete iter->second;
|
| sync_items_.erase(iter);
|
| - model_->item_list()->DeleteItem(item_id);
|
| + if (item_type != sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP)
|
| + model_->item_list()->DeleteItem(item_id);
|
| }
|
|
|
| std::string AppListSyncableService::SyncItem::ToString() const {
|
| - return item_id.substr(0, 8) + " [" + item_ordinal.ToDebugString() + "]";
|
| + std::string res = item_id.substr(0, 8);
|
| + if (item_type == sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
|
| + res += " { RemoveDefault }";
|
| + } else {
|
| + res += " { " + item_name + " }";
|
| + res += " [" + item_ordinal.ToDebugString() + "]";
|
| + }
|
| + return res;
|
| }
|
|
|
| } // namespace app_list
|
|
|