Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / app_list / app_list_syncable_service.cc
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
6
7 #include "base/command_line.h"
8 #include "chrome/browser/chrome_notification_types.h"
9 #include "chrome/browser/extensions/extension_service.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/app_list/app_list_service.h"
12 #include "chrome/browser/ui/app_list/extension_app_item.h"
13 #include "chrome/browser/ui/app_list/extension_app_model_builder.h"
14 #include "chrome/browser/ui/host_desktop.h"
15 #include "chrome/common/chrome_switches.h"
16 #include "content/public/browser/notification_source.h"
17 #include "extensions/browser/extension_prefs.h"
18 #include "extensions/browser/extension_system.h"
19 #include "sync/api/sync_change_processor.h"
20 #include "sync/api/sync_data.h"
21 #include "sync/api/sync_merge_result.h"
22 #include "sync/protocol/sync.pb.h"
23 #include "ui/app_list/app_list_folder_item.h"
24 #include "ui/app_list/app_list_item.h"
25 #include "ui/app_list/app_list_model.h"
26 #include "ui/app_list/app_list_model_observer.h"
27 #include "ui/app_list/app_list_switches.h"
28
29 using syncer::SyncChange;
30
31 namespace app_list {
32
33 namespace {
34
35 bool SyncAppListEnabled() {
36   return CommandLine::ForCurrentProcess()->HasSwitch(
37       ::switches::kEnableSyncAppList);
38 }
39
40 void UpdateSyncItemFromSync(const sync_pb::AppListSpecifics& specifics,
41                             AppListSyncableService::SyncItem* item) {
42   DCHECK_EQ(item->item_id, specifics.item_id());
43   item->item_type = specifics.item_type();
44   item->item_name = specifics.item_name();
45   item->parent_id = specifics.parent_id();
46   if (!specifics.page_ordinal().empty())
47     item->page_ordinal = syncer::StringOrdinal(specifics.page_ordinal());
48   if (!specifics.item_ordinal().empty())
49     item->item_ordinal = syncer::StringOrdinal(specifics.item_ordinal());
50 }
51
52 bool UpdateSyncItemFromAppItem(const AppListItem* app_item,
53                                AppListSyncableService::SyncItem* sync_item) {
54   DCHECK_EQ(sync_item->item_id, app_item->id());
55   bool changed = false;
56   if (app_list::switches::IsFolderUIEnabled() &&
57       sync_item->parent_id != app_item->folder_id()) {
58     sync_item->parent_id = app_item->folder_id();
59     changed = true;
60   }
61   if (sync_item->item_name != app_item->title()) {
62     sync_item->item_name = app_item->title();
63     changed = true;
64   }
65   if (!sync_item->item_ordinal.IsValid() ||
66       !app_item->position().Equals(sync_item->item_ordinal)) {
67     sync_item->item_ordinal = app_item->position();
68     changed = true;
69   }
70   // TODO(stevenjb): Set page_ordinal.
71   return changed;
72 }
73
74 void GetSyncSpecificsFromSyncItem(const AppListSyncableService::SyncItem* item,
75                                   sync_pb::AppListSpecifics* specifics) {
76   DCHECK(specifics);
77   specifics->set_item_id(item->item_id);
78   specifics->set_item_type(item->item_type);
79   specifics->set_item_name(item->item_name);
80   specifics->set_parent_id(item->parent_id);
81   if (item->page_ordinal.IsValid())
82     specifics->set_page_ordinal(item->page_ordinal.ToInternalValue());
83   if (item->item_ordinal.IsValid())
84     specifics->set_item_ordinal(item->item_ordinal.ToInternalValue());
85 }
86
87 syncer::SyncData GetSyncDataFromSyncItem(
88     const AppListSyncableService::SyncItem* item) {
89   sync_pb::EntitySpecifics specifics;
90   GetSyncSpecificsFromSyncItem(item, specifics.mutable_app_list());
91   return syncer::SyncData::CreateLocalData(item->item_id,
92                                            item->item_id,
93                                            specifics);
94 }
95
96 bool AppIsDefault(ExtensionService* service, const std::string& id) {
97   return service && service->extension_prefs()->WasInstalledByDefault(id);
98 }
99
100 void UninstallExtension(ExtensionService* service, const std::string& id) {
101   if (service && service->GetInstalledExtension(id))
102     service->UninstallExtension(id, false, NULL);
103 }
104
105 bool GetAppListItemType(AppListItem* item,
106                         sync_pb::AppListSpecifics::AppListItemType* type) {
107   const char* item_type = item->GetItemType();
108   if (item_type == ExtensionAppItem::kItemType) {
109     *type = sync_pb::AppListSpecifics::TYPE_APP;
110   } else if (item_type == AppListFolderItem::kItemType) {
111     *type = sync_pb::AppListSpecifics::TYPE_FOLDER;
112   } else {
113     LOG(ERROR) << "Unrecognized model type: " << item_type;
114     return false;
115   }
116   return true;
117 }
118
119 }  // namespace
120
121 // AppListSyncableService::SyncItem
122
123 AppListSyncableService::SyncItem::SyncItem(
124     const std::string& id,
125     sync_pb::AppListSpecifics::AppListItemType type)
126     : item_id(id),
127       item_type(type) {
128 }
129
130 AppListSyncableService::SyncItem::~SyncItem() {
131 }
132
133 // AppListSyncableService::ModelObserver
134
135 class AppListSyncableService::ModelObserver : public AppListModelObserver {
136  public:
137   explicit ModelObserver(AppListSyncableService* owner)
138       : owner_(owner) {
139     DVLOG(2) << owner_ << ": ModelObserver Added";
140     owner_->model()->AddObserver(this);
141   }
142
143   virtual ~ModelObserver() {
144     owner_->model()->RemoveObserver(this);
145     DVLOG(2) << owner_ << ": ModelObserver Removed";
146   }
147
148  private:
149   // AppListModelObserver
150   virtual void OnAppListItemAdded(AppListItem* item) OVERRIDE {
151     DVLOG(2) << owner_ << " OnAppListItemAdded: " << item->ToDebugString();
152     owner_->AddOrUpdateFromSyncItem(item);
153   }
154
155   virtual void OnAppListItemWillBeDeleted(AppListItem* item) OVERRIDE {
156     DVLOG(2) << owner_ << " OnAppListItemDeleted: " << item->ToDebugString();
157     owner_->RemoveSyncItem(item->id());
158   }
159
160   virtual void OnAppListItemUpdated(AppListItem* item) OVERRIDE {
161     DVLOG(2) << owner_ << " OnAppListItemUpdated: " << item->ToDebugString();
162     owner_->UpdateSyncItem(item);
163   }
164
165   AppListSyncableService* owner_;
166
167   DISALLOW_COPY_AND_ASSIGN(ModelObserver);
168 };
169
170 // AppListSyncableService
171
172 AppListSyncableService::AppListSyncableService(
173     Profile* profile,
174     extensions::ExtensionSystem* extension_system)
175     : profile_(profile),
176       extension_system_(extension_system),
177       model_(new AppListModel) {
178   if (!extension_system) {
179     LOG(ERROR) << "AppListSyncableService created with no ExtensionSystem";
180     return;
181   }
182
183   // Note: model_observer_ is constructed after the initial sync changes are
184   // received in MergeDataAndStartSyncing(). Changes to the model before that
185   // will be synced after the initial sync occurs.
186   if (extension_system->extension_service() &&
187       extension_system->extension_service()->is_ready()) {
188     BuildModel();
189     return;
190   }
191
192   // The extensions for this profile have not yet all been loaded.
193   registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
194                  content::Source<Profile>(profile));
195 }
196
197 AppListSyncableService::~AppListSyncableService() {
198   // Remove observers.
199   model_observer_.reset();
200
201   STLDeleteContainerPairSecondPointers(sync_items_.begin(), sync_items_.end());
202 }
203
204 void AppListSyncableService::BuildModel() {
205   // For now, use the AppListControllerDelegate associated with the native
206   // desktop. TODO(stevenjb): Remove ExtensionAppModelBuilder controller
207   // dependency and move the dependent methods from AppListControllerDelegate
208   // to an extension service delegate associated with this class.
209   AppListControllerDelegate* controller = NULL;
210   AppListService* service =
211       AppListService::Get(chrome::HOST_DESKTOP_TYPE_NATIVE);
212   if (service)
213     controller = service->GetControllerDelegate();
214   apps_builder_.reset(new ExtensionAppModelBuilder(controller));
215   DCHECK(profile_);
216   // TODO(stevenjb): Correctly handle OTR profiles for Guest mode.
217   if (!profile_->IsOffTheRecord() && SyncAppListEnabled()) {
218     DVLOG(1) << this << ": AppListSyncableService: InitializeWithService.";
219     SyncStarted();
220     apps_builder_->InitializeWithService(this);
221   } else {
222     DVLOG(1) << this << ": AppListSyncableService: InitializeWithProfile.";
223     apps_builder_->InitializeWithProfile(profile_, model_.get());
224   }
225 }
226
227 void AppListSyncableService::Observe(
228     int type,
229     const content::NotificationSource& source,
230     const content::NotificationDetails& details) {
231   DCHECK_EQ(chrome::NOTIFICATION_EXTENSIONS_READY, type);
232   DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
233   registrar_.RemoveAll();
234   BuildModel();
235 }
236
237 const AppListSyncableService::SyncItem*
238 AppListSyncableService::GetSyncItem(const std::string& id) const {
239   SyncItemMap::const_iterator iter = sync_items_.find(id);
240   if (iter != sync_items_.end())
241     return iter->second;
242   return NULL;
243 }
244
245 void AppListSyncableService::AddItem(scoped_ptr<AppListItem> app_item) {
246   SyncItem* sync_item = FindOrAddSyncItem(app_item.get());
247   if (!sync_item)
248     return;  // Item is not valid.
249
250   DVLOG(1) << this << ": AddItem: " << sync_item->ToString();
251   std::string folder_id = sync_item->parent_id;
252   model_->AddItemToFolder(app_item.Pass(), folder_id);
253 }
254
255 AppListSyncableService::SyncItem* AppListSyncableService::FindOrAddSyncItem(
256     AppListItem* app_item) {
257   const std::string& item_id = app_item->id();
258   if (item_id.empty()) {
259     LOG(ERROR) << "AppListItem item with empty ID";
260     return NULL;
261   }
262   SyncItem* sync_item = FindSyncItem(item_id);
263   if (sync_item) {
264     // If there is an existing, non-REMOVE_DEFAULT entry, return it.
265     if (sync_item->item_type !=
266         sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
267       DVLOG(2) << this << ": AddItem already exists: " << sync_item->ToString();
268       return sync_item;
269     }
270
271     if (RemoveDefaultApp(app_item, sync_item))
272       return NULL;
273
274     // Fall through. The REMOVE_DEFAULT_APP entry has been deleted, now a new
275     // App entry can be added.
276   }
277
278   return CreateSyncItemFromAppItem(app_item);
279 }
280
281 AppListSyncableService::SyncItem*
282 AppListSyncableService::CreateSyncItemFromAppItem(AppListItem* app_item) {
283   sync_pb::AppListSpecifics::AppListItemType type;
284   if (!GetAppListItemType(app_item, &type))
285     return NULL;
286   SyncItem* sync_item = CreateSyncItem(app_item->id(), type);
287   UpdateSyncItemFromAppItem(app_item, sync_item);
288   SendSyncChange(sync_item, SyncChange::ACTION_ADD);
289   return sync_item;
290 }
291
292 void AppListSyncableService::AddOrUpdateFromSyncItem(AppListItem* app_item) {
293   SyncItem* sync_item = FindSyncItem(app_item->id());
294   if (sync_item) {
295     UpdateAppItemFromSyncItem(sync_item, app_item);
296     return;
297   }
298   CreateSyncItemFromAppItem(app_item);
299 }
300
301 bool AppListSyncableService::RemoveDefaultApp(AppListItem* item,
302                                               SyncItem* sync_item) {
303   CHECK_EQ(sync_item->item_type,
304            sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP);
305
306   // If there is an existing REMOVE_DEFAULT_APP entry, and the app is
307   // installed as a Default app, uninstall the app instead of adding it.
308   if (sync_item->item_type == sync_pb::AppListSpecifics::TYPE_APP &&
309       AppIsDefault(extension_system_->extension_service(), item->id())) {
310     DVLOG(1) << this << ": HandleDefaultApp: Uninstall: "
311              << sync_item->ToString();
312     UninstallExtension(extension_system_->extension_service(), item->id());
313     return true;
314   }
315
316   // Otherwise, we are adding the app as a non-default app (i.e. an app that
317   // was installed by Default and removed is getting installed explicitly by
318   // the user), so delete the REMOVE_DEFAULT_APP.
319   DeleteSyncItem(sync_item);
320   return false;
321 }
322
323 void AppListSyncableService::DeleteSyncItem(SyncItem* sync_item) {
324   if (SyncStarted()) {
325     DVLOG(2) << this << " -> SYNC DELETE: " << sync_item->ToString();
326     SyncChange sync_change(FROM_HERE, SyncChange::ACTION_DELETE,
327                            GetSyncDataFromSyncItem(sync_item));
328     sync_processor_->ProcessSyncChanges(
329         FROM_HERE, syncer::SyncChangeList(1, sync_change));
330   }
331   std::string item_id = sync_item->item_id;
332   delete sync_item;
333   sync_items_.erase(item_id);
334 }
335
336 void AppListSyncableService::UpdateSyncItem(AppListItem* app_item) {
337   SyncItem* sync_item = FindSyncItem(app_item->id());
338   if (!sync_item) {
339     LOG(ERROR) << "UpdateItem: no sync item: " << app_item->id();
340     return;
341   }
342   bool changed = UpdateSyncItemFromAppItem(app_item, sync_item);
343   if (!changed) {
344     DVLOG(2) << this << " - Update: SYNC NO CHANGE: " << sync_item->ToString();
345     return;
346   }
347   SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
348 }
349
350 void AppListSyncableService::RemoveItem(const std::string& id) {
351   RemoveSyncItem(id);
352   model_->DeleteItem(id);
353   PruneEmptySyncFolders();
354 }
355
356 void AppListSyncableService::RemoveSyncItem(const std::string& id) {
357   DVLOG(2) << this << ": RemoveSyncItem: " << id.substr(0, 8);
358   SyncItemMap::iterator iter = sync_items_.find(id);
359   if (iter == sync_items_.end()) {
360     DVLOG(2) << this << " : RemoveSyncItem: No Item.";
361     return;
362   }
363
364   // Check for existing RemoveDefault sync item.
365   SyncItem* sync_item = iter->second;
366   sync_pb::AppListSpecifics::AppListItemType type = sync_item->item_type;
367   if (type == sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
368     // RemoveDefault item exists, just return.
369     DVLOG(2) << this << " : RemoveDefault Item exists.";
370     return;
371   }
372
373   if (type == sync_pb::AppListSpecifics::TYPE_APP &&
374       AppIsDefault(extension_system_->extension_service(), id)) {
375     // This is a Default app; update the entry to a REMOVE_DEFAULT entry. This
376     // will overwrite any existing entry for the item.
377     DVLOG(2) << this << " -> SYNC UPDATE: REMOVE_DEFAULT: "
378              << sync_item->item_id;
379     sync_item->item_type = sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP;
380     SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
381     return;
382   }
383
384   DeleteSyncItem(sync_item);
385 }
386
387 void AppListSyncableService::ResolveFolderPositions() {
388   if (!app_list::switches::IsFolderUIEnabled())
389     return;
390
391   for (SyncItemMap::iterator iter = sync_items_.begin();
392        iter != sync_items_.end(); ++iter) {
393     SyncItem* sync_item = iter->second;
394     if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
395       continue;
396     AppListItem* app_item = model_->FindItem(sync_item->item_id);
397     if (!app_item)
398       continue;
399     UpdateAppItemFromSyncItem(sync_item, app_item);
400   }
401 }
402
403 void AppListSyncableService::PruneEmptySyncFolders() {
404   if (!app_list::switches::IsFolderUIEnabled())
405     return;
406
407   std::set<std::string> parent_ids;
408   for (SyncItemMap::iterator iter = sync_items_.begin();
409        iter != sync_items_.end(); ++iter) {
410     parent_ids.insert(iter->second->parent_id);
411   }
412   for (SyncItemMap::iterator iter = sync_items_.begin();
413        iter != sync_items_.end(); ) {
414     SyncItem* sync_item = (iter++)->second;
415     if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
416       continue;
417     if (!ContainsKey(parent_ids, sync_item->item_id))
418       DeleteSyncItem(sync_item);
419   }
420 }
421
422 // AppListSyncableService syncer::SyncableService
423
424 syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing(
425     syncer::ModelType type,
426     const syncer::SyncDataList& initial_sync_data,
427     scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
428     scoped_ptr<syncer::SyncErrorFactory> error_handler) {
429   DCHECK(!sync_processor_.get());
430   DCHECK(sync_processor.get());
431   DCHECK(error_handler.get());
432
433   sync_processor_ = sync_processor.Pass();
434   sync_error_handler_ = error_handler.Pass();
435
436   syncer::SyncMergeResult result = syncer::SyncMergeResult(type);
437   result.set_num_items_before_association(sync_items_.size());
438   DVLOG(1) << this << ": MergeDataAndStartSyncing: "
439            << initial_sync_data.size();
440
441   // Copy all sync items to |unsynced_items|.
442   std::set<std::string> unsynced_items;
443   for (SyncItemMap::const_iterator iter = sync_items_.begin();
444        iter != sync_items_.end(); ++iter) {
445     unsynced_items.insert(iter->first);
446   }
447
448   // Create SyncItem entries for initial_sync_data.
449   size_t new_items = 0, updated_items = 0;
450   for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin();
451        iter != initial_sync_data.end(); ++iter) {
452     const syncer::SyncData& data = *iter;
453     DVLOG(2) << this << "  Initial Sync Item: "
454              << data.GetSpecifics().app_list().item_id()
455              << " Type: " << data.GetSpecifics().app_list().item_type();
456     DCHECK_EQ(syncer::APP_LIST, data.GetDataType());
457     if (ProcessSyncItemSpecifics(data.GetSpecifics().app_list()))
458       ++new_items;
459     else
460       ++updated_items;
461     unsynced_items.erase(data.GetSpecifics().app_list().item_id());
462   }
463
464   result.set_num_items_after_association(sync_items_.size());
465   result.set_num_items_added(new_items);
466   result.set_num_items_deleted(0);
467   result.set_num_items_modified(updated_items);
468
469   // Send unsynced items. Does not affect |result|.
470   syncer::SyncChangeList change_list;
471   for (std::set<std::string>::iterator iter = unsynced_items.begin();
472        iter != unsynced_items.end(); ++iter) {
473     SyncItem* sync_item = FindSyncItem(*iter);
474     DVLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
475     change_list.push_back(SyncChange(FROM_HERE,  SyncChange::ACTION_ADD,
476                                      GetSyncDataFromSyncItem(sync_item)));
477   }
478   sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
479
480   // Adding items may have created folders without setting their positions
481   // since we haven't started observing the item list yet. Resolve those.
482   ResolveFolderPositions();
483
484   // Start observing app list model changes.
485   model_observer_.reset(new ModelObserver(this));
486
487   return result;
488 }
489
490 void AppListSyncableService::StopSyncing(syncer::ModelType type) {
491   DCHECK_EQ(type, syncer::APP_LIST);
492
493   sync_processor_.reset();
494   sync_error_handler_.reset();
495 }
496
497 syncer::SyncDataList AppListSyncableService::GetAllSyncData(
498     syncer::ModelType type) const {
499   DCHECK_EQ(syncer::APP_LIST, type);
500
501   DVLOG(1) << this << ": GetAllSyncData: " << sync_items_.size();
502   syncer::SyncDataList list;
503   for (SyncItemMap::const_iterator iter = sync_items_.begin();
504        iter != sync_items_.end(); ++iter) {
505     DVLOG(2) << this << " -> SYNC: " << iter->second->ToString();
506     list.push_back(GetSyncDataFromSyncItem(iter->second));
507   }
508   return list;
509 }
510
511 syncer::SyncError AppListSyncableService::ProcessSyncChanges(
512     const tracked_objects::Location& from_here,
513     const syncer::SyncChangeList& change_list) {
514   if (!sync_processor_.get()) {
515     return syncer::SyncError(FROM_HERE,
516                              syncer::SyncError::DATATYPE_ERROR,
517                              "App List syncable service is not started.",
518                              syncer::APP_LIST);
519   }
520
521   // Don't observe the model while processing incoming sync changes.
522   model_observer_.reset();
523
524   DVLOG(1) << this << ": ProcessSyncChanges: " << change_list.size();
525   for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
526        iter != change_list.end(); ++iter) {
527     const SyncChange& change = *iter;
528     DVLOG(2) << this << "  Change: "
529              << change.sync_data().GetSpecifics().app_list().item_id()
530              << " (" << change.change_type() << ")";
531     if (change.change_type() == SyncChange::ACTION_ADD ||
532         change.change_type() == SyncChange::ACTION_UPDATE) {
533       ProcessSyncItemSpecifics(change.sync_data().GetSpecifics().app_list());
534     } else if (change.change_type() == SyncChange::ACTION_DELETE) {
535       DeleteSyncItemSpecifics(change.sync_data().GetSpecifics().app_list());
536     } else {
537       LOG(ERROR) << "Invalid sync change";
538     }
539   }
540
541   // Continue observing app list model changes.
542   model_observer_.reset(new ModelObserver(this));
543
544   return syncer::SyncError();
545 }
546
547 // AppListSyncableService private
548
549 bool AppListSyncableService::ProcessSyncItemSpecifics(
550     const sync_pb::AppListSpecifics& specifics) {
551   const std::string& item_id = specifics.item_id();
552   if (item_id.empty()) {
553     LOG(ERROR) << "AppList item with empty ID";
554     return false;
555   }
556   SyncItem* sync_item = FindSyncItem(item_id);
557   if (sync_item) {
558     // If an item of the same type exists, update it.
559     if (sync_item->item_type == specifics.item_type()) {
560       UpdateSyncItemFromSync(specifics, sync_item);
561       ProcessExistingSyncItem(sync_item);
562       DVLOG(2) << this << " <- SYNC UPDATE: " << sync_item->ToString();
563       return false;
564     }
565     // Otherwise, one of the entries should be TYPE_REMOVE_DEFAULT_APP.
566     if (sync_item->item_type !=
567         sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP &&
568         specifics.item_type() !=
569         sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
570       LOG(ERROR) << "Synced item type: " << specifics.item_type()
571                  << " != existing sync item type: " << sync_item->item_type
572                  << " Deleting item from model!";
573       model_->DeleteItem(item_id);
574     }
575     DVLOG(2) << this << " - ProcessSyncItem: Delete existing entry: "
576              << sync_item->ToString();
577     delete sync_item;
578     sync_items_.erase(item_id);
579   }
580
581   sync_item = CreateSyncItem(item_id, specifics.item_type());
582   UpdateSyncItemFromSync(specifics, sync_item);
583   ProcessNewSyncItem(sync_item);
584   DVLOG(2) << this << " <- SYNC ADD: " << sync_item->ToString();
585   return true;
586 }
587
588 void AppListSyncableService::ProcessNewSyncItem(SyncItem* sync_item) {
589   DVLOG(2) << "ProcessNewSyncItem: " << sync_item->ToString();
590   switch (sync_item->item_type) {
591     case sync_pb::AppListSpecifics::TYPE_APP: {
592       // New apps are added through ExtensionAppModelBuilder.
593       // TODO(stevenjb): Determine how to handle app items in sync that
594       // are not installed (e.g. default / OEM apps).
595       return;
596     }
597     case sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP: {
598       DVLOG(1) << this << ": Uninstall: " << sync_item->ToString();
599       UninstallExtension(extension_system_->extension_service(),
600                          sync_item->item_id);
601       return;
602     }
603     case sync_pb::AppListSpecifics::TYPE_FOLDER: {
604       AppListItem* app_item = model_->FindItem(sync_item->item_id);
605       if (!app_item)
606         return;  // Don't create new folders here, the model will do that.
607       UpdateAppItemFromSyncItem(sync_item, app_item);
608       return;
609     }
610     case sync_pb::AppListSpecifics::TYPE_URL: {
611       // TODO(stevenjb): Implement
612       LOG(WARNING) << "TYPE_URL not supported";
613       return;
614     }
615   }
616   NOTREACHED() << "Unrecognized sync item type: " << sync_item->ToString();
617 }
618
619 void AppListSyncableService::ProcessExistingSyncItem(SyncItem* sync_item) {
620   if (sync_item->item_type ==
621       sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
622     return;
623   }
624   DVLOG(2) << "ProcessExistingSyncItem: " << sync_item->ToString();
625   AppListItem* app_item = model_->FindItem(sync_item->item_id);
626   DVLOG(2) << " AppItem: " << app_item->ToDebugString();
627   if (!app_item) {
628     LOG(ERROR) << "Item not found in model: " << sync_item->ToString();
629     return;
630   }
631   // This is the only place where sync can cause an item to change folders.
632   if (app_list::switches::IsFolderUIEnabled() &&
633       app_item->folder_id() != sync_item->parent_id) {
634     DVLOG(2) << " Moving Item To Folder: " << sync_item->parent_id;
635     model_->MoveItemToFolder(app_item, sync_item->parent_id);
636   }
637   UpdateAppItemFromSyncItem(sync_item, app_item);
638 }
639
640 void AppListSyncableService::UpdateAppItemFromSyncItem(
641     const AppListSyncableService::SyncItem* sync_item,
642     AppListItem* app_item) {
643   if (!app_item->position().Equals(sync_item->item_ordinal))
644     model_->SetItemPosition(app_item, sync_item->item_ordinal);
645 }
646
647 bool AppListSyncableService::SyncStarted() {
648   if (sync_processor_.get())
649     return true;
650   if (flare_.is_null()) {
651     DVLOG(2) << this << ": SyncStarted: Flare.";
652     flare_ = sync_start_util::GetFlareForSyncableService(profile_->GetPath());
653     flare_.Run(syncer::APP_LIST);
654   }
655   return false;
656 }
657
658 void AppListSyncableService::SendSyncChange(
659     SyncItem* sync_item,
660     SyncChange::SyncChangeType sync_change_type) {
661   if (!SyncStarted()) {
662     DVLOG(2) << this << " - SendSyncChange: SYNC NOT STARTED: "
663              << sync_item->ToString();
664     return;
665   }
666   if (sync_change_type == SyncChange::ACTION_ADD)
667     DVLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
668   else
669     DVLOG(2) << this << " -> SYNC UPDATE: " << sync_item->ToString();
670   SyncChange sync_change(FROM_HERE, sync_change_type,
671                          GetSyncDataFromSyncItem(sync_item));
672   sync_processor_->ProcessSyncChanges(
673       FROM_HERE, syncer::SyncChangeList(1, sync_change));
674 }
675
676 AppListSyncableService::SyncItem*
677 AppListSyncableService::FindSyncItem(const std::string& item_id) {
678   SyncItemMap::iterator iter = sync_items_.find(item_id);
679   if (iter == sync_items_.end())
680     return NULL;
681   return iter->second;
682 }
683
684 AppListSyncableService::SyncItem*
685 AppListSyncableService::CreateSyncItem(
686     const std::string& item_id,
687     sync_pb::AppListSpecifics::AppListItemType item_type) {
688   DCHECK(!ContainsKey(sync_items_, item_id));
689   SyncItem* sync_item = new SyncItem(item_id, item_type);
690   sync_items_[item_id] = sync_item;
691   return sync_item;
692 }
693
694 void AppListSyncableService::DeleteSyncItemSpecifics(
695     const sync_pb::AppListSpecifics& specifics) {
696   const std::string& item_id = specifics.item_id();
697   if (item_id.empty()) {
698     LOG(ERROR) << "Delete AppList item with empty ID";
699     return;
700   }
701   DVLOG(2) << this << ": DeleteSyncItemSpecifics: " << item_id.substr(0, 8);
702   SyncItemMap::iterator iter = sync_items_.find(item_id);
703   if (iter == sync_items_.end())
704     return;
705   sync_pb::AppListSpecifics::AppListItemType item_type =
706       iter->second->item_type;
707   DVLOG(2) << this << " <- SYNC DELETE: " << iter->second->ToString();
708   delete iter->second;
709   sync_items_.erase(iter);
710   // Only delete apps from the model. Folders will be deleted when all
711   // children have been deleted.
712   if (item_type == sync_pb::AppListSpecifics::TYPE_APP)
713     model_->DeleteItem(item_id);
714 }
715
716 std::string AppListSyncableService::SyncItem::ToString() const {
717   std::string res = item_id.substr(0, 8);
718   if (item_type == sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
719     res += " { RemoveDefault }";
720   } else {
721     res += " { " + item_name + " }";
722     res += " [" + item_ordinal.ToDebugString() + "]";
723     if (!parent_id.empty())
724       res += " <" + parent_id.substr(0, 8) + ">";
725   }
726   return res;
727 }
728
729 }  // namespace app_list