Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / app_list / app_list_syncable_service.cc
index c7d96bd..f97c94f 100644 (file)
@@ -7,7 +7,6 @@
 #include "base/command_line.h"
 #include "chrome/browser/chrome_notification_types.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"
@@ -16,6 +15,7 @@
 #include "chrome/common/chrome_switches.h"
 #include "content/public/browser/notification_source.h"
 #include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_system.h"
 #include "sync/api/sync_change_processor.h"
 #include "sync/api/sync_data.h"
 #include "sync/api/sync_merge_result.h"
@@ -23,6 +23,8 @@
 #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"
+#include "ui/app_list/app_list_model_observer.h"
+#include "ui/app_list/app_list_switches.h"
 
 using syncer::SyncChange;
 
@@ -32,7 +34,7 @@ namespace {
 
 bool SyncAppListEnabled() {
   return CommandLine::ForCurrentProcess()->HasSwitch(
-      switches::kEnableSyncAppList);
+      ::switches::kEnableSyncAppList);
 }
 
 void UpdateSyncItemFromSync(const sync_pb::AppListSpecifics& specifics,
@@ -51,6 +53,11 @@ bool UpdateSyncItemFromAppItem(const AppListItem* app_item,
                                AppListSyncableService::SyncItem* sync_item) {
   DCHECK_EQ(sync_item->item_id, app_item->id());
   bool changed = false;
+  if (app_list::switches::IsFolderUIEnabled() &&
+      sync_item->parent_id != app_item->folder_id()) {
+    sync_item->parent_id = app_item->folder_id();
+    changed = true;
+  }
   if (sync_item->item_name != app_item->title()) {
     sync_item->item_name = app_item->title();
     changed = true;
@@ -60,7 +67,7 @@ bool UpdateSyncItemFromAppItem(const AppListItem* app_item,
     sync_item->item_ordinal = app_item->position();
     changed = true;
   }
-  // TODO(stevenjb): Set parent_id and page_ordinal.
+  // TODO(stevenjb): Set page_ordinal.
   return changed;
 }
 
@@ -123,38 +130,41 @@ AppListSyncableService::SyncItem::SyncItem(
 AppListSyncableService::SyncItem::~SyncItem() {
 }
 
-// AppListSyncableService::ItemListObserver
+// AppListSyncableService::ModelObserver
 
-class AppListSyncableService::ItemListObserver
-    : public AppListItemListObserver {
+class AppListSyncableService::ModelObserver : public AppListModelObserver {
  public:
-  explicit ItemListObserver(AppListSyncableService* owner) : owner_(owner) {
-    owner_->model()->item_list()->AddObserver(this);
+  explicit ModelObserver(AppListSyncableService* owner)
+      : owner_(owner) {
+    DVLOG(2) << owner_ << ": ModelObserver Added";
+    owner_->model()->AddObserver(this);
   }
 
-  virtual ~ItemListObserver() {
-    owner_->model()->item_list()->RemoveObserver(this);
+  virtual ~ModelObserver() {
+    owner_->model()->RemoveObserver(this);
+    DVLOG(2) << owner_ << ": ModelObserver Removed";
   }
 
  private:
-  // AppListItemListObserver
-  virtual void OnListItemAdded(size_t index, AppListItem* item) OVERRIDE {
+  // AppListModelObserver
+  virtual void OnAppListItemAdded(AppListItem* item) OVERRIDE {
+    DVLOG(2) << owner_ << " OnAppListItemAdded: " << item->ToDebugString();
     owner_->AddOrUpdateFromSyncItem(item);
   }
 
-  virtual void OnListItemRemoved(size_t index, AppListItem* item) OVERRIDE {
+  virtual void OnAppListItemWillBeDeleted(AppListItem* item) OVERRIDE {
+    DVLOG(2) << owner_ << " OnAppListItemDeleted: " << item->ToDebugString();
     owner_->RemoveSyncItem(item->id());
   }
 
-  virtual void OnListItemMoved(size_t from_index,
-                               size_t to_index,
-                               AppListItem* item) OVERRIDE {
+  virtual void OnAppListItemUpdated(AppListItem* item) OVERRIDE {
+    DVLOG(2) << owner_ << " OnAppListItemUpdated: " << item->ToDebugString();
     owner_->UpdateSyncItem(item);
   }
 
   AppListSyncableService* owner_;
 
-  DISALLOW_COPY_AND_ASSIGN(ItemListObserver);
+  DISALLOW_COPY_AND_ASSIGN(ModelObserver);
 };
 
 // AppListSyncableService
@@ -165,15 +175,16 @@ AppListSyncableService::AppListSyncableService(
     : profile_(profile),
       extension_system_(extension_system),
       model_(new AppListModel) {
-  if (!extension_system || !extension_system->extension_service()) {
-    LOG(WARNING) << "AppListSyncableService created with no ExtensionService";
+  if (!extension_system) {
+    LOG(ERROR) << "AppListSyncableService created with no ExtensionSystem";
     return;
   }
 
-  if (SyncAppListEnabled())
-    item_list_observer_.reset(new ItemListObserver(this));
-
-  if (extension_system->extension_service()->is_ready()) {
+  // Note: model_observer_ is constructed after the initial sync changes are
+  // received in MergeDataAndStartSyncing(). Changes to the model before that
+  // will be synced after the initial sync occurs.
+  if (extension_system->extension_service() &&
+      extension_system->extension_service()->is_ready()) {
     BuildModel();
     return;
   }
@@ -185,7 +196,7 @@ AppListSyncableService::AppListSyncableService(
 
 AppListSyncableService::~AppListSyncableService() {
   // Remove observers.
-  item_list_observer_.reset();
+  model_observer_.reset();
 
   STLDeleteContainerPairSecondPointers(sync_items_.begin(), sync_items_.end());
 }
@@ -231,21 +242,17 @@ AppListSyncableService::GetSyncItem(const std::string& id) const {
   return NULL;
 }
 
-void AppListSyncableService::AddItem(AppListItem* app_item) {
-  SyncItem* sync_item = AddOrUpdateSyncItem(app_item);
+void AppListSyncableService::AddItem(scoped_ptr<AppListItem> app_item) {
+  SyncItem* sync_item = FindOrAddSyncItem(app_item.get());
   if (!sync_item)
     return;  // Item is not valid.
 
   DVLOG(1) << this << ": AddItem: " << sync_item->ToString();
-
-  // Add the item to the model if necessary.
-  if (!model_->item_list()->FindItem(app_item->id()))
-    model_->item_list()->AddItem(app_item);
-  else
-    model_->item_list()->SetItemPosition(app_item, sync_item->item_ordinal);
+  std::string folder_id = sync_item->parent_id;
+  model_->AddItemToFolder(app_item.Pass(), folder_id);
 }
 
-AppListSyncableService::SyncItem* AppListSyncableService::AddOrUpdateSyncItem(
+AppListSyncableService::SyncItem* AppListSyncableService::FindOrAddSyncItem(
     AppListItem* app_item) {
   const std::string& item_id = app_item->id();
   if (item_id.empty()) {
@@ -254,11 +261,10 @@ AppListSyncableService::SyncItem* AppListSyncableService::AddOrUpdateSyncItem(
   }
   SyncItem* sync_item = FindSyncItem(item_id);
   if (sync_item) {
-    // If there is an existing, non-REMOVE_DEFAULT entry, update it.
+    // If there is an existing, non-REMOVE_DEFAULT entry, return it.
     if (sync_item->item_type !=
         sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
       DVLOG(2) << this << ": AddItem already exists: " << sync_item->ToString();
-      UpdateSyncItem(app_item);
       return sync_item;
     }
 
@@ -343,7 +349,8 @@ void AppListSyncableService::UpdateSyncItem(AppListItem* app_item) {
 
 void AppListSyncableService::RemoveItem(const std::string& id) {
   RemoveSyncItem(id);
-  model_->item_list()->DeleteItem(id);
+  model_->DeleteItem(id);
+  PruneEmptySyncFolders();
 }
 
 void AppListSyncableService::RemoveSyncItem(const std::string& id) {
@@ -377,6 +384,41 @@ void AppListSyncableService::RemoveSyncItem(const std::string& id) {
   DeleteSyncItem(sync_item);
 }
 
+void AppListSyncableService::ResolveFolderPositions() {
+  if (!app_list::switches::IsFolderUIEnabled())
+    return;
+
+  for (SyncItemMap::iterator iter = sync_items_.begin();
+       iter != sync_items_.end(); ++iter) {
+    SyncItem* sync_item = iter->second;
+    if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
+      continue;
+    AppListItem* app_item = model_->FindItem(sync_item->item_id);
+    if (!app_item)
+      continue;
+    UpdateAppItemFromSyncItem(sync_item, app_item);
+  }
+}
+
+void AppListSyncableService::PruneEmptySyncFolders() {
+  if (!app_list::switches::IsFolderUIEnabled())
+    return;
+
+  std::set<std::string> parent_ids;
+  for (SyncItemMap::iterator iter = sync_items_.begin();
+       iter != sync_items_.end(); ++iter) {
+    parent_ids.insert(iter->second->parent_id);
+  }
+  for (SyncItemMap::iterator iter = sync_items_.begin();
+       iter != sync_items_.end(); ) {
+    SyncItem* sync_item = (iter++)->second;
+    if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
+      continue;
+    if (!ContainsKey(parent_ids, sync_item->item_id))
+      DeleteSyncItem(sync_item);
+  }
+}
+
 // AppListSyncableService syncer::SyncableService
 
 syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing(
@@ -435,6 +477,13 @@ syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing(
   }
   sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
 
+  // Adding items may have created folders without setting their positions
+  // since we haven't started observing the item list yet. Resolve those.
+  ResolveFolderPositions();
+
+  // Start observing app list model changes.
+  model_observer_.reset(new ModelObserver(this));
+
   return result;
 }
 
@@ -469,6 +518,9 @@ syncer::SyncError AppListSyncableService::ProcessSyncChanges(
                              syncer::APP_LIST);
   }
 
+  // Don't observe the model while processing incoming sync changes.
+  model_observer_.reset();
+
   DVLOG(1) << this << ": ProcessSyncChanges: " << change_list.size();
   for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
        iter != change_list.end(); ++iter) {
@@ -485,6 +537,10 @@ syncer::SyncError AppListSyncableService::ProcessSyncChanges(
       LOG(ERROR) << "Invalid sync change";
     }
   }
+
+  // Continue observing app list model changes.
+  model_observer_.reset(new ModelObserver(this));
+
   return syncer::SyncError();
 }
 
@@ -514,7 +570,7 @@ bool AppListSyncableService::ProcessSyncItemSpecifics(
       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);
+      model_->DeleteItem(item_id);
     }
     DVLOG(2) << this << " - ProcessSyncItem: Delete existing entry: "
              << sync_item->ToString();
@@ -545,8 +601,10 @@ void AppListSyncableService::ProcessNewSyncItem(SyncItem* sync_item) {
       return;
     }
     case sync_pb::AppListSpecifics::TYPE_FOLDER: {
-      // TODO(stevenjb): Implement
-      LOG(WARNING) << "TYPE_FOLDER not supported";
+      AppListItem* app_item = model_->FindItem(sync_item->item_id);
+      if (!app_item)
+        return;  // Don't create new folders here, the model will do that.
+      UpdateAppItemFromSyncItem(sync_item, app_item);
       return;
     }
     case sync_pb::AppListSpecifics::TYPE_URL: {
@@ -555,7 +613,7 @@ void AppListSyncableService::ProcessNewSyncItem(SyncItem* sync_item) {
       return;
     }
   }
-  NOTREACHED() << "Unrecoginized sync item type: " << sync_item->ToString();
+  NOTREACHED() << "Unrecognized sync item type: " << sync_item->ToString();
 }
 
 void AppListSyncableService::ProcessExistingSyncItem(SyncItem* sync_item) {
@@ -564,11 +622,18 @@ void AppListSyncableService::ProcessExistingSyncItem(SyncItem* sync_item) {
     return;
   }
   DVLOG(2) << "ProcessExistingSyncItem: " << sync_item->ToString();
-  AppListItem* app_item = model_->item_list()->FindItem(sync_item->item_id);
+  AppListItem* app_item = model_->FindItem(sync_item->item_id);
+  DVLOG(2) << " AppItem: " << app_item->ToDebugString();
   if (!app_item) {
     LOG(ERROR) << "Item not found in model: " << sync_item->ToString();
     return;
   }
+  // This is the only place where sync can cause an item to change folders.
+  if (app_list::switches::IsFolderUIEnabled() &&
+      app_item->folder_id() != sync_item->parent_id) {
+    DVLOG(2) << " Moving Item To Folder: " << sync_item->parent_id;
+    model_->MoveItemToFolder(app_item, sync_item->parent_id);
+  }
   UpdateAppItemFromSyncItem(sync_item, app_item);
 }
 
@@ -576,7 +641,7 @@ void AppListSyncableService::UpdateAppItemFromSyncItem(
     const AppListSyncableService::SyncItem* sync_item,
     AppListItem* app_item) {
   if (!app_item->position().Equals(sync_item->item_ordinal))
-    model_->item_list()->SetItemPosition(app_item, sync_item->item_ordinal);
+    model_->SetItemPosition(app_item, sync_item->item_ordinal);
 }
 
 bool AppListSyncableService::SyncStarted() {
@@ -642,8 +707,10 @@ void AppListSyncableService::DeleteSyncItemSpecifics(
   DVLOG(2) << this << " <- SYNC DELETE: " << iter->second->ToString();
   delete iter->second;
   sync_items_.erase(iter);
-  if (item_type != sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP)
-    model_->item_list()->DeleteItem(item_id);
+  // Only delete apps from the model. Folders will be deleted when all
+  // children have been deleted.
+  if (item_type == sync_pb::AppListSpecifics::TYPE_APP)
+    model_->DeleteItem(item_id);
 }
 
 std::string AppListSyncableService::SyncItem::ToString() const {
@@ -653,6 +720,8 @@ std::string AppListSyncableService::SyncItem::ToString() const {
   } else {
     res += " { " + item_name + " }";
     res += " [" + item_ordinal.ToDebugString() + "]";
+    if (!parent_id.empty())
+      res += " <" + parent_id.substr(0, 8) + ">";
   }
   return res;
 }