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