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"
181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chrome/grit/generated_resources.h"
19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "content/public/browser/notification_source.h"
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_prefs.h"
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "extensions/browser/extension_system.h"
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "extensions/browser/uninstall_reason.h"
231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "extensions/common/constants.h"
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "sync/api/sync_change_processor.h"
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "sync/api/sync_data.h"
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "sync/api/sync_merge_result.h"
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "sync/protocol/sync.pb.h"
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/app_list/app_list_folder_item.h"
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/app_list/app_list_item.h"
30f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)#include "ui/app_list/app_list_model.h"
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/app_list/app_list_model_observer.h"
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "ui/app_list/app_list_switches.h"
3323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#include "ui/base/l10n/l10n_util.h"
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
35116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if defined(OS_CHROMEOS)
36116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chrome/browser/chromeos/file_manager/app_id.h"
37116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "chrome/browser/chromeos/genius_app/app_id.h"
38116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
39116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using syncer::SyncChange;
41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace app_list {
43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)namespace {
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)const char kOemFolderId[] = "ddb1da55-d478-4243-8642-56d3041f0263";
4723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UpdateSyncItemFromSync(const sync_pb::AppListSpecifics& specifics,
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            AppListSyncableService::SyncItem* item) {
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_EQ(item->item_id, specifics.item_id());
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  item->item_type = specifics.item_type();
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  item->item_name = specifics.item_name();
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  item->parent_id = specifics.parent_id();
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!specifics.page_ordinal().empty())
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    item->page_ordinal = syncer::StringOrdinal(specifics.page_ordinal());
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!specifics.item_ordinal().empty())
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    item->item_ordinal = syncer::StringOrdinal(specifics.item_ordinal());
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool UpdateSyncItemFromAppItem(const AppListItem* app_item,
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                               AppListSyncableService::SyncItem* sync_item) {
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_EQ(sync_item->item_id, app_item->id());
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  bool changed = false;
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (app_list::switches::IsFolderUIEnabled() &&
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      sync_item->parent_id != app_item->folder_id()) {
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_item->parent_id = app_item->folder_id();
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    changed = true;
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (sync_item->item_name != app_item->name()) {
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    sync_item->item_name = app_item->name();
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    changed = true;
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!sync_item->item_ordinal.IsValid() ||
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      !app_item->position().Equals(sync_item->item_ordinal)) {
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_item->item_ordinal = app_item->position();
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    changed = true;
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // TODO(stevenjb): Set page_ordinal.
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return changed;
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void GetSyncSpecificsFromSyncItem(const AppListSyncableService::SyncItem* item,
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                  sync_pb::AppListSpecifics* specifics) {
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(specifics);
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  specifics->set_item_id(item->item_id);
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  specifics->set_item_type(item->item_type);
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  specifics->set_item_name(item->item_name);
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  specifics->set_parent_id(item->parent_id);
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item->page_ordinal.IsValid())
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    specifics->set_page_ordinal(item->page_ordinal.ToInternalValue());
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item->item_ordinal.IsValid())
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    specifics->set_item_ordinal(item->item_ordinal.ToInternalValue());
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)syncer::SyncData GetSyncDataFromSyncItem(
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const AppListSyncableService::SyncItem* item) {
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_pb::EntitySpecifics specifics;
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  GetSyncSpecificsFromSyncItem(item, specifics.mutable_app_list());
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return syncer::SyncData::CreateLocalData(item->item_id,
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                           item->item_id,
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                           specifics);
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool AppIsDefault(ExtensionService* service, const std::string& id) {
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return service && extensions::ExtensionPrefs::Get(service->profile())
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                        ->WasInstalledByDefault(id);
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
109116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbool IsUnRemovableDefaultApp(const std::string& id) {
110116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (id == extension_misc::kChromeAppId ||
1111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      id == extensions::kWebStoreAppId)
112116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return true;
113116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#if defined(OS_CHROMEOS)
114116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (id == file_manager::kFileManagerAppId || id == genius_app::kGeniusAppId)
115116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return true;
116116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#endif
117116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return false;
118116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
119116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void UninstallExtension(ExtensionService* service, const std::string& id) {
1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (service && service->GetInstalledExtension(id)) {
1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    service->UninstallExtension(id,
1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                extensions::UNINSTALL_REASON_SYNC,
1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                base::Bind(&base::DoNothing),
1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                NULL);
1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool GetAppListItemType(AppListItem* item,
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                        sync_pb::AppListSpecifics::AppListItemType* type) {
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const char* item_type = item->GetItemType();
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_type == ExtensionAppItem::kItemType) {
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *type = sync_pb::AppListSpecifics::TYPE_APP;
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else if (item_type == AppListFolderItem::kItemType) {
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    *type = sync_pb::AppListSpecifics::TYPE_FOLDER;
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "Unrecognized model type: " << item_type;
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return true;
1415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}  // namespace
1445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// AppListSyncableService::SyncItem
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem::SyncItem(
1485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& id,
1495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_pb::AppListSpecifics::AppListItemType type)
1505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : item_id(id),
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      item_type(type) {
1525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem::~SyncItem() {
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
1565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// AppListSyncableService::ModelObserver
1585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class AppListSyncableService::ModelObserver : public AppListModelObserver {
1605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public:
1615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  explicit ModelObserver(AppListSyncableService* owner)
162c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      : owner_(owner),
163c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch        adding_item_(NULL) {
1645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << owner_ << ": ModelObserver Added";
1655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    owner_->model()->AddObserver(this);
1665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual ~ModelObserver() {
1695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    owner_->model()->RemoveObserver(this);
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << owner_ << ": ModelObserver Removed";
1715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private:
1745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // AppListModelObserver
1755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual void OnAppListItemAdded(AppListItem* item) OVERRIDE {
176c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    DCHECK(!adding_item_);
177c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    adding_item_ = item;  // Ignore updates while adding an item.
178c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << owner_ << " OnAppListItemAdded: " << item->ToDebugString();
1795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    owner_->AddOrUpdateFromSyncItem(item);
180c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    adding_item_ = NULL;
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual void OnAppListItemWillBeDeleted(AppListItem* item) OVERRIDE {
184c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    DCHECK(!adding_item_);
185c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << owner_ << " OnAppListItemDeleted: " << item->ToDebugString();
186c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    // Don't sync folder removal in case the folder still exists on another
187c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    // device (e.g. with device specific items in it). Empty folders will be
188c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    // deleted when the last item is removed (in PruneEmptySyncFolders()).
189c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    if (item->GetItemType() == AppListFolderItem::kItemType)
190c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return;
1915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    owner_->RemoveSyncItem(item->id());
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  virtual void OnAppListItemUpdated(AppListItem* item) OVERRIDE {
195c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    if (adding_item_) {
196c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // Adding an item may trigger update notifications which should be
197c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      // ignored.
198c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      DCHECK_EQ(adding_item_, item);
199c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      return;
200c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    }
201c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << owner_ << " OnAppListItemUpdated: " << item->ToDebugString();
2025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    owner_->UpdateSyncItem(item);
2035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  AppListSyncableService* owner_;
206c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  AppListItem* adding_item_;  // Unowned pointer to item being added.
2075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ModelObserver);
2095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
2105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// AppListSyncableService
2125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
213f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)AppListSyncableService::AppListSyncableService(
214f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    Profile* profile,
2155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    extensions::ExtensionSystem* extension_system)
216f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    : profile_(profile),
2175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      extension_system_(extension_system),
218116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      model_(new AppListModel),
2191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      initial_sync_data_processed_(false),
220116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      first_app_list_sync_(true) {
2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!extension_system) {
2225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "AppListSyncableService created with no ExtensionSystem";
2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
2245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
22623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  oem_folder_name_ =
22723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      l10n_util::GetStringUTF8(IDS_APP_LIST_OEM_DEFAULT_FOLDER_NAME);
22823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
2295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Note: model_observer_ is constructed after the initial sync changes are
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // received in MergeDataAndStartSyncing(). Changes to the model before that
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // will be synced after the initial sync occurs.
2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (extension_system->extension_service() &&
2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      extension_system->extension_service()->is_ready()) {
234f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    BuildModel();
235f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return;
236f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  }
237f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
238f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // The extensions for this profile have not yet all been loaded.
2395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  registrar_.Add(this,
2405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
241f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)                 content::Source<Profile>(profile));
242f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
243f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
244f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)AppListSyncableService::~AppListSyncableService() {
2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Remove observers.
2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_observer_.reset();
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  STLDeleteContainerPairSecondPointers(sync_items_.begin(), sync_items_.end());
249f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
250f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
251f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void AppListSyncableService::BuildModel() {
252f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // For now, use the AppListControllerDelegate associated with the native
253f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // desktop. TODO(stevenjb): Remove ExtensionAppModelBuilder controller
254f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // dependency and move the dependent methods from AppListControllerDelegate
255f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  // to an extension service delegate associated with this class.
256f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  AppListControllerDelegate* controller = NULL;
257f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  AppListService* service =
258f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      AppListService::Get(chrome::HOST_DESKTOP_TYPE_NATIVE);
259f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  if (service)
260f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    controller = service->GetControllerDelegate();
2615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  apps_builder_.reset(new ExtensionAppModelBuilder(controller));
262f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK(profile_);
263a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  if (app_list::switches::IsAppListSyncEnabled()) {
264c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(1) << this << ": AppListSyncableService: InitializeWithService.";
2655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncStarted();
2665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    apps_builder_->InitializeWithService(this);
2675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
268c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(1) << this << ": AppListSyncableService: InitializeWithProfile.";
2695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    apps_builder_->InitializeWithProfile(profile_, model_.get());
2705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
2716d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2726d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (app_list::switches::IsDriveAppsInAppListEnabled())
2736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    drive_app_provider_.reset(new DriveAppProvider(profile_));
2746d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
2756d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid AppListSyncableService::ResetDriveAppProviderForTest() {
2771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  drive_app_provider_.reset();
2781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
2791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
2806d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void AppListSyncableService::Shutdown() {
2816d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // DriveAppProvider touches other KeyedServices in its dtor and needs be
2826d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // released in shutdown stage.
2836d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  drive_app_provider_.reset();
284f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
285f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
286f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void AppListSyncableService::Observe(
287f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    int type,
288f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const content::NotificationSource& source,
289f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    const content::NotificationDetails& details) {
2905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK_EQ(extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, type);
291f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
292f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  registrar_.RemoveAll();
293f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  BuildModel();
294f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}
295f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
2965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const AppListSyncableService::SyncItem*
2975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::GetSyncItem(const std::string& id) const {
2985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItemMap::const_iterator iter = sync_items_.find(id);
2995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (iter != sync_items_.end())
3005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return iter->second;
3015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return NULL;
3025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
30423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void AppListSyncableService::SetOemFolderName(const std::string& name) {
30523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  oem_folder_name_ = name;
30623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
30723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (oem_folder)
30823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    model_->SetItemName(oem_folder, oem_folder_name_);
30923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
31023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
3115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::AddItem(scoped_ptr<AppListItem> app_item) {
3125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = FindOrAddSyncItem(app_item.get());
3135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!sync_item)
3145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;  // Item is not valid.
3155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
316effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  std::string folder_id;
317e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (app_list::switches::IsFolderUIEnabled()) {
318e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    if (AppIsOem(app_item->id())) {
3195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      folder_id = FindOrCreateOemFolder();
320116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      VLOG_IF(2, !folder_id.empty())
321116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << this << ": AddItem to OEM folder: " << sync_item->ToString();
322e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    } else {
323e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      folder_id = sync_item->parent_id;
324e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    }
32523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  }
326c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << ": AddItem: " << sync_item->ToString()
327116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << " Folder: '" << folder_id << "'";
3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_->AddItemToFolder(app_item.Pass(), folder_id);
3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem* AppListSyncableService::FindOrAddSyncItem(
3325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    AppListItem* app_item) {
3335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const std::string& item_id = app_item->id();
3345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_id.empty()) {
3355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "AppListItem item with empty ID";
3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return NULL;
3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = FindSyncItem(item_id);
3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_item) {
3405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // If there is an existing, non-REMOVE_DEFAULT entry, return it.
3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (sync_item->item_type !=
3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
3435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DVLOG(2) << this << ": AddItem already exists: " << sync_item->ToString();
3445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return sync_item;
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (RemoveDefaultApp(app_item, sync_item))
3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return NULL;
3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Fall through. The REMOVE_DEFAULT_APP entry has been deleted, now a new
3515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // App entry can be added.
3525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return CreateSyncItemFromAppItem(app_item);
3555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem*
3585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::CreateSyncItemFromAppItem(AppListItem* app_item) {
3595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_pb::AppListSpecifics::AppListItemType type;
3605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!GetAppListItemType(app_item, &type))
3615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return NULL;
362c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << " CreateSyncItemFromAppItem:" << app_item->ToDebugString();
3635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = CreateSyncItem(app_item->id(), type);
3645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UpdateSyncItemFromAppItem(app_item, sync_item);
3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SendSyncChange(sync_item, SyncChange::ACTION_ADD);
3665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return sync_item;
3675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::AddOrUpdateFromSyncItem(AppListItem* app_item) {
3705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Do not create a sync item for the OEM folder here, do that in
3715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // ResolveFolderPositions once the position has been resolved.
3725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (app_item->id() == kOemFolderId)
3735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return;
3745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = FindSyncItem(app_item->id());
3765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_item) {
3775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    UpdateAppItemFromSyncItem(sync_item, app_item);
3785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
3795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CreateSyncItemFromAppItem(app_item);
3815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
3825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool AppListSyncableService::RemoveDefaultApp(AppListItem* item,
3845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                              SyncItem* sync_item) {
3855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  CHECK_EQ(sync_item->item_type,
3865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)           sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP);
3875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // If there is an existing REMOVE_DEFAULT_APP entry, and the app is
3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // installed as a Default app, uninstall the app instead of adding it.
3905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_item->item_type == sync_pb::AppListSpecifics::TYPE_APP &&
3915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      AppIsDefault(extension_system_->extension_service(), item->id())) {
392c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << ": HandleDefaultApp: Uninstall: "
393c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            << sync_item->ToString();
3945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    UninstallExtension(extension_system_->extension_service(), item->id());
3955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return true;
3965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
3975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Otherwise, we are adding the app as a non-default app (i.e. an app that
3995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // was installed by Default and removed is getting installed explicitly by
4005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // the user), so delete the REMOVE_DEFAULT_APP.
4015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DeleteSyncItem(sync_item);
4025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return false;
4035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::DeleteSyncItem(SyncItem* sync_item) {
4065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (SyncStarted()) {
407c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC DELETE: " << sync_item->ToString();
4085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncChange sync_change(FROM_HERE, SyncChange::ACTION_DELETE,
4095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                           GetSyncDataFromSyncItem(sync_item));
4105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_processor_->ProcessSyncChanges(
4115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        FROM_HERE, syncer::SyncChangeList(1, sync_change));
4125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string item_id = sync_item->item_id;
4145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  delete sync_item;
4155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_items_.erase(item_id);
4165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::UpdateSyncItem(AppListItem* app_item) {
4195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = FindSyncItem(app_item->id());
4205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!sync_item) {
4215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "UpdateItem: no sync item: " << app_item->id();
4225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  bool changed = UpdateSyncItemFromAppItem(app_item, sync_item);
4255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!changed) {
4265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << this << " - Update: SYNC NO CHANGE: " << sync_item->ToString();
4275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
4305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::RemoveItem(const std::string& id) {
4335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  RemoveSyncItem(id);
4345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_->DeleteItem(id);
4355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  PruneEmptySyncFolders();
4365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
43823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void AppListSyncableService::UpdateItem(AppListItem* app_item) {
43923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  // Check to see if the item needs to be moved to/from the OEM folder.
440e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  if (!app_list::switches::IsFolderUIEnabled())
441e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    return;
442effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  bool is_oem = AppIsOem(app_item->id());
44323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (!is_oem && app_item->folder_id() == kOemFolderId)
44423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    model_->MoveItemToFolder(app_item, "");
44523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  else if (is_oem && app_item->folder_id() != kOemFolderId)
44623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    model_->MoveItemToFolder(app_item, kOemFolderId);
44723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
44823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
4495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::RemoveSyncItem(const std::string& id) {
450c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << ": RemoveSyncItem: " << id.substr(0, 8);
4515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItemMap::iterator iter = sync_items_.find(id);
4525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (iter == sync_items_.end()) {
4535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << this << " : RemoveSyncItem: No Item.";
4545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Check for existing RemoveDefault sync item.
4585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = iter->second;
4595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_pb::AppListSpecifics::AppListItemType type = sync_item->item_type;
4605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (type == sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
4615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // RemoveDefault item exists, just return.
4625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << this << " : RemoveDefault Item exists.";
4635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (type == sync_pb::AppListSpecifics::TYPE_APP &&
4675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      AppIsDefault(extension_system_->extension_service(), id)) {
4685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // This is a Default app; update the entry to a REMOVE_DEFAULT entry. This
4695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // will overwrite any existing entry for the item.
470c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC UPDATE: REMOVE_DEFAULT: "
471c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            << sync_item->item_id;
4725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_item->item_type = sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP;
4735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
4745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
4765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DeleteSyncItem(sync_item);
4785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
4795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
480116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid AppListSyncableService::ResolveFolderPositions() {
4815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!app_list::switches::IsFolderUIEnabled())
4825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
4835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  VLOG(1) << "ResolveFolderPositions.";
4855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (SyncItemMap::iterator iter = sync_items_.begin();
4865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != sync_items_.end(); ++iter) {
4875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncItem* sync_item = iter->second;
4885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
4895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
4905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    AppListItem* app_item = model_->FindItem(sync_item->item_id);
4915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!app_item)
4925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
4935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    UpdateAppItemFromSyncItem(sync_item, app_item);
4945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
495116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
4965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Move the OEM folder if one exists and we have not synced its position.
4975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
4985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (oem_folder && !FindSyncItem(kOemFolderId)) {
4995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    model_->SetItemPosition(oem_folder, GetOemFolderPos());
5005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    VLOG(1) << "Creating new OEM folder sync item: "
5015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            << oem_folder->position().ToDebugString();
5025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    CreateSyncItemFromAppItem(oem_folder);
503116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
5045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
5055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::PruneEmptySyncFolders() {
5075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!app_list::switches::IsFolderUIEnabled())
5085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
5095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::set<std::string> parent_ids;
5115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (SyncItemMap::iterator iter = sync_items_.begin();
5125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != sync_items_.end(); ++iter) {
5135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    parent_ids.insert(iter->second->parent_id);
5145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (SyncItemMap::iterator iter = sync_items_.begin();
5165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != sync_items_.end(); ) {
5175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncItem* sync_item = (iter++)->second;
5185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
5195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
5205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!ContainsKey(parent_ids, sync_item->item_id))
5215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DeleteSyncItem(sync_item);
5225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
5245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// AppListSyncableService syncer::SyncableService
5265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing(
5285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    syncer::ModelType type,
5295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const syncer::SyncDataList& initial_sync_data,
5305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
5315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<syncer::SyncErrorFactory> error_handler) {
5325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(!sync_processor_.get());
5335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(sync_processor.get());
5345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(error_handler.get());
5355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_processor_ = sync_processor.Pass();
5375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_error_handler_ = error_handler.Pass();
53846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  if (switches::IsFolderUIEnabled())
53946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    model_->SetFoldersEnabled(true);
5405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  syncer::SyncMergeResult result = syncer::SyncMergeResult(type);
5425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  result.set_num_items_before_association(sync_items_.size());
543c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(1) << this << ": MergeDataAndStartSyncing: "
544c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch          << initial_sync_data.size();
5455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Copy all sync items to |unsynced_items|.
5475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::set<std::string> unsynced_items;
5485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (SyncItemMap::const_iterator iter = sync_items_.begin();
5495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != sync_items_.end(); ++iter) {
5505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    unsynced_items.insert(iter->first);
5515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Create SyncItem entries for initial_sync_data.
5545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  size_t new_items = 0, updated_items = 0;
5555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin();
5565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != initial_sync_data.end(); ++iter) {
5575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const syncer::SyncData& data = *iter;
558e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    const std::string& item_id = data.GetSpecifics().app_list().item_id();
559116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const sync_pb::AppListSpecifics& specifics = data.GetSpecifics().app_list();
560e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    DVLOG(2) << this << "  Initial Sync Item: " << item_id
561116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch             << " Type: " << specifics.item_type();
5625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DCHECK_EQ(syncer::APP_LIST, data.GetDataType());
563116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (ProcessSyncItemSpecifics(specifics))
5645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ++new_items;
5655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    else
5665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ++updated_items;
567116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (specifics.item_type() != sync_pb::AppListSpecifics::TYPE_FOLDER &&
568116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        !IsUnRemovableDefaultApp(item_id) &&
569116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        !AppIsOem(item_id) &&
570116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        !AppIsDefault(extension_system_->extension_service(), item_id)) {
571116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      VLOG(2) << "Syncing non-default item: " << item_id;
572116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      first_app_list_sync_ = false;
573116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
574e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch    unsynced_items.erase(item_id);
5755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  result.set_num_items_after_association(sync_items_.size());
5775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  result.set_num_items_added(new_items);
5785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  result.set_num_items_deleted(0);
5795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  result.set_num_items_modified(updated_items);
5805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // Initial sync data has been processed, it is safe now to add new sync items.
5821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  initial_sync_data_processed_ = true;
5831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
5845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Send unsynced items. Does not affect |result|.
5855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  syncer::SyncChangeList change_list;
5865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (std::set<std::string>::iterator iter = unsynced_items.begin();
5875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != unsynced_items.end(); ++iter) {
5885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncItem* sync_item = FindSyncItem(*iter);
589116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Sync can cause an item to change folders, causing an unsynced folder
590116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // item to be removed.
591116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (!sync_item)
592116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      continue;
593c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
5945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    change_list.push_back(SyncChange(FROM_HERE,  SyncChange::ACTION_ADD,
5955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                     GetSyncDataFromSyncItem(sync_item)));
5965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
5985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Adding items may have created folders without setting their positions
6005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // since we haven't started observing the item list yet. Resolve those.
601116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  ResolveFolderPositions();
6025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Start observing app list model changes.
6045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_observer_.reset(new ModelObserver(this));
6055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return result;
6075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
6085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::StopSyncing(syncer::ModelType type) {
6105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_EQ(type, syncer::APP_LIST);
6115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_processor_.reset();
6135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_error_handler_.reset();
61446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  model_->SetFoldersEnabled(false);
6155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
6165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)syncer::SyncDataList AppListSyncableService::GetAllSyncData(
6185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    syncer::ModelType type) const {
6195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK_EQ(syncer::APP_LIST, type);
6205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
621c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(1) << this << ": GetAllSyncData: " << sync_items_.size();
6225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  syncer::SyncDataList list;
6235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (SyncItemMap::const_iterator iter = sync_items_.begin();
6245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != sync_items_.end(); ++iter) {
625c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC: " << iter->second->ToString();
6265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    list.push_back(GetSyncDataFromSyncItem(iter->second));
6275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return list;
6295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
6305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)syncer::SyncError AppListSyncableService::ProcessSyncChanges(
6325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const tracked_objects::Location& from_here,
6335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const syncer::SyncChangeList& change_list) {
6345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!sync_processor_.get()) {
6355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return syncer::SyncError(FROM_HERE,
6365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             syncer::SyncError::DATATYPE_ERROR,
6375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             "App List syncable service is not started.",
6385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                             syncer::APP_LIST);
6395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Don't observe the model while processing incoming sync changes.
6425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_observer_.reset();
6435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
644c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(1) << this << ": ProcessSyncChanges: " << change_list.size();
6455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
6465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)       iter != change_list.end(); ++iter) {
6475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const SyncChange& change = *iter;
648c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << "  Change: "
649c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            << change.sync_data().GetSpecifics().app_list().item_id()
650c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            << " (" << change.change_type() << ")";
6515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (change.change_type() == SyncChange::ACTION_ADD ||
6525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        change.change_type() == SyncChange::ACTION_UPDATE) {
6535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ProcessSyncItemSpecifics(change.sync_data().GetSpecifics().app_list());
6545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    } else if (change.change_type() == SyncChange::ACTION_DELETE) {
6555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DeleteSyncItemSpecifics(change.sync_data().GetSpecifics().app_list());
6565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    } else {
6575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      LOG(ERROR) << "Invalid sync change";
6585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
6595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Continue observing app list model changes.
6625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  model_observer_.reset(new ModelObserver(this));
6635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return syncer::SyncError();
6655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
6665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// AppListSyncableService private
6685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
6695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool AppListSyncableService::ProcessSyncItemSpecifics(
6705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const sync_pb::AppListSpecifics& specifics) {
6715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const std::string& item_id = specifics.item_id();
6725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_id.empty()) {
6735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "AppList item with empty ID";
6745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return false;
6755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
6765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = FindSyncItem(item_id);
6775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_item) {
6785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // If an item of the same type exists, update it.
6795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (sync_item->item_type == specifics.item_type()) {
6805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      UpdateSyncItemFromSync(specifics, sync_item);
6815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ProcessExistingSyncItem(sync_item);
682c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      VLOG(2) << this << " <- SYNC UPDATE: " << sync_item->ToString();
6835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return false;
6845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
6855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Otherwise, one of the entries should be TYPE_REMOVE_DEFAULT_APP.
6865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (sync_item->item_type !=
6875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP &&
6885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        specifics.item_type() !=
6895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
6905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      LOG(ERROR) << "Synced item type: " << specifics.item_type()
6915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 << " != existing sync item type: " << sync_item->item_type
6925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 << " Deleting item from model!";
6935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      model_->DeleteItem(item_id);
6945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
695c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " - ProcessSyncItem: Delete existing entry: "
696c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch            << sync_item->ToString();
6975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    delete sync_item;
6985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_items_.erase(item_id);
6995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_item = CreateSyncItem(item_id, specifics.item_type());
7025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UpdateSyncItemFromSync(specifics, sync_item);
7035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ProcessNewSyncItem(sync_item);
704c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << " <- SYNC ADD: " << sync_item->ToString();
7055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return true;
7065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
7075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::ProcessNewSyncItem(SyncItem* sync_item) {
709c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << "ProcessNewSyncItem: " << sync_item->ToString();
7105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  switch (sync_item->item_type) {
7115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case sync_pb::AppListSpecifics::TYPE_APP: {
7125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // New apps are added through ExtensionAppModelBuilder.
7135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // TODO(stevenjb): Determine how to handle app items in sync that
7145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // are not installed (e.g. default / OEM apps).
7155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
7165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
7175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP: {
718c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch      VLOG(1) << this << ": Uninstall: " << sync_item->ToString();
7195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      UninstallExtension(extension_system_->extension_service(),
7205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         sync_item->item_id);
7215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
7225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
7235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case sync_pb::AppListSpecifics::TYPE_FOLDER: {
7245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      AppListItem* app_item = model_->FindItem(sync_item->item_id);
7255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if (!app_item)
7265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return;  // Don't create new folders here, the model will do that.
7275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      UpdateAppItemFromSyncItem(sync_item, app_item);
7285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
7295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
7305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    case sync_pb::AppListSpecifics::TYPE_URL: {
7315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // TODO(stevenjb): Implement
7325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      LOG(WARNING) << "TYPE_URL not supported";
7335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
7345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
7355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  NOTREACHED() << "Unrecognized sync item type: " << sync_item->ToString();
7375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
7385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::ProcessExistingSyncItem(SyncItem* sync_item) {
7405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_item->item_type ==
7415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
7425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
7435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
744c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << "ProcessExistingSyncItem: " << sync_item->ToString();
7455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  AppListItem* app_item = model_->FindItem(sync_item->item_id);
7465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DVLOG(2) << " AppItem: " << app_item->ToDebugString();
7475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!app_item) {
7485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "Item not found in model: " << sync_item->ToString();
7495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
7505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // This is the only place where sync can cause an item to change folders.
7525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (app_list::switches::IsFolderUIEnabled() &&
753effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      app_item->folder_id() != sync_item->parent_id &&
754effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      !AppIsOem(app_item->id())) {
7555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    VLOG(2) << " Moving Item To Folder: " << sync_item->parent_id;
7565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model_->MoveItemToFolder(app_item, sync_item->parent_id);
7575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  UpdateAppItemFromSyncItem(sync_item, app_item);
7595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
7605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::UpdateAppItemFromSyncItem(
7625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const AppListSyncableService::SyncItem* sync_item,
7635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    AppListItem* app_item) {
764116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  VLOG(2) << this << " UpdateAppItemFromSyncItem: " << sync_item->ToString();
7655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!app_item->position().Equals(sync_item->item_ordinal))
7665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model_->SetItemPosition(app_item, sync_item->item_ordinal);
767a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Only update the item name if it is a Folder or the name is empty.
768a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (sync_item->item_name != app_item->name() &&
76923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)      sync_item->item_id != kOemFolderId &&
770a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      (app_item->GetItemType() == AppListFolderItem::kItemType ||
771a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)       app_item->name().empty())) {
772a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    model_->SetItemName(app_item, sync_item->item_name);
773a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
7745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
7755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)bool AppListSyncableService::SyncStarted() {
7775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_processor_.get())
7785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return true;
7795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (flare_.is_null()) {
780c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(1) << this << ": SyncStarted: Flare.";
7815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    flare_ = sync_start_util::GetFlareForSyncableService(profile_->GetPath());
7825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    flare_.Run(syncer::APP_LIST);
7835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return false;
7855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
7865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::SendSyncChange(
7885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncItem* sync_item,
7895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncChange::SyncChangeType sync_change_type) {
7905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!SyncStarted()) {
7915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(2) << this << " - SendSyncChange: SYNC NOT STARTED: "
7925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)             << sync_item->ToString();
7935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
7945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!initial_sync_data_processed_ &&
7961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      sync_change_type == SyncChange::ACTION_ADD) {
7971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // This can occur if an initial item is created before its folder item.
7981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // A sync item should already exist for the folder, so we do not want to
7991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    // send an ADD event, since that would trigger a CHECK in the sync code.
8001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    DCHECK(sync_item->item_type == sync_pb::AppListSpecifics::TYPE_FOLDER);
8011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    DVLOG(2) << this << " - SendSyncChange: ADD before initial data processed: "
8021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci             << sync_item->ToString();
8031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return;
8041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
8055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (sync_change_type == SyncChange::ACTION_ADD)
806c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
8075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  else
808c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch    VLOG(2) << this << " -> SYNC UPDATE: " << sync_item->ToString();
8095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncChange sync_change(FROM_HERE, sync_change_type,
8105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         GetSyncDataFromSyncItem(sync_item));
8115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_processor_->ProcessSyncChanges(
8125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      FROM_HERE, syncer::SyncChangeList(1, sync_change));
8135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
8145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
8155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem*
8165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::FindSyncItem(const std::string& item_id) {
8175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItemMap::iterator iter = sync_items_.find(item_id);
8185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (iter == sync_items_.end())
8195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return NULL;
8205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return iter->second;
8215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
8225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
8235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::SyncItem*
8245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)AppListSyncableService::CreateSyncItem(
8255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& item_id,
8265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    sync_pb::AppListSpecifics::AppListItemType item_type) {
8275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  DCHECK(!ContainsKey(sync_items_, item_id));
8285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItem* sync_item = new SyncItem(item_id, item_type);
8295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_items_[item_id] = sync_item;
8305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return sync_item;
8315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
8325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
8335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void AppListSyncableService::DeleteSyncItemSpecifics(
8345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const sync_pb::AppListSpecifics& specifics) {
8355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const std::string& item_id = specifics.item_id();
8365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_id.empty()) {
8375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    LOG(ERROR) << "Delete AppList item with empty ID";
8385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
8395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
840c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << ": DeleteSyncItemSpecifics: " << item_id.substr(0, 8);
8415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncItemMap::iterator iter = sync_items_.find(item_id);
8425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (iter == sync_items_.end())
8435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
8445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_pb::AppListSpecifics::AppListItemType item_type =
8455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      iter->second->item_type;
846c5cede9ae108bb15f6b7a8aea21c7e1fefa2834cBen Murdoch  VLOG(2) << this << " <- SYNC DELETE: " << iter->second->ToString();
8475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  delete iter->second;
8485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  sync_items_.erase(iter);
8495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Only delete apps from the model. Folders will be deleted when all
8505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // children have been deleted.
8515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_type == sync_pb::AppListSpecifics::TYPE_APP)
8525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    model_->DeleteItem(item_id);
8535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
8545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
8555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)std::string AppListSyncableService::FindOrCreateOemFolder() {
85623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
85723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  if (!oem_folder) {
85823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    scoped_ptr<AppListFolderItem> new_folder(new AppListFolderItem(
85923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        kOemFolderId, AppListFolderItem::FOLDER_TYPE_OEM));
86023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)    oem_folder = static_cast<AppListFolderItem*>(
86123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)        model_->AddItem(new_folder.PassAs<app_list::AppListItem>()));
862116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    SyncItem* oem_sync_item = FindSyncItem(kOemFolderId);
863116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (oem_sync_item) {
8645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      VLOG(1) << "Creating OEM folder from existing sync item: "
865116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch               << oem_sync_item->item_ordinal.ToDebugString();
866116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      model_->SetItemPosition(oem_folder, oem_sync_item->item_ordinal);
867116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    } else {
868116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      model_->SetItemPosition(oem_folder, GetOemFolderPos());
8695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // Do not create a sync item for the OEM folder here, do it in
8705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // ResolveFolderPositions() when the item position is finalized.
871116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
87223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  }
87323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  model_->SetItemName(oem_folder, oem_folder_name_);
87423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)  return oem_folder->id();
87523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)}
87623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)
877116680a4aac90f2aa7413d9095a592090648e557Ben Murdochsyncer::StringOrdinal AppListSyncableService::GetOemFolderPos() {
878116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  VLOG(1) << "GetOemFolderPos: " << first_app_list_sync_;
879116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!first_app_list_sync_) {
8805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    VLOG(1) << "Sync items exist, placing OEM folder at end.";
881116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    syncer::StringOrdinal last;
882116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    for (SyncItemMap::iterator iter = sync_items_.begin();
883116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch         iter != sync_items_.end(); ++iter) {
884116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      SyncItem* sync_item = iter->second;
885116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      if (!last.IsValid() || sync_item->item_ordinal.GreaterThan(last))
886116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        last = sync_item->item_ordinal;
887116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
888116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return last.CreateAfter();
889116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
890116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
891116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Place the OEM folder just after the web store, which should always be
892116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // followed by a pre-installed app (e.g. Search), so the poosition should be
893116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // stable. TODO(stevenjb): consider explicitly setting the OEM folder location
894116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // along with the name in ServicesCustomizationDocument::SetOemFolderName().
895116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  AppListItemList* item_list = model_->top_level_item_list();
896116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (item_list->item_count() == 0)
897116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return syncer::StringOrdinal();
898116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
899116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  size_t oem_index = 0;
900116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  for (; oem_index < item_list->item_count() - 1; ++oem_index) {
901116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    AppListItem* cur_item = item_list->item_at(oem_index);
9021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (cur_item->id() == extensions::kWebStoreAppId)
903116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      break;
904116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
905116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  syncer::StringOrdinal oem_ordinal;
906116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  AppListItem* prev = item_list->item_at(oem_index);
907116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (oem_index + 1 < item_list->item_count()) {
908116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    AppListItem* next = item_list->item_at(oem_index + 1);
909116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    oem_ordinal = prev->position().CreateBetween(next->position());
910116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  } else {
911116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    oem_ordinal = prev->position().CreateAfter();
912116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
913116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  VLOG(1) << "Placing OEM Folder at: " << oem_index
914116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          << " position: " << oem_ordinal.ToDebugString();
915116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return oem_ordinal;
916116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
917116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
918effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochbool AppListSyncableService::AppIsOem(const std::string& id) {
919effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!extension_system_->extension_service())
920effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return false;
921effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  const extensions::Extension* extension =
922effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      extension_system_->extension_service()->GetExtensionById(id, true);
923effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  return extension && extension->was_installed_by_oem();
924effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch}
925effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
9265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)std::string AppListSyncableService::SyncItem::ToString() const {
9275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string res = item_id.substr(0, 8);
9285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (item_type == sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
9295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    res += " { RemoveDefault }";
9305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  } else {
9315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    res += " { " + item_name + " }";
9325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    res += " [" + item_ordinal.ToDebugString() + "]";
9335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!parent_id.empty())
9345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      res += " <" + parent_id.substr(0, 8) + ">";
9355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
9365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return res;
9375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
9385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
939f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)}  // namespace app_list
940