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 "grit/generated_resources.h"
20 #include "sync/api/sync_change_processor.h"
21 #include "sync/api/sync_data.h"
22 #include "sync/api/sync_merge_result.h"
23 #include "sync/protocol/sync.pb.h"
24 #include "ui/app_list/app_list_folder_item.h"
25 #include "ui/app_list/app_list_item.h"
26 #include "ui/app_list/app_list_model.h"
27 #include "ui/app_list/app_list_model_observer.h"
28 #include "ui/app_list/app_list_switches.h"
29 #include "ui/base/l10n/l10n_util.h"
31 using syncer::SyncChange;
37 const char kOemFolderId[] = "ddb1da55-d478-4243-8642-56d3041f0263";
39 bool SyncAppListEnabled() {
40 return !CommandLine::ForCurrentProcess()->HasSwitch(
41 ::switches::kDisableSyncAppList);
44 void UpdateSyncItemFromSync(const sync_pb::AppListSpecifics& specifics,
45 AppListSyncableService::SyncItem* item) {
46 DCHECK_EQ(item->item_id, specifics.item_id());
47 item->item_type = specifics.item_type();
48 item->item_name = specifics.item_name();
49 item->parent_id = specifics.parent_id();
50 if (!specifics.page_ordinal().empty())
51 item->page_ordinal = syncer::StringOrdinal(specifics.page_ordinal());
52 if (!specifics.item_ordinal().empty())
53 item->item_ordinal = syncer::StringOrdinal(specifics.item_ordinal());
56 bool UpdateSyncItemFromAppItem(const AppListItem* app_item,
57 AppListSyncableService::SyncItem* sync_item) {
58 DCHECK_EQ(sync_item->item_id, app_item->id());
60 if (app_list::switches::IsFolderUIEnabled() &&
61 sync_item->parent_id != app_item->folder_id()) {
62 sync_item->parent_id = app_item->folder_id();
65 if (sync_item->item_name != app_item->name()) {
66 sync_item->item_name = app_item->name();
69 if (!sync_item->item_ordinal.IsValid() ||
70 !app_item->position().Equals(sync_item->item_ordinal)) {
71 sync_item->item_ordinal = app_item->position();
74 // TODO(stevenjb): Set page_ordinal.
78 void GetSyncSpecificsFromSyncItem(const AppListSyncableService::SyncItem* item,
79 sync_pb::AppListSpecifics* specifics) {
81 specifics->set_item_id(item->item_id);
82 specifics->set_item_type(item->item_type);
83 specifics->set_item_name(item->item_name);
84 specifics->set_parent_id(item->parent_id);
85 if (item->page_ordinal.IsValid())
86 specifics->set_page_ordinal(item->page_ordinal.ToInternalValue());
87 if (item->item_ordinal.IsValid())
88 specifics->set_item_ordinal(item->item_ordinal.ToInternalValue());
91 syncer::SyncData GetSyncDataFromSyncItem(
92 const AppListSyncableService::SyncItem* item) {
93 sync_pb::EntitySpecifics specifics;
94 GetSyncSpecificsFromSyncItem(item, specifics.mutable_app_list());
95 return syncer::SyncData::CreateLocalData(item->item_id,
100 bool AppIsDefault(ExtensionService* service, const std::string& id) {
101 return service && extensions::ExtensionPrefs::Get(service->profile())
102 ->WasInstalledByDefault(id);
105 void UninstallExtension(ExtensionService* service, const std::string& id) {
106 if (service && service->GetInstalledExtension(id))
107 service->UninstallExtension(id, false, NULL);
110 bool GetAppListItemType(AppListItem* item,
111 sync_pb::AppListSpecifics::AppListItemType* type) {
112 const char* item_type = item->GetItemType();
113 if (item_type == ExtensionAppItem::kItemType) {
114 *type = sync_pb::AppListSpecifics::TYPE_APP;
115 } else if (item_type == AppListFolderItem::kItemType) {
116 *type = sync_pb::AppListSpecifics::TYPE_FOLDER;
118 LOG(ERROR) << "Unrecognized model type: " << item_type;
126 // AppListSyncableService::SyncItem
128 AppListSyncableService::SyncItem::SyncItem(
129 const std::string& id,
130 sync_pb::AppListSpecifics::AppListItemType type)
135 AppListSyncableService::SyncItem::~SyncItem() {
138 // AppListSyncableService::ModelObserver
140 class AppListSyncableService::ModelObserver : public AppListModelObserver {
142 explicit ModelObserver(AppListSyncableService* owner)
144 DVLOG(2) << owner_ << ": ModelObserver Added";
145 owner_->model()->AddObserver(this);
148 virtual ~ModelObserver() {
149 owner_->model()->RemoveObserver(this);
150 DVLOG(2) << owner_ << ": ModelObserver Removed";
154 // AppListModelObserver
155 virtual void OnAppListItemAdded(AppListItem* item) OVERRIDE {
156 DVLOG(2) << owner_ << " OnAppListItemAdded: " << item->ToDebugString();
157 owner_->AddOrUpdateFromSyncItem(item);
160 virtual void OnAppListItemWillBeDeleted(AppListItem* item) OVERRIDE {
161 DVLOG(2) << owner_ << " OnAppListItemDeleted: " << item->ToDebugString();
162 owner_->RemoveSyncItem(item->id());
165 virtual void OnAppListItemUpdated(AppListItem* item) OVERRIDE {
166 DVLOG(2) << owner_ << " OnAppListItemUpdated: " << item->ToDebugString();
167 owner_->UpdateSyncItem(item);
170 AppListSyncableService* owner_;
172 DISALLOW_COPY_AND_ASSIGN(ModelObserver);
175 // AppListSyncableService
177 AppListSyncableService::AppListSyncableService(
179 extensions::ExtensionSystem* extension_system)
181 extension_system_(extension_system),
182 model_(new AppListModel) {
183 if (!extension_system) {
184 LOG(ERROR) << "AppListSyncableService created with no ExtensionSystem";
189 l10n_util::GetStringUTF8(IDS_APP_LIST_OEM_DEFAULT_FOLDER_NAME);
191 // Note: model_observer_ is constructed after the initial sync changes are
192 // received in MergeDataAndStartSyncing(). Changes to the model before that
193 // will be synced after the initial sync occurs.
194 if (extension_system->extension_service() &&
195 extension_system->extension_service()->is_ready()) {
200 // The extensions for this profile have not yet all been loaded.
201 registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
202 content::Source<Profile>(profile));
205 AppListSyncableService::~AppListSyncableService() {
207 model_observer_.reset();
209 STLDeleteContainerPairSecondPointers(sync_items_.begin(), sync_items_.end());
212 void AppListSyncableService::BuildModel() {
213 // For now, use the AppListControllerDelegate associated with the native
214 // desktop. TODO(stevenjb): Remove ExtensionAppModelBuilder controller
215 // dependency and move the dependent methods from AppListControllerDelegate
216 // to an extension service delegate associated with this class.
217 AppListControllerDelegate* controller = NULL;
218 AppListService* service =
219 AppListService::Get(chrome::HOST_DESKTOP_TYPE_NATIVE);
221 controller = service->GetControllerDelegate();
222 apps_builder_.reset(new ExtensionAppModelBuilder(controller));
224 // TODO(stevenjb): Correctly handle OTR profiles for Guest mode.
225 if (!profile_->IsOffTheRecord() && SyncAppListEnabled()) {
226 DVLOG(1) << this << ": AppListSyncableService: InitializeWithService.";
228 apps_builder_->InitializeWithService(this);
230 DVLOG(1) << this << ": AppListSyncableService: InitializeWithProfile.";
231 apps_builder_->InitializeWithProfile(profile_, model_.get());
235 void AppListSyncableService::Observe(
237 const content::NotificationSource& source,
238 const content::NotificationDetails& details) {
239 DCHECK_EQ(chrome::NOTIFICATION_EXTENSIONS_READY, type);
240 DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
241 registrar_.RemoveAll();
245 const AppListSyncableService::SyncItem*
246 AppListSyncableService::GetSyncItem(const std::string& id) const {
247 SyncItemMap::const_iterator iter = sync_items_.find(id);
248 if (iter != sync_items_.end())
253 void AppListSyncableService::SetOemFolderName(const std::string& name) {
254 oem_folder_name_ = name;
255 AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
257 model_->SetItemName(oem_folder, oem_folder_name_);
260 void AppListSyncableService::AddItem(scoped_ptr<AppListItem> app_item) {
261 SyncItem* sync_item = FindOrAddSyncItem(app_item.get());
263 return; // Item is not valid.
265 std::string folder_id;
266 if (AppIsOem(app_item->id())) {
267 folder_id = FindOrCreateOemFolder();
268 DVLOG(1) << this << ": AddItem to OEM folder: " << sync_item->ToString();
270 folder_id = sync_item->parent_id;
271 DVLOG(1) << this << ": AddItem: " << sync_item->ToString()
272 << " Folder: '" << folder_id << "'";
274 model_->AddItemToFolder(app_item.Pass(), folder_id);
277 AppListSyncableService::SyncItem* AppListSyncableService::FindOrAddSyncItem(
278 AppListItem* app_item) {
279 const std::string& item_id = app_item->id();
280 if (item_id.empty()) {
281 LOG(ERROR) << "AppListItem item with empty ID";
284 SyncItem* sync_item = FindSyncItem(item_id);
286 // If there is an existing, non-REMOVE_DEFAULT entry, return it.
287 if (sync_item->item_type !=
288 sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
289 DVLOG(2) << this << ": AddItem already exists: " << sync_item->ToString();
293 if (RemoveDefaultApp(app_item, sync_item))
296 // Fall through. The REMOVE_DEFAULT_APP entry has been deleted, now a new
297 // App entry can be added.
300 return CreateSyncItemFromAppItem(app_item);
303 AppListSyncableService::SyncItem*
304 AppListSyncableService::CreateSyncItemFromAppItem(AppListItem* app_item) {
305 sync_pb::AppListSpecifics::AppListItemType type;
306 if (!GetAppListItemType(app_item, &type))
308 SyncItem* sync_item = CreateSyncItem(app_item->id(), type);
309 UpdateSyncItemFromAppItem(app_item, sync_item);
310 SendSyncChange(sync_item, SyncChange::ACTION_ADD);
314 void AppListSyncableService::AddOrUpdateFromSyncItem(AppListItem* app_item) {
315 SyncItem* sync_item = FindSyncItem(app_item->id());
317 UpdateAppItemFromSyncItem(sync_item, app_item);
320 CreateSyncItemFromAppItem(app_item);
323 bool AppListSyncableService::RemoveDefaultApp(AppListItem* item,
324 SyncItem* sync_item) {
325 CHECK_EQ(sync_item->item_type,
326 sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP);
328 // If there is an existing REMOVE_DEFAULT_APP entry, and the app is
329 // installed as a Default app, uninstall the app instead of adding it.
330 if (sync_item->item_type == sync_pb::AppListSpecifics::TYPE_APP &&
331 AppIsDefault(extension_system_->extension_service(), item->id())) {
332 DVLOG(1) << this << ": HandleDefaultApp: Uninstall: "
333 << sync_item->ToString();
334 UninstallExtension(extension_system_->extension_service(), item->id());
338 // Otherwise, we are adding the app as a non-default app (i.e. an app that
339 // was installed by Default and removed is getting installed explicitly by
340 // the user), so delete the REMOVE_DEFAULT_APP.
341 DeleteSyncItem(sync_item);
345 void AppListSyncableService::DeleteSyncItem(SyncItem* sync_item) {
347 DVLOG(2) << this << " -> SYNC DELETE: " << sync_item->ToString();
348 SyncChange sync_change(FROM_HERE, SyncChange::ACTION_DELETE,
349 GetSyncDataFromSyncItem(sync_item));
350 sync_processor_->ProcessSyncChanges(
351 FROM_HERE, syncer::SyncChangeList(1, sync_change));
353 std::string item_id = sync_item->item_id;
355 sync_items_.erase(item_id);
358 void AppListSyncableService::UpdateSyncItem(AppListItem* app_item) {
359 SyncItem* sync_item = FindSyncItem(app_item->id());
361 LOG(ERROR) << "UpdateItem: no sync item: " << app_item->id();
364 bool changed = UpdateSyncItemFromAppItem(app_item, sync_item);
366 DVLOG(2) << this << " - Update: SYNC NO CHANGE: " << sync_item->ToString();
369 SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
372 void AppListSyncableService::RemoveItem(const std::string& id) {
374 model_->DeleteItem(id);
375 PruneEmptySyncFolders();
378 void AppListSyncableService::UpdateItem(AppListItem* app_item) {
379 // Check to see if the item needs to be moved to/from the OEM folder.
380 bool is_oem = AppIsOem(app_item->id());
381 if (!is_oem && app_item->folder_id() == kOemFolderId)
382 model_->MoveItemToFolder(app_item, "");
383 else if (is_oem && app_item->folder_id() != kOemFolderId)
384 model_->MoveItemToFolder(app_item, kOemFolderId);
387 void AppListSyncableService::RemoveSyncItem(const std::string& id) {
388 DVLOG(2) << this << ": RemoveSyncItem: " << id.substr(0, 8);
389 SyncItemMap::iterator iter = sync_items_.find(id);
390 if (iter == sync_items_.end()) {
391 DVLOG(2) << this << " : RemoveSyncItem: No Item.";
395 // Check for existing RemoveDefault sync item.
396 SyncItem* sync_item = iter->second;
397 sync_pb::AppListSpecifics::AppListItemType type = sync_item->item_type;
398 if (type == sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
399 // RemoveDefault item exists, just return.
400 DVLOG(2) << this << " : RemoveDefault Item exists.";
404 if (type == sync_pb::AppListSpecifics::TYPE_APP &&
405 AppIsDefault(extension_system_->extension_service(), id)) {
406 // This is a Default app; update the entry to a REMOVE_DEFAULT entry. This
407 // will overwrite any existing entry for the item.
408 DVLOG(2) << this << " -> SYNC UPDATE: REMOVE_DEFAULT: "
409 << sync_item->item_id;
410 sync_item->item_type = sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP;
411 SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
415 DeleteSyncItem(sync_item);
418 void AppListSyncableService::ResolveFolderPositions(bool move_oem_to_end) {
419 if (!app_list::switches::IsFolderUIEnabled())
422 for (SyncItemMap::iterator iter = sync_items_.begin();
423 iter != sync_items_.end(); ++iter) {
424 SyncItem* sync_item = iter->second;
425 if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
427 AppListItem* app_item = model_->FindItem(sync_item->item_id);
430 if (move_oem_to_end && app_item->id() == kOemFolderId) {
431 // Move the OEM folder to the end.
432 model_->SetItemPosition(app_item, syncer::StringOrdinal());
434 UpdateAppItemFromSyncItem(sync_item, app_item);
438 void AppListSyncableService::PruneEmptySyncFolders() {
439 if (!app_list::switches::IsFolderUIEnabled())
442 std::set<std::string> parent_ids;
443 for (SyncItemMap::iterator iter = sync_items_.begin();
444 iter != sync_items_.end(); ++iter) {
445 parent_ids.insert(iter->second->parent_id);
447 for (SyncItemMap::iterator iter = sync_items_.begin();
448 iter != sync_items_.end(); ) {
449 SyncItem* sync_item = (iter++)->second;
450 if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
452 if (!ContainsKey(parent_ids, sync_item->item_id))
453 DeleteSyncItem(sync_item);
457 // AppListSyncableService syncer::SyncableService
459 syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing(
460 syncer::ModelType type,
461 const syncer::SyncDataList& initial_sync_data,
462 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
463 scoped_ptr<syncer::SyncErrorFactory> error_handler) {
464 DCHECK(!sync_processor_.get());
465 DCHECK(sync_processor.get());
466 DCHECK(error_handler.get());
468 sync_processor_ = sync_processor.Pass();
469 sync_error_handler_ = error_handler.Pass();
471 syncer::SyncMergeResult result = syncer::SyncMergeResult(type);
472 result.set_num_items_before_association(sync_items_.size());
473 DVLOG(1) << this << ": MergeDataAndStartSyncing: "
474 << initial_sync_data.size();
476 // Copy all sync items to |unsynced_items|.
477 std::set<std::string> unsynced_items;
478 for (SyncItemMap::const_iterator iter = sync_items_.begin();
479 iter != sync_items_.end(); ++iter) {
480 unsynced_items.insert(iter->first);
483 // Create SyncItem entries for initial_sync_data.
484 size_t new_items = 0, updated_items = 0;
485 bool oem_folder_is_synced = false;
486 for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin();
487 iter != initial_sync_data.end(); ++iter) {
488 const syncer::SyncData& data = *iter;
489 const std::string& item_id = data.GetSpecifics().app_list().item_id();
490 DVLOG(2) << this << " Initial Sync Item: " << item_id
491 << " Type: " << data.GetSpecifics().app_list().item_type();
492 DCHECK_EQ(syncer::APP_LIST, data.GetDataType());
493 if (ProcessSyncItemSpecifics(data.GetSpecifics().app_list()))
497 unsynced_items.erase(item_id);
498 if (item_id == kOemFolderId)
499 oem_folder_is_synced = true;
502 result.set_num_items_after_association(sync_items_.size());
503 result.set_num_items_added(new_items);
504 result.set_num_items_deleted(0);
505 result.set_num_items_modified(updated_items);
507 // Send unsynced items. Does not affect |result|.
508 syncer::SyncChangeList change_list;
509 for (std::set<std::string>::iterator iter = unsynced_items.begin();
510 iter != unsynced_items.end(); ++iter) {
511 SyncItem* sync_item = FindSyncItem(*iter);
512 DVLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
513 change_list.push_back(SyncChange(FROM_HERE, SyncChange::ACTION_ADD,
514 GetSyncDataFromSyncItem(sync_item)));
516 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
518 // Adding items may have created folders without setting their positions
519 // since we haven't started observing the item list yet. Resolve those.
520 // Also ensure the OEM folder is at the end if its position hasn't been set.
521 bool move_oem_to_end = !oem_folder_is_synced;
522 ResolveFolderPositions(move_oem_to_end);
524 // Start observing app list model changes.
525 model_observer_.reset(new ModelObserver(this));
530 void AppListSyncableService::StopSyncing(syncer::ModelType type) {
531 DCHECK_EQ(type, syncer::APP_LIST);
533 sync_processor_.reset();
534 sync_error_handler_.reset();
537 syncer::SyncDataList AppListSyncableService::GetAllSyncData(
538 syncer::ModelType type) const {
539 DCHECK_EQ(syncer::APP_LIST, type);
541 DVLOG(1) << this << ": GetAllSyncData: " << sync_items_.size();
542 syncer::SyncDataList list;
543 for (SyncItemMap::const_iterator iter = sync_items_.begin();
544 iter != sync_items_.end(); ++iter) {
545 DVLOG(2) << this << " -> SYNC: " << iter->second->ToString();
546 list.push_back(GetSyncDataFromSyncItem(iter->second));
551 syncer::SyncError AppListSyncableService::ProcessSyncChanges(
552 const tracked_objects::Location& from_here,
553 const syncer::SyncChangeList& change_list) {
554 if (!sync_processor_.get()) {
555 return syncer::SyncError(FROM_HERE,
556 syncer::SyncError::DATATYPE_ERROR,
557 "App List syncable service is not started.",
561 // Don't observe the model while processing incoming sync changes.
562 model_observer_.reset();
564 DVLOG(1) << this << ": ProcessSyncChanges: " << change_list.size();
565 for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
566 iter != change_list.end(); ++iter) {
567 const SyncChange& change = *iter;
568 DVLOG(2) << this << " Change: "
569 << change.sync_data().GetSpecifics().app_list().item_id()
570 << " (" << change.change_type() << ")";
571 if (change.change_type() == SyncChange::ACTION_ADD ||
572 change.change_type() == SyncChange::ACTION_UPDATE) {
573 ProcessSyncItemSpecifics(change.sync_data().GetSpecifics().app_list());
574 } else if (change.change_type() == SyncChange::ACTION_DELETE) {
575 DeleteSyncItemSpecifics(change.sync_data().GetSpecifics().app_list());
577 LOG(ERROR) << "Invalid sync change";
581 // Continue observing app list model changes.
582 model_observer_.reset(new ModelObserver(this));
584 return syncer::SyncError();
587 // AppListSyncableService private
589 bool AppListSyncableService::ProcessSyncItemSpecifics(
590 const sync_pb::AppListSpecifics& specifics) {
591 const std::string& item_id = specifics.item_id();
592 if (item_id.empty()) {
593 LOG(ERROR) << "AppList item with empty ID";
596 SyncItem* sync_item = FindSyncItem(item_id);
598 // If an item of the same type exists, update it.
599 if (sync_item->item_type == specifics.item_type()) {
600 UpdateSyncItemFromSync(specifics, sync_item);
601 ProcessExistingSyncItem(sync_item);
602 DVLOG(2) << this << " <- SYNC UPDATE: " << sync_item->ToString();
605 // Otherwise, one of the entries should be TYPE_REMOVE_DEFAULT_APP.
606 if (sync_item->item_type !=
607 sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP &&
608 specifics.item_type() !=
609 sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
610 LOG(ERROR) << "Synced item type: " << specifics.item_type()
611 << " != existing sync item type: " << sync_item->item_type
612 << " Deleting item from model!";
613 model_->DeleteItem(item_id);
615 DVLOG(2) << this << " - ProcessSyncItem: Delete existing entry: "
616 << sync_item->ToString();
618 sync_items_.erase(item_id);
621 sync_item = CreateSyncItem(item_id, specifics.item_type());
622 UpdateSyncItemFromSync(specifics, sync_item);
623 ProcessNewSyncItem(sync_item);
624 DVLOG(2) << this << " <- SYNC ADD: " << sync_item->ToString();
628 void AppListSyncableService::ProcessNewSyncItem(SyncItem* sync_item) {
629 DVLOG(2) << "ProcessNewSyncItem: " << sync_item->ToString();
630 switch (sync_item->item_type) {
631 case sync_pb::AppListSpecifics::TYPE_APP: {
632 // New apps are added through ExtensionAppModelBuilder.
633 // TODO(stevenjb): Determine how to handle app items in sync that
634 // are not installed (e.g. default / OEM apps).
637 case sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP: {
638 DVLOG(1) << this << ": Uninstall: " << sync_item->ToString();
639 UninstallExtension(extension_system_->extension_service(),
643 case sync_pb::AppListSpecifics::TYPE_FOLDER: {
644 AppListItem* app_item = model_->FindItem(sync_item->item_id);
646 return; // Don't create new folders here, the model will do that.
647 UpdateAppItemFromSyncItem(sync_item, app_item);
650 case sync_pb::AppListSpecifics::TYPE_URL: {
651 // TODO(stevenjb): Implement
652 LOG(WARNING) << "TYPE_URL not supported";
656 NOTREACHED() << "Unrecognized sync item type: " << sync_item->ToString();
659 void AppListSyncableService::ProcessExistingSyncItem(SyncItem* sync_item) {
660 if (sync_item->item_type ==
661 sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
664 DVLOG(2) << "ProcessExistingSyncItem: " << sync_item->ToString();
665 AppListItem* app_item = model_->FindItem(sync_item->item_id);
666 DVLOG(2) << " AppItem: " << app_item->ToDebugString();
668 LOG(ERROR) << "Item not found in model: " << sync_item->ToString();
671 // This is the only place where sync can cause an item to change folders.
672 if (app_list::switches::IsFolderUIEnabled() &&
673 app_item->folder_id() != sync_item->parent_id &&
674 !AppIsOem(app_item->id())) {
675 DVLOG(2) << " Moving Item To Folder: " << sync_item->parent_id;
676 model_->MoveItemToFolder(app_item, sync_item->parent_id);
678 UpdateAppItemFromSyncItem(sync_item, app_item);
681 void AppListSyncableService::UpdateAppItemFromSyncItem(
682 const AppListSyncableService::SyncItem* sync_item,
683 AppListItem* app_item) {
684 if (!app_item->position().Equals(sync_item->item_ordinal))
685 model_->SetItemPosition(app_item, sync_item->item_ordinal);
686 // Only update the item name if it is a Folder or the name is empty.
687 if (sync_item->item_name != app_item->name() &&
688 sync_item->item_id != kOemFolderId &&
689 (app_item->GetItemType() == AppListFolderItem::kItemType ||
690 app_item->name().empty())) {
691 model_->SetItemName(app_item, sync_item->item_name);
695 bool AppListSyncableService::SyncStarted() {
696 if (sync_processor_.get())
698 if (flare_.is_null()) {
699 DVLOG(2) << this << ": SyncStarted: Flare.";
700 flare_ = sync_start_util::GetFlareForSyncableService(profile_->GetPath());
701 flare_.Run(syncer::APP_LIST);
706 void AppListSyncableService::SendSyncChange(
708 SyncChange::SyncChangeType sync_change_type) {
709 if (!SyncStarted()) {
710 DVLOG(2) << this << " - SendSyncChange: SYNC NOT STARTED: "
711 << sync_item->ToString();
714 if (sync_change_type == SyncChange::ACTION_ADD)
715 DVLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
717 DVLOG(2) << this << " -> SYNC UPDATE: " << sync_item->ToString();
718 SyncChange sync_change(FROM_HERE, sync_change_type,
719 GetSyncDataFromSyncItem(sync_item));
720 sync_processor_->ProcessSyncChanges(
721 FROM_HERE, syncer::SyncChangeList(1, sync_change));
724 AppListSyncableService::SyncItem*
725 AppListSyncableService::FindSyncItem(const std::string& item_id) {
726 SyncItemMap::iterator iter = sync_items_.find(item_id);
727 if (iter == sync_items_.end())
732 AppListSyncableService::SyncItem*
733 AppListSyncableService::CreateSyncItem(
734 const std::string& item_id,
735 sync_pb::AppListSpecifics::AppListItemType item_type) {
736 DCHECK(!ContainsKey(sync_items_, item_id));
737 SyncItem* sync_item = new SyncItem(item_id, item_type);
738 sync_items_[item_id] = sync_item;
742 void AppListSyncableService::DeleteSyncItemSpecifics(
743 const sync_pb::AppListSpecifics& specifics) {
744 const std::string& item_id = specifics.item_id();
745 if (item_id.empty()) {
746 LOG(ERROR) << "Delete AppList item with empty ID";
749 DVLOG(2) << this << ": DeleteSyncItemSpecifics: " << item_id.substr(0, 8);
750 SyncItemMap::iterator iter = sync_items_.find(item_id);
751 if (iter == sync_items_.end())
753 sync_pb::AppListSpecifics::AppListItemType item_type =
754 iter->second->item_type;
755 DVLOG(2) << this << " <- SYNC DELETE: " << iter->second->ToString();
757 sync_items_.erase(iter);
758 // Only delete apps from the model. Folders will be deleted when all
759 // children have been deleted.
760 if (item_type == sync_pb::AppListSpecifics::TYPE_APP)
761 model_->DeleteItem(item_id);
764 std::string AppListSyncableService::FindOrCreateOemFolder() {
765 AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
767 scoped_ptr<AppListFolderItem> new_folder(new AppListFolderItem(
768 kOemFolderId, AppListFolderItem::FOLDER_TYPE_OEM));
769 oem_folder = static_cast<AppListFolderItem*>(
770 model_->AddItem(new_folder.PassAs<app_list::AppListItem>()));
772 model_->SetItemName(oem_folder, oem_folder_name_);
773 return oem_folder->id();
776 bool AppListSyncableService::AppIsOem(const std::string& id) {
777 if (!extension_system_->extension_service())
779 const extensions::Extension* extension =
780 extension_system_->extension_service()->GetExtensionById(id, true);
781 return extension && extension->was_installed_by_oem();
784 std::string AppListSyncableService::SyncItem::ToString() const {
785 std::string res = item_id.substr(0, 8);
786 if (item_type == sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
787 res += " { RemoveDefault }";
789 res += " { " + item_name + " }";
790 res += " [" + item_ordinal.ToDebugString() + "]";
791 if (!parent_id.empty())
792 res += " <" + parent_id.substr(0, 8) + ">";
797 } // namespace app_list