app_list_syncable_service.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// found in the LICENSE file.
4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/app_list/app_list_syncable_service.h"
6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
75d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/command_line.h"
86d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/apps/drive/drive_app_provider.h"
9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/chrome_notification_types.h"
10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/app_list/app_list_service.h"
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/ui/app_list/extension_app_item.h"
14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/app_list/extension_app_model_builder.h"
15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "chrome/browser/ui/host_desktop.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/common/chrome_switches.h"
17116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chrome/common/extensions/extension_constants.h"
18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/public/browser/notification_source.h"
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_prefs.h"
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/browser/uninstall_reason.h"
2223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "grit/generated_resources.h"
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "sync/api/sync_change_processor.h"
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "sync/api/sync_data.h"
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "sync/api/sync_merge_result.h"
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "sync/protocol/sync.pb.h"
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/app_list/app_list_folder_item.h"
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/app_list/app_list_item.h"
29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/app_list/app_list_model.h"
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/app_list/app_list_model_observer.h"
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/app_list/app_list_switches.h"
3223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
34116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if defined(OS_CHROMEOS)
35116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chrome/browser/chromeos/file_manager/app_id.h"
36116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chrome/browser/chromeos/genius_app/app_id.h"
37116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
38116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using syncer::SyncChange;
40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace app_list {
42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace {
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)const char kOemFolderId[] = "ddb1da55-d478-4243-8642-56d3041f0263";
4623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UpdateSyncItemFromSync(const sync_pb::AppListSpecifics& specifics,
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            AppListSyncableService::SyncItem* item) {
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_EQ(item->item_id, specifics.item_id());
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  item->item_type = specifics.item_type();
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  item->item_name = specifics.item_name();
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  item->parent_id = specifics.parent_id();
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!specifics.page_ordinal().empty())
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    item->page_ordinal = syncer::StringOrdinal(specifics.page_ordinal());
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!specifics.item_ordinal().empty())
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    item->item_ordinal = syncer::StringOrdinal(specifics.item_ordinal());
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool UpdateSyncItemFromAppItem(const AppListItem* app_item,
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                               AppListSyncableService::SyncItem* sync_item) {
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_EQ(sync_item->item_id, app_item->id());
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  bool changed = false;
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (app_list::switches::IsFolderUIEnabled() &&
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      sync_item->parent_id != app_item->folder_id()) {
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_item->parent_id = app_item->folder_id();
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    changed = true;
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (sync_item->item_name != app_item->name()) {
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    sync_item->item_name = app_item->name();
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    changed = true;
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!sync_item->item_ordinal.IsValid() ||
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      !app_item->position().Equals(sync_item->item_ordinal)) {
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_item->item_ordinal = app_item->position();
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    changed = true;
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // TODO(stevenjb): Set page_ordinal.
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return changed;
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void GetSyncSpecificsFromSyncItem(const AppListSyncableService::SyncItem* item,
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  sync_pb::AppListSpecifics* specifics) {
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(specifics);
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  specifics->set_item_id(item->item_id);
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  specifics->set_item_type(item->item_type);
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  specifics->set_item_name(item->item_name);
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  specifics->set_parent_id(item->parent_id);
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item->page_ordinal.IsValid())
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    specifics->set_page_ordinal(item->page_ordinal.ToInternalValue());
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item->item_ordinal.IsValid())
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    specifics->set_item_ordinal(item->item_ordinal.ToInternalValue());
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)syncer::SyncData GetSyncDataFromSyncItem(
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const AppListSyncableService::SyncItem* item) {
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_pb::EntitySpecifics specifics;
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  GetSyncSpecificsFromSyncItem(item, specifics.mutable_app_list());
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return syncer::SyncData::CreateLocalData(item->item_id,
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                           item->item_id,
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                           specifics);
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool AppIsDefault(ExtensionService* service, const std::string& id) {
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return service && extensions::ExtensionPrefs::Get(service->profile())
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                        ->WasInstalledByDefault(id);
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
108116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbool IsUnRemovableDefaultApp(const std::string& id) {
109116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (id == extension_misc::kChromeAppId ||
110116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      id == extension_misc::kWebStoreAppId)
111116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return true;
112116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if defined(OS_CHROMEOS)
113116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (id == file_manager::kFileManagerAppId || id == genius_app::kGeniusAppId)
114116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return true;
115116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
116116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return false;
117116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
118116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UninstallExtension(ExtensionService* service, const std::string& id) {
1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (service && service->GetInstalledExtension(id)) {
1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    service->UninstallExtension(id,
1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                extensions::UNINSTALL_REASON_SYNC,
1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                base::Bind(&base::DoNothing),
1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                NULL);
1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool GetAppListItemType(AppListItem* item,
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sync_pb::AppListSpecifics::AppListItemType* type) {
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const char* item_type = item->GetItemType();
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_type == ExtensionAppItem::kItemType) {
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *type = sync_pb::AppListSpecifics::TYPE_APP;
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (item_type == AppListFolderItem::kItemType) {
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *type = sync_pb::AppListSpecifics::TYPE_FOLDER;
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "Unrecognized model type: " << item_type;
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return true;
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// AppListSyncableService::SyncItem
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem::SyncItem(
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& id,
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_pb::AppListSpecifics::AppListItemType type)
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : item_id(id),
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      item_type(type) {
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem::~SyncItem() {
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// AppListSyncableService::ModelObserver
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class AppListSyncableService::ModelObserver : public AppListModelObserver {
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public:
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  explicit ModelObserver(AppListSyncableService* owner)
161c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      : owner_(owner),
162c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        adding_item_(NULL) {
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << owner_ << ": ModelObserver Added";
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    owner_->model()->AddObserver(this);
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual ~ModelObserver() {
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    owner_->model()->RemoveObserver(this);
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << owner_ << ": ModelObserver Removed";
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private:
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // AppListModelObserver
1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual void OnAppListItemAdded(AppListItem* item) OVERRIDE {
175c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    DCHECK(!adding_item_);
176c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    adding_item_ = item;  // Ignore updates while adding an item.
177c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << owner_ << " OnAppListItemAdded: " << item->ToDebugString();
1785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    owner_->AddOrUpdateFromSyncItem(item);
179c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    adding_item_ = NULL;
1805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual void OnAppListItemWillBeDeleted(AppListItem* item) OVERRIDE {
183c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    DCHECK(!adding_item_);
184c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << owner_ << " OnAppListItemDeleted: " << item->ToDebugString();
185c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    // Don't sync folder removal in case the folder still exists on another
186c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    // device (e.g. with device specific items in it). Empty folders will be
187c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    // deleted when the last item is removed (in PruneEmptySyncFolders()).
188c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    if (item->GetItemType() == AppListFolderItem::kItemType)
189c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return;
1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    owner_->RemoveSyncItem(item->id());
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual void OnAppListItemUpdated(AppListItem* item) OVERRIDE {
194c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    if (adding_item_) {
195c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // Adding an item may trigger update notifications which should be
196c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // ignored.
197c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      DCHECK_EQ(adding_item_, item);
198c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return;
199c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    }
200c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << owner_ << " OnAppListItemUpdated: " << item->ToDebugString();
2015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    owner_->UpdateSyncItem(item);
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  AppListSyncableService* owner_;
205c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  AppListItem* adding_item_;  // Unowned pointer to item being added.
2065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ModelObserver);
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// AppListSyncableService
2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
212f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)AppListSyncableService::AppListSyncableService(
213f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    Profile* profile,
2145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    extensions::ExtensionSystem* extension_system)
215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : profile_(profile),
2165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      extension_system_(extension_system),
217116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      model_(new AppListModel),
218116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      first_app_list_sync_(true) {
2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!extension_system) {
2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "AppListSyncableService created with no ExtensionSystem";
2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
22423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  oem_folder_name_ =
22523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      l10n_util::GetStringUTF8(IDS_APP_LIST_OEM_DEFAULT_FOLDER_NAME);
22623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
2275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Note: model_observer_ is constructed after the initial sync changes are
2285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // received in MergeDataAndStartSyncing(). Changes to the model before that
2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // will be synced after the initial sync occurs.
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (extension_system->extension_service() &&
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      extension_system->extension_service()->is_ready()) {
232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    BuildModel();
233f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
234f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
235f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
236f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // The extensions for this profile have not yet all been loaded.
2375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  registrar_.Add(this,
2385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
239f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                 content::Source<Profile>(profile));
240f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
241f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
242f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)AppListSyncableService::~AppListSyncableService() {
2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Remove observers.
2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_observer_.reset();
2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  STLDeleteContainerPairSecondPointers(sync_items_.begin(), sync_items_.end());
247f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
248f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
249f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void AppListSyncableService::BuildModel() {
250f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // For now, use the AppListControllerDelegate associated with the native
251f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // desktop. TODO(stevenjb): Remove ExtensionAppModelBuilder controller
252f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // dependency and move the dependent methods from AppListControllerDelegate
253f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // to an extension service delegate associated with this class.
254f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  AppListControllerDelegate* controller = NULL;
255f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  AppListService* service =
256f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      AppListService::Get(chrome::HOST_DESKTOP_TYPE_NATIVE);
257f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (service)
258f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    controller = service->GetControllerDelegate();
2595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  apps_builder_.reset(new ExtensionAppModelBuilder(controller));
260f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(profile_);
261a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  if (app_list::switches::IsAppListSyncEnabled()) {
262c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(1) << this << ": AppListSyncableService: InitializeWithService.";
2635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncStarted();
2645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    apps_builder_->InitializeWithService(this);
2655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
266c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(1) << this << ": AppListSyncableService: InitializeWithProfile.";
2675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    apps_builder_->InitializeWithProfile(profile_, model_.get());
2685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2696d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2706d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (app_list::switches::IsDriveAppsInAppListEnabled())
2716d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    drive_app_provider_.reset(new DriveAppProvider(profile_));
2726d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
2736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2746d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void AppListSyncableService::Shutdown() {
2756d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // DriveAppProvider touches other KeyedServices in its dtor and needs be
2766d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // released in shutdown stage.
2776d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  drive_app_provider_.reset();
278f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
279f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
280f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void AppListSyncableService::Observe(
281f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    int type,
282f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const content::NotificationSource& source,
283f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const content::NotificationDetails& details) {
2845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK_EQ(extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, type);
285f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
286f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  registrar_.RemoveAll();
287f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  BuildModel();
288f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
289f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
2905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const AppListSyncableService::SyncItem*
2915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::GetSyncItem(const std::string& id) const {
2925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItemMap::const_iterator iter = sync_items_.find(id);
2935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (iter != sync_items_.end())
2945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return iter->second;
2955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return NULL;
2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
29823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void AppListSyncableService::SetOemFolderName(const std::string& name) {
29923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  oem_folder_name_ = name;
30023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
30123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (oem_folder)
30223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    model_->SetItemName(oem_folder, oem_folder_name_);
30323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
30423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
3055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::AddItem(scoped_ptr<AppListItem> app_item) {
3065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = FindOrAddSyncItem(app_item.get());
3075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!sync_item)
3085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;  // Item is not valid.
3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
310effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  std::string folder_id;
311e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (app_list::switches::IsFolderUIEnabled()) {
312e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    if (AppIsOem(app_item->id())) {
3135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      folder_id = FindOrCreateOemFolder();
314116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      VLOG_IF(2, !folder_id.empty())
315116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << this << ": AddItem to OEM folder: " << sync_item->ToString();
316e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    } else {
317e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      folder_id = sync_item->parent_id;
318e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    }
31923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  }
320c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << ": AddItem: " << sync_item->ToString()
321116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << " Folder: '" << folder_id << "'";
3225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_->AddItemToFolder(app_item.Pass(), folder_id);
3235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem* AppListSyncableService::FindOrAddSyncItem(
3265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    AppListItem* app_item) {
3275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const std::string& item_id = app_item->id();
3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_id.empty()) {
3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "AppListItem item with empty ID";
3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return NULL;
3315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = FindSyncItem(item_id);
3335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_item) {
3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // If there is an existing, non-REMOVE_DEFAULT entry, return it.
3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (sync_item->item_type !=
3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DVLOG(2) << this << ": AddItem already exists: " << sync_item->ToString();
3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return sync_item;
3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (RemoveDefaultApp(app_item, sync_item))
3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return NULL;
3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Fall through. The REMOVE_DEFAULT_APP entry has been deleted, now a new
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // App entry can be added.
3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return CreateSyncItemFromAppItem(app_item);
3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem*
3525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::CreateSyncItemFromAppItem(AppListItem* app_item) {
3535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_pb::AppListSpecifics::AppListItemType type;
3545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!GetAppListItemType(app_item, &type))
3555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return NULL;
356c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << " CreateSyncItemFromAppItem:" << app_item->ToDebugString();
3575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = CreateSyncItem(app_item->id(), type);
3585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UpdateSyncItemFromAppItem(app_item, sync_item);
3595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SendSyncChange(sync_item, SyncChange::ACTION_ADD);
3605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return sync_item;
3615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::AddOrUpdateFromSyncItem(AppListItem* app_item) {
3645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Do not create a sync item for the OEM folder here, do that in
3655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // ResolveFolderPositions once the position has been resolved.
3665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (app_item->id() == kOemFolderId)
3675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return;
3685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = FindSyncItem(app_item->id());
3705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_item) {
3715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    UpdateAppItemFromSyncItem(sync_item, app_item);
3725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
3735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CreateSyncItemFromAppItem(app_item);
3755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool AppListSyncableService::RemoveDefaultApp(AppListItem* item,
3785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                              SyncItem* sync_item) {
3795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CHECK_EQ(sync_item->item_type,
3805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)           sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP);
3815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // If there is an existing REMOVE_DEFAULT_APP entry, and the app is
3835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // installed as a Default app, uninstall the app instead of adding it.
3845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_item->item_type == sync_pb::AppListSpecifics::TYPE_APP &&
3855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      AppIsDefault(extension_system_->extension_service(), item->id())) {
386c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << ": HandleDefaultApp: Uninstall: "
387c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            << sync_item->ToString();
3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    UninstallExtension(extension_system_->extension_service(), item->id());
3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return true;
3905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Otherwise, we are adding the app as a non-default app (i.e. an app that
3935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // was installed by Default and removed is getting installed explicitly by
3945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // the user), so delete the REMOVE_DEFAULT_APP.
3955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DeleteSyncItem(sync_item);
3965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return false;
3975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::DeleteSyncItem(SyncItem* sync_item) {
4005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (SyncStarted()) {
401c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC DELETE: " << sync_item->ToString();
4025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncChange sync_change(FROM_HERE, SyncChange::ACTION_DELETE,
4035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                           GetSyncDataFromSyncItem(sync_item));
4045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_processor_->ProcessSyncChanges(
4055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        FROM_HERE, syncer::SyncChangeList(1, sync_change));
4065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string item_id = sync_item->item_id;
4085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  delete sync_item;
4095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_items_.erase(item_id);
4105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::UpdateSyncItem(AppListItem* app_item) {
4135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = FindSyncItem(app_item->id());
4145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!sync_item) {
4155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "UpdateItem: no sync item: " << app_item->id();
4165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  bool changed = UpdateSyncItemFromAppItem(app_item, sync_item);
4195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!changed) {
4205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << this << " - Update: SYNC NO CHANGE: " << sync_item->ToString();
4215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
4245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::RemoveItem(const std::string& id) {
4275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  RemoveSyncItem(id);
4285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_->DeleteItem(id);
4295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PruneEmptySyncFolders();
4305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
43223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void AppListSyncableService::UpdateItem(AppListItem* app_item) {
43323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  // Check to see if the item needs to be moved to/from the OEM folder.
434e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (!app_list::switches::IsFolderUIEnabled())
435e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    return;
436effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  bool is_oem = AppIsOem(app_item->id());
43723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (!is_oem && app_item->folder_id() == kOemFolderId)
43823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    model_->MoveItemToFolder(app_item, "");
43923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  else if (is_oem && app_item->folder_id() != kOemFolderId)
44023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    model_->MoveItemToFolder(app_item, kOemFolderId);
44123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
44223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
4435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::RemoveSyncItem(const std::string& id) {
444c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << ": RemoveSyncItem: " << id.substr(0, 8);
4455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItemMap::iterator iter = sync_items_.find(id);
4465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (iter == sync_items_.end()) {
4475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << this << " : RemoveSyncItem: No Item.";
4485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Check for existing RemoveDefault sync item.
4525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = iter->second;
4535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_pb::AppListSpecifics::AppListItemType type = sync_item->item_type;
4545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (type == sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
4555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // RemoveDefault item exists, just return.
4565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << this << " : RemoveDefault Item exists.";
4575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (type == sync_pb::AppListSpecifics::TYPE_APP &&
4615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      AppIsDefault(extension_system_->extension_service(), id)) {
4625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // This is a Default app; update the entry to a REMOVE_DEFAULT entry. This
4635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // will overwrite any existing entry for the item.
464c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC UPDATE: REMOVE_DEFAULT: "
465c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            << sync_item->item_id;
4665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_item->item_type = sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP;
4675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
4685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DeleteSyncItem(sync_item);
4725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
474116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid AppListSyncableService::ResolveFolderPositions() {
4755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!app_list::switches::IsFolderUIEnabled())
4765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  VLOG(1) << "ResolveFolderPositions.";
4795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (SyncItemMap::iterator iter = sync_items_.begin();
4805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != sync_items_.end(); ++iter) {
4815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncItem* sync_item = iter->second;
4825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
4835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
4845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    AppListItem* app_item = model_->FindItem(sync_item->item_id);
4855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!app_item)
4865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
4875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    UpdateAppItemFromSyncItem(sync_item, app_item);
4885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
489116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
4905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Move the OEM folder if one exists and we have not synced its position.
4915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
4925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (oem_folder && !FindSyncItem(kOemFolderId)) {
4935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    model_->SetItemPosition(oem_folder, GetOemFolderPos());
4945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    VLOG(1) << "Creating new OEM folder sync item: "
4955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            << oem_folder->position().ToDebugString();
4965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    CreateSyncItemFromAppItem(oem_folder);
497116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
4985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::PruneEmptySyncFolders() {
5015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!app_list::switches::IsFolderUIEnabled())
5025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
5035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::set<std::string> parent_ids;
5055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (SyncItemMap::iterator iter = sync_items_.begin();
5065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != sync_items_.end(); ++iter) {
5075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    parent_ids.insert(iter->second->parent_id);
5085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (SyncItemMap::iterator iter = sync_items_.begin();
5105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != sync_items_.end(); ) {
5115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncItem* sync_item = (iter++)->second;
5125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
5135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
5145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!ContainsKey(parent_ids, sync_item->item_id))
5155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DeleteSyncItem(sync_item);
5165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
5185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// AppListSyncableService syncer::SyncableService
5205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing(
5225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    syncer::ModelType type,
5235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const syncer::SyncDataList& initial_sync_data,
5245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
5255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<syncer::SyncErrorFactory> error_handler) {
5265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(!sync_processor_.get());
5275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(sync_processor.get());
5285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(error_handler.get());
5295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_processor_ = sync_processor.Pass();
5315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_error_handler_ = error_handler.Pass();
53246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (switches::IsFolderUIEnabled())
53346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    model_->SetFoldersEnabled(true);
5345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  syncer::SyncMergeResult result = syncer::SyncMergeResult(type);
5365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  result.set_num_items_before_association(sync_items_.size());
537c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(1) << this << ": MergeDataAndStartSyncing: "
538c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          << initial_sync_data.size();
5395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Copy all sync items to |unsynced_items|.
5415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::set<std::string> unsynced_items;
5425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (SyncItemMap::const_iterator iter = sync_items_.begin();
5435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != sync_items_.end(); ++iter) {
5445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    unsynced_items.insert(iter->first);
5455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Create SyncItem entries for initial_sync_data.
5485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  size_t new_items = 0, updated_items = 0;
5495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin();
5505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != initial_sync_data.end(); ++iter) {
5515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const syncer::SyncData& data = *iter;
552e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    const std::string& item_id = data.GetSpecifics().app_list().item_id();
553116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const sync_pb::AppListSpecifics& specifics = data.GetSpecifics().app_list();
554e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    DVLOG(2) << this << "  Initial Sync Item: " << item_id
555116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch             << " Type: " << specifics.item_type();
5565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DCHECK_EQ(syncer::APP_LIST, data.GetDataType());
557116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (ProcessSyncItemSpecifics(specifics))
5585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ++new_items;
5595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    else
5605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ++updated_items;
561116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (specifics.item_type() != sync_pb::AppListSpecifics::TYPE_FOLDER &&
562116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        !IsUnRemovableDefaultApp(item_id) &&
563116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        !AppIsOem(item_id) &&
564116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        !AppIsDefault(extension_system_->extension_service(), item_id)) {
565116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      VLOG(2) << "Syncing non-default item: " << item_id;
566116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      first_app_list_sync_ = false;
567116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
568e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    unsynced_items.erase(item_id);
5695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  result.set_num_items_after_association(sync_items_.size());
5725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  result.set_num_items_added(new_items);
5735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  result.set_num_items_deleted(0);
5745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  result.set_num_items_modified(updated_items);
5755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Send unsynced items. Does not affect |result|.
5775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  syncer::SyncChangeList change_list;
5785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (std::set<std::string>::iterator iter = unsynced_items.begin();
5795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != unsynced_items.end(); ++iter) {
5805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncItem* sync_item = FindSyncItem(*iter);
581116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Sync can cause an item to change folders, causing an unsynced folder
582116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // item to be removed.
583116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (!sync_item)
584116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      continue;
585c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
5865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    change_list.push_back(SyncChange(FROM_HERE,  SyncChange::ACTION_ADD,
5875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                     GetSyncDataFromSyncItem(sync_item)));
5885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
5905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Adding items may have created folders without setting their positions
5925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // since we haven't started observing the item list yet. Resolve those.
593116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  ResolveFolderPositions();
5945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Start observing app list model changes.
5965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_observer_.reset(new ModelObserver(this));
5975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return result;
5995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
6005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::StopSyncing(syncer::ModelType type) {
6025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_EQ(type, syncer::APP_LIST);
6035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_processor_.reset();
6055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_error_handler_.reset();
60646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  model_->SetFoldersEnabled(false);
6075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
6085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)syncer::SyncDataList AppListSyncableService::GetAllSyncData(
6105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    syncer::ModelType type) const {
6115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_EQ(syncer::APP_LIST, type);
6125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
613c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(1) << this << ": GetAllSyncData: " << sync_items_.size();
6145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  syncer::SyncDataList list;
6155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (SyncItemMap::const_iterator iter = sync_items_.begin();
6165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != sync_items_.end(); ++iter) {
617c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC: " << iter->second->ToString();
6185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    list.push_back(GetSyncDataFromSyncItem(iter->second));
6195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return list;
6215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
6225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)syncer::SyncError AppListSyncableService::ProcessSyncChanges(
6245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const tracked_objects::Location& from_here,
6255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const syncer::SyncChangeList& change_list) {
6265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!sync_processor_.get()) {
6275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return syncer::SyncError(FROM_HERE,
6285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             syncer::SyncError::DATATYPE_ERROR,
6295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             "App List syncable service is not started.",
6305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             syncer::APP_LIST);
6315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Don't observe the model while processing incoming sync changes.
6345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_observer_.reset();
6355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
636c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(1) << this << ": ProcessSyncChanges: " << change_list.size();
6375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
6385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != change_list.end(); ++iter) {
6395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const SyncChange& change = *iter;
640c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << "  Change: "
641c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            << change.sync_data().GetSpecifics().app_list().item_id()
642c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            << " (" << change.change_type() << ")";
6435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (change.change_type() == SyncChange::ACTION_ADD ||
6445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        change.change_type() == SyncChange::ACTION_UPDATE) {
6455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ProcessSyncItemSpecifics(change.sync_data().GetSpecifics().app_list());
6465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    } else if (change.change_type() == SyncChange::ACTION_DELETE) {
6475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DeleteSyncItemSpecifics(change.sync_data().GetSpecifics().app_list());
6485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    } else {
6495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      LOG(ERROR) << "Invalid sync change";
6505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
6515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Continue observing app list model changes.
6545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_observer_.reset(new ModelObserver(this));
6555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return syncer::SyncError();
6575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
6585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// AppListSyncableService private
6605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool AppListSyncableService::ProcessSyncItemSpecifics(
6625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const sync_pb::AppListSpecifics& specifics) {
6635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const std::string& item_id = specifics.item_id();
6645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_id.empty()) {
6655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "AppList item with empty ID";
6665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
6675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = FindSyncItem(item_id);
6695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_item) {
6705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // If an item of the same type exists, update it.
6715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (sync_item->item_type == specifics.item_type()) {
6725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      UpdateSyncItemFromSync(specifics, sync_item);
6735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ProcessExistingSyncItem(sync_item);
674c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      VLOG(2) << this << " <- SYNC UPDATE: " << sync_item->ToString();
6755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return false;
6765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
6775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Otherwise, one of the entries should be TYPE_REMOVE_DEFAULT_APP.
6785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (sync_item->item_type !=
6795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP &&
6805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        specifics.item_type() !=
6815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
6825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      LOG(ERROR) << "Synced item type: " << specifics.item_type()
6835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 << " != existing sync item type: " << sync_item->item_type
6845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 << " Deleting item from model!";
6855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      model_->DeleteItem(item_id);
6865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
687c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " - ProcessSyncItem: Delete existing entry: "
688c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            << sync_item->ToString();
6895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    delete sync_item;
6905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_items_.erase(item_id);
6915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_item = CreateSyncItem(item_id, specifics.item_type());
6945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UpdateSyncItemFromSync(specifics, sync_item);
6955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ProcessNewSyncItem(sync_item);
696c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << " <- SYNC ADD: " << sync_item->ToString();
6975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return true;
6985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
6995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::ProcessNewSyncItem(SyncItem* sync_item) {
701c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << "ProcessNewSyncItem: " << sync_item->ToString();
7025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  switch (sync_item->item_type) {
7035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case sync_pb::AppListSpecifics::TYPE_APP: {
7045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // New apps are added through ExtensionAppModelBuilder.
7055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // TODO(stevenjb): Determine how to handle app items in sync that
7065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // are not installed (e.g. default / OEM apps).
7075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
7085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
7095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP: {
710c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      VLOG(1) << this << ": Uninstall: " << sync_item->ToString();
7115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      UninstallExtension(extension_system_->extension_service(),
7125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         sync_item->item_id);
7135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
7145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
7155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case sync_pb::AppListSpecifics::TYPE_FOLDER: {
7165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      AppListItem* app_item = model_->FindItem(sync_item->item_id);
7175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (!app_item)
7185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return;  // Don't create new folders here, the model will do that.
7195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      UpdateAppItemFromSyncItem(sync_item, app_item);
7205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
7215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
7225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case sync_pb::AppListSpecifics::TYPE_URL: {
7235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // TODO(stevenjb): Implement
7245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      LOG(WARNING) << "TYPE_URL not supported";
7255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
7265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
7275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  NOTREACHED() << "Unrecognized sync item type: " << sync_item->ToString();
7295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
7305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::ProcessExistingSyncItem(SyncItem* sync_item) {
7325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_item->item_type ==
7335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
7345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
7355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
736c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << "ProcessExistingSyncItem: " << sync_item->ToString();
7375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  AppListItem* app_item = model_->FindItem(sync_item->item_id);
7385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DVLOG(2) << " AppItem: " << app_item->ToDebugString();
7395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!app_item) {
7405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "Item not found in model: " << sync_item->ToString();
7415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
7425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // This is the only place where sync can cause an item to change folders.
7445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (app_list::switches::IsFolderUIEnabled() &&
745effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      app_item->folder_id() != sync_item->parent_id &&
746effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      !AppIsOem(app_item->id())) {
7475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    VLOG(2) << " Moving Item To Folder: " << sync_item->parent_id;
7485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model_->MoveItemToFolder(app_item, sync_item->parent_id);
7495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UpdateAppItemFromSyncItem(sync_item, app_item);
7515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
7525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::UpdateAppItemFromSyncItem(
7545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const AppListSyncableService::SyncItem* sync_item,
7555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    AppListItem* app_item) {
756116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  VLOG(2) << this << " UpdateAppItemFromSyncItem: " << sync_item->ToString();
7575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!app_item->position().Equals(sync_item->item_ordinal))
7585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model_->SetItemPosition(app_item, sync_item->item_ordinal);
759a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Only update the item name if it is a Folder or the name is empty.
760a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (sync_item->item_name != app_item->name() &&
76123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      sync_item->item_id != kOemFolderId &&
762a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      (app_item->GetItemType() == AppListFolderItem::kItemType ||
763a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)       app_item->name().empty())) {
764a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    model_->SetItemName(app_item, sync_item->item_name);
765a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
7665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
7675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool AppListSyncableService::SyncStarted() {
7695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_processor_.get())
7705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return true;
7715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (flare_.is_null()) {
772c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(1) << this << ": SyncStarted: Flare.";
7735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    flare_ = sync_start_util::GetFlareForSyncableService(profile_->GetPath());
7745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    flare_.Run(syncer::APP_LIST);
7755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return false;
7775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
7785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::SendSyncChange(
7805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncItem* sync_item,
7815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncChange::SyncChangeType sync_change_type) {
7825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!SyncStarted()) {
7835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << this << " - SendSyncChange: SYNC NOT STARTED: "
7845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)             << sync_item->ToString();
7855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
7865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_change_type == SyncChange::ACTION_ADD)
788c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
7895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  else
790c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC UPDATE: " << sync_item->ToString();
7915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncChange sync_change(FROM_HERE, sync_change_type,
7925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         GetSyncDataFromSyncItem(sync_item));
7935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_processor_->ProcessSyncChanges(
7945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      FROM_HERE, syncer::SyncChangeList(1, sync_change));
7955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
7965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem*
7985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::FindSyncItem(const std::string& item_id) {
7995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItemMap::iterator iter = sync_items_.find(item_id);
8005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (iter == sync_items_.end())
8015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return NULL;
8025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return iter->second;
8035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
8045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
8055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem*
8065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::CreateSyncItem(
8075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& item_id,
8085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_pb::AppListSpecifics::AppListItemType item_type) {
8095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(!ContainsKey(sync_items_, item_id));
8105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = new SyncItem(item_id, item_type);
8115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_items_[item_id] = sync_item;
8125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return sync_item;
8135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
8145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
8155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::DeleteSyncItemSpecifics(
8165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const sync_pb::AppListSpecifics& specifics) {
8175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const std::string& item_id = specifics.item_id();
8185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_id.empty()) {
8195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "Delete AppList item with empty ID";
8205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
8215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
822c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << ": DeleteSyncItemSpecifics: " << item_id.substr(0, 8);
8235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItemMap::iterator iter = sync_items_.find(item_id);
8245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (iter == sync_items_.end())
8255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
8265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_pb::AppListSpecifics::AppListItemType item_type =
8275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      iter->second->item_type;
828c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << " <- SYNC DELETE: " << iter->second->ToString();
8295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  delete iter->second;
8305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_items_.erase(iter);
8315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Only delete apps from the model. Folders will be deleted when all
8325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // children have been deleted.
8335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_type == sync_pb::AppListSpecifics::TYPE_APP)
8345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model_->DeleteItem(item_id);
8355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
8365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
8375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)std::string AppListSyncableService::FindOrCreateOemFolder() {
83823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
83923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (!oem_folder) {
84023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    scoped_ptr<AppListFolderItem> new_folder(new AppListFolderItem(
84123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        kOemFolderId, AppListFolderItem::FOLDER_TYPE_OEM));
84223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    oem_folder = static_cast<AppListFolderItem*>(
84323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        model_->AddItem(new_folder.PassAs<app_list::AppListItem>()));
844116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    SyncItem* oem_sync_item = FindSyncItem(kOemFolderId);
845116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (oem_sync_item) {
8465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      VLOG(1) << "Creating OEM folder from existing sync item: "
847116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch               << oem_sync_item->item_ordinal.ToDebugString();
848116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      model_->SetItemPosition(oem_folder, oem_sync_item->item_ordinal);
849116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    } else {
850116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      model_->SetItemPosition(oem_folder, GetOemFolderPos());
8515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // Do not create a sync item for the OEM folder here, do it in
8525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // ResolveFolderPositions() when the item position is finalized.
853116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
85423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  }
85523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  model_->SetItemName(oem_folder, oem_folder_name_);
85623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  return oem_folder->id();
85723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
85823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
859116680a4aac90f2aa7413d9095a592090648e557Ben Murdochsyncer::StringOrdinal AppListSyncableService::GetOemFolderPos() {
860116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  VLOG(1) << "GetOemFolderPos: " << first_app_list_sync_;
861116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!first_app_list_sync_) {
8625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    VLOG(1) << "Sync items exist, placing OEM folder at end.";
863116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    syncer::StringOrdinal last;
864116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    for (SyncItemMap::iterator iter = sync_items_.begin();
865116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch         iter != sync_items_.end(); ++iter) {
866116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      SyncItem* sync_item = iter->second;
867116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      if (!last.IsValid() || sync_item->item_ordinal.GreaterThan(last))
868116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        last = sync_item->item_ordinal;
869116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
870116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return last.CreateAfter();
871116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
872116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
873116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Place the OEM folder just after the web store, which should always be
874116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // followed by a pre-installed app (e.g. Search), so the poosition should be
875116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // stable. TODO(stevenjb): consider explicitly setting the OEM folder location
876116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // along with the name in ServicesCustomizationDocument::SetOemFolderName().
877116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  AppListItemList* item_list = model_->top_level_item_list();
878116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (item_list->item_count() == 0)
879116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return syncer::StringOrdinal();
880116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
881116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  size_t oem_index = 0;
882116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  for (; oem_index < item_list->item_count() - 1; ++oem_index) {
883116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    AppListItem* cur_item = item_list->item_at(oem_index);
884116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (cur_item->id() == extension_misc::kWebStoreAppId)
885116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      break;
886116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
887116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  syncer::StringOrdinal oem_ordinal;
888116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  AppListItem* prev = item_list->item_at(oem_index);
889116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (oem_index + 1 < item_list->item_count()) {
890116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    AppListItem* next = item_list->item_at(oem_index + 1);
891116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    oem_ordinal = prev->position().CreateBetween(next->position());
892116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  } else {
893116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    oem_ordinal = prev->position().CreateAfter();
894116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
895116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  VLOG(1) << "Placing OEM Folder at: " << oem_index
896116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << " position: " << oem_ordinal.ToDebugString();
897116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return oem_ordinal;
898116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
899116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
900effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochbool AppListSyncableService::AppIsOem(const std::string& id) {
901effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!extension_system_->extension_service())
902effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return false;
903effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  const extensions::Extension* extension =
904effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      extension_system_->extension_service()->GetExtensionById(id, true);
905effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return extension && extension->was_installed_by_oem();
906effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
907effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
9085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)std::string AppListSyncableService::SyncItem::ToString() const {
9095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string res = item_id.substr(0, 8);
9105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_type == sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
9115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    res += " { RemoveDefault }";
9125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
9135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    res += " { " + item_name + " }";
9145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    res += " [" + item_ordinal.ToDebugString() + "]";
9155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!parent_id.empty())
9165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      res += " <" + parent_id.substr(0, 8) + ">";
9175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
9185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return res;
9195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
9205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
921f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}  // namespace app_list
922