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 52ce18dcbf35bee9d8dc64b1631563e3597b2f19..3eff2bbb70f7faf5d29b23b58be87bcb2ae0cd69 100644 |
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc |
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc |
@@ -6,8 +6,8 @@ |
#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" |
#include "chrome/browser/ui/app_list/app_list_service.h" |
#include "chrome/browser/ui/app_list/extension_app_item.h" |
@@ -19,6 +19,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_model.h" |
#include "ui/app_list/app_list_model.h" |
@@ -44,6 +45,10 @@ 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_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 +80,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 |
@@ -95,8 +117,14 @@ AppListSyncableService::AppListSyncableService( |
Profile* profile, |
ExtensionService* extension_service) |
: profile_(profile), |
+ extension_service_(extension_service), |
model_(new AppListModel) { |
- if (extension_service && extension_service->is_ready()) { |
+ if (!extension_service) { |
+ LOG(WARNING) << "AppListSyncableService created with no ExtensionService"; |
+ return; |
+ } |
+ |
+ if (extension_service->is_ready()) { |
BuildModel(); |
return; |
} |
@@ -152,26 +180,66 @@ 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(AppListItemModel* item) { |
+ const std::string& item_id = item->id(); |
+ if (item_id.empty()) { |
+ LOG(ERROR) << "AppListItemModel 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 entry, uninstall the app instead. |
+ if (type == sync_pb::AppListSpecifics_AppListItemType_TYPE_APP && |
+ AppIsDefault(extension_service_, item_id)) { |
+ DVLOG(1) << this << ": AddItem: Uninstall: " << sync_item->ToString(); |
+ UninstallExtension(extension_service_, item_id); |
+ return; |
+ } |
+ // Otherwise, delete the REMOVE_DEFAULT entry. |
Nicolas Zea
2013/12/20 20:44:30
I'm not sure I understand the purpose of this. If
stevenjb
2013/12/20 21:05:56
I updated this comment and the comment above to cl
|
+ 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); |
Nicolas Zea
2013/12/20 20:44:30
should you return after this?
stevenjb
2013/12/20 21:05:56
No, now we add the app as usual. Comment added.
|
+ } |
+ |
+ 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_service_, item->id()); |
SendSyncChange(sync_item, SyncChange::ACTION_ADD); |
} |
-void AppListSyncableService::UpdateExtensionAppItem(ExtensionAppItem* item) { |
+void AppListSyncableService::UpdateItem(AppListItemModel* 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; |
@@ -182,9 +250,24 @@ 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; |
+ } |
+ |
+ // 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, |
@@ -194,7 +277,15 @@ void AppListSyncableService::RemoveItem(const std::string& id) { |
} |
delete sync_item; |
sync_items_.erase(iter); |
- model_->item_list()->DeleteItem(id); |
+ |
+ if (!AppIsDefault(extension_service_, id)) |
+ return; |
+ // This is a Default app; create and send a REMOVE_DEFAULT sync change. |
+ // Note: we add the REMOVE_DEFAULT entry after we delete the existing entry. |
+ sync_item = CreateSyncItem( |
+ id, sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP); |
+ DVLOG(1) << this << ": RemoveDefaultItem: " << sync_item->ToString(); |
+ SendSyncChange(sync_item, SyncChange::ACTION_ADD); |
} |
// AppListSyncableService syncer::SyncableService |
@@ -228,8 +319,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; |
@@ -266,7 +360,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) { |
@@ -286,14 +380,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 { |
@@ -305,36 +401,87 @@ 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_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_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) { |
+ AppListItemModel* 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() { |
@@ -348,26 +495,6 @@ bool AppListSyncableService::SyncStarted() { |
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); |
- 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) { |
@@ -394,47 +521,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 |
- AppListItemModel* 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( |
@@ -444,18 +538,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 |