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.
5 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
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"
29 using syncer::SyncChange;
35 bool SyncAppListEnabled() {
36 return CommandLine::ForCurrentProcess()->HasSwitch(
37 ::switches::kEnableSyncAppList);
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());
52 bool UpdateSyncItemFromAppItem(const AppListItem* app_item,
53 AppListSyncableService::SyncItem* sync_item) {
54 DCHECK_EQ(sync_item->item_id, app_item->id());
56 if (app_list::switches::IsFolderUIEnabled() &&
57 sync_item->parent_id != app_item->folder_id()) {
58 sync_item->parent_id = app_item->folder_id();
61 if (sync_item->item_name != app_item->title()) {
62 sync_item->item_name = app_item->title();
65 if (!sync_item->item_ordinal.IsValid() ||
66 !app_item->position().Equals(sync_item->item_ordinal)) {
67 sync_item->item_ordinal = app_item->position();
70 // TODO(stevenjb): Set page_ordinal.
74 void GetSyncSpecificsFromSyncItem(const AppListSyncableService::SyncItem* item,
75 sync_pb::AppListSpecifics* 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());
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,
96 bool AppIsDefault(ExtensionService* service, const std::string& id) {
97 return service && service->extension_prefs()->WasInstalledByDefault(id);
100 void UninstallExtension(ExtensionService* service, const std::string& id) {
101 if (service && service->GetInstalledExtension(id))
102 service->UninstallExtension(id, false, NULL);
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;
113 LOG(ERROR) << "Unrecognized model type: " << item_type;
121 // AppListSyncableService::SyncItem
123 AppListSyncableService::SyncItem::SyncItem(
124 const std::string& id,
125 sync_pb::AppListSpecifics::AppListItemType type)
130 AppListSyncableService::SyncItem::~SyncItem() {
133 // AppListSyncableService::ModelObserver
135 class AppListSyncableService::ModelObserver : public AppListModelObserver {
137 explicit ModelObserver(AppListSyncableService* owner)
139 DVLOG(2) << owner_ << ": ModelObserver Added";
140 owner_->model()->AddObserver(this);
143 virtual ~ModelObserver() {
144 owner_->model()->RemoveObserver(this);
145 DVLOG(2) << owner_ << ": ModelObserver Removed";
149 // AppListModelObserver
150 virtual void OnAppListItemAdded(AppListItem* item) OVERRIDE {
151 DVLOG(2) << owner_ << " OnAppListItemAdded: " << item->ToDebugString();
152 owner_->AddOrUpdateFromSyncItem(item);
155 virtual void OnAppListItemWillBeDeleted(AppListItem* item) OVERRIDE {
156 DVLOG(2) << owner_ << " OnAppListItemDeleted: " << item->ToDebugString();
157 owner_->RemoveSyncItem(item->id());
160 virtual void OnAppListItemUpdated(AppListItem* item) OVERRIDE {
161 DVLOG(2) << owner_ << " OnAppListItemUpdated: " << item->ToDebugString();
162 owner_->UpdateSyncItem(item);
165 AppListSyncableService* owner_;
167 DISALLOW_COPY_AND_ASSIGN(ModelObserver);
170 // AppListSyncableService
172 AppListSyncableService::AppListSyncableService(
174 extensions::ExtensionSystem* extension_system)
176 extension_system_(extension_system),
177 model_(new AppListModel) {
178 if (!extension_system) {
179 LOG(ERROR) << "AppListSyncableService created with no ExtensionSystem";
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()) {
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));
197 AppListSyncableService::~AppListSyncableService() {
199 model_observer_.reset();
201 STLDeleteContainerPairSecondPointers(sync_items_.begin(), sync_items_.end());
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);
213 controller = service->GetControllerDelegate();
214 apps_builder_.reset(new ExtensionAppModelBuilder(controller));
216 // TODO(stevenjb): Correctly handle OTR profiles for Guest mode.
217 if (!profile_->IsOffTheRecord() && SyncAppListEnabled()) {
218 DVLOG(1) << this << ": AppListSyncableService: InitializeWithService.";
220 apps_builder_->InitializeWithService(this);
222 DVLOG(1) << this << ": AppListSyncableService: InitializeWithProfile.";
223 apps_builder_->InitializeWithProfile(profile_, model_.get());
227 void AppListSyncableService::Observe(
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();
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())
245 void AppListSyncableService::AddItem(scoped_ptr<AppListItem> app_item) {
246 SyncItem* sync_item = FindOrAddSyncItem(app_item.get());
248 return; // Item is not valid.
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);
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";
262 SyncItem* sync_item = FindSyncItem(item_id);
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();
271 if (RemoveDefaultApp(app_item, sync_item))
274 // Fall through. The REMOVE_DEFAULT_APP entry has been deleted, now a new
275 // App entry can be added.
278 return CreateSyncItemFromAppItem(app_item);
281 AppListSyncableService::SyncItem*
282 AppListSyncableService::CreateSyncItemFromAppItem(AppListItem* app_item) {
283 sync_pb::AppListSpecifics::AppListItemType type;
284 if (!GetAppListItemType(app_item, &type))
286 SyncItem* sync_item = CreateSyncItem(app_item->id(), type);
287 UpdateSyncItemFromAppItem(app_item, sync_item);
288 SendSyncChange(sync_item, SyncChange::ACTION_ADD);
292 void AppListSyncableService::AddOrUpdateFromSyncItem(AppListItem* app_item) {
293 SyncItem* sync_item = FindSyncItem(app_item->id());
295 UpdateAppItemFromSyncItem(sync_item, app_item);
298 CreateSyncItemFromAppItem(app_item);
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);
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());
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);
323 void AppListSyncableService::DeleteSyncItem(SyncItem* sync_item) {
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));
331 std::string item_id = sync_item->item_id;
333 sync_items_.erase(item_id);
336 void AppListSyncableService::UpdateSyncItem(AppListItem* app_item) {
337 SyncItem* sync_item = FindSyncItem(app_item->id());
339 LOG(ERROR) << "UpdateItem: no sync item: " << app_item->id();
342 bool changed = UpdateSyncItemFromAppItem(app_item, sync_item);
344 DVLOG(2) << this << " - Update: SYNC NO CHANGE: " << sync_item->ToString();
347 SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
350 void AppListSyncableService::RemoveItem(const std::string& id) {
352 model_->DeleteItem(id);
353 PruneEmptySyncFolders();
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.";
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.";
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);
384 DeleteSyncItem(sync_item);
387 void AppListSyncableService::ResolveFolderPositions() {
388 if (!app_list::switches::IsFolderUIEnabled())
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)
396 AppListItem* app_item = model_->FindItem(sync_item->item_id);
399 UpdateAppItemFromSyncItem(sync_item, app_item);
403 void AppListSyncableService::PruneEmptySyncFolders() {
404 if (!app_list::switches::IsFolderUIEnabled())
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);
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)
417 if (!ContainsKey(parent_ids, sync_item->item_id))
418 DeleteSyncItem(sync_item);
422 // AppListSyncableService syncer::SyncableService
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());
433 sync_processor_ = sync_processor.Pass();
434 sync_error_handler_ = error_handler.Pass();
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();
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);
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()))
461 unsynced_items.erase(data.GetSpecifics().app_list().item_id());
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);
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)));
478 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
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();
484 // Start observing app list model changes.
485 model_observer_.reset(new ModelObserver(this));
490 void AppListSyncableService::StopSyncing(syncer::ModelType type) {
491 DCHECK_EQ(type, syncer::APP_LIST);
493 sync_processor_.reset();
494 sync_error_handler_.reset();
497 syncer::SyncDataList AppListSyncableService::GetAllSyncData(
498 syncer::ModelType type) const {
499 DCHECK_EQ(syncer::APP_LIST, type);
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));
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.",
521 // Don't observe the model while processing incoming sync changes.
522 model_observer_.reset();
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());
537 LOG(ERROR) << "Invalid sync change";
541 // Continue observing app list model changes.
542 model_observer_.reset(new ModelObserver(this));
544 return syncer::SyncError();
547 // AppListSyncableService private
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";
556 SyncItem* sync_item = FindSyncItem(item_id);
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();
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);
575 DVLOG(2) << this << " - ProcessSyncItem: Delete existing entry: "
576 << sync_item->ToString();
578 sync_items_.erase(item_id);
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();
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).
597 case sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP: {
598 DVLOG(1) << this << ": Uninstall: " << sync_item->ToString();
599 UninstallExtension(extension_system_->extension_service(),
603 case sync_pb::AppListSpecifics::TYPE_FOLDER: {
604 AppListItem* app_item = model_->FindItem(sync_item->item_id);
606 return; // Don't create new folders here, the model will do that.
607 UpdateAppItemFromSyncItem(sync_item, app_item);
610 case sync_pb::AppListSpecifics::TYPE_URL: {
611 // TODO(stevenjb): Implement
612 LOG(WARNING) << "TYPE_URL not supported";
616 NOTREACHED() << "Unrecognized sync item type: " << sync_item->ToString();
619 void AppListSyncableService::ProcessExistingSyncItem(SyncItem* sync_item) {
620 if (sync_item->item_type ==
621 sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
624 DVLOG(2) << "ProcessExistingSyncItem: " << sync_item->ToString();
625 AppListItem* app_item = model_->FindItem(sync_item->item_id);
626 DVLOG(2) << " AppItem: " << app_item->ToDebugString();
628 LOG(ERROR) << "Item not found in model: " << sync_item->ToString();
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);
637 UpdateAppItemFromSyncItem(sync_item, app_item);
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);
647 bool AppListSyncableService::SyncStarted() {
648 if (sync_processor_.get())
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);
658 void AppListSyncableService::SendSyncChange(
660 SyncChange::SyncChangeType sync_change_type) {
661 if (!SyncStarted()) {
662 DVLOG(2) << this << " - SendSyncChange: SYNC NOT STARTED: "
663 << sync_item->ToString();
666 if (sync_change_type == SyncChange::ACTION_ADD)
667 DVLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
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));
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())
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;
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";
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())
705 sync_pb::AppListSpecifics::AppListItemType item_type =
706 iter->second->item_type;
707 DVLOG(2) << this << " <- SYNC DELETE: " << iter->second->ToString();
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);
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 }";
721 res += " { " + item_name + " }";
722 res += " [" + item_ordinal.ToDebugString() + "]";
723 if (!parent_id.empty())
724 res += " <" + parent_id.substr(0, 8) + ">";
729 } // namespace app_list