app_list_syncable_service.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/app_list/app_list_syncable_service.h" 6 7#include "base/command_line.h" 8#include "chrome/browser/chrome_notification_types.h" 9#include "chrome/browser/extensions/extension_service.h" 10#include "chrome/browser/profiles/profile.h" 11#include "chrome/browser/ui/app_list/app_list_service.h" 12#include "chrome/browser/ui/app_list/extension_app_item.h" 13#include "chrome/browser/ui/app_list/extension_app_model_builder.h" 14#include "chrome/browser/ui/host_desktop.h" 15#include "chrome/common/chrome_switches.h" 16#include "content/public/browser/notification_source.h" 17#include "extensions/browser/extension_prefs.h" 18#include "extensions/browser/extension_system.h" 19#include "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" 30 31using syncer::SyncChange; 32 33namespace app_list { 34 35namespace { 36 37const char kOemFolderId[] = "ddb1da55-d478-4243-8642-56d3041f0263"; 38 39bool SyncAppListEnabled() { 40 return !CommandLine::ForCurrentProcess()->HasSwitch( 41 ::switches::kDisableSyncAppList); 42} 43 44void 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()); 54} 55 56bool UpdateSyncItemFromAppItem(const AppListItem* app_item, 57 AppListSyncableService::SyncItem* sync_item) { 58 DCHECK_EQ(sync_item->item_id, app_item->id()); 59 bool changed = false; 60 if (app_list::switches::IsFolderUIEnabled() && 61 sync_item->parent_id != app_item->folder_id()) { 62 sync_item->parent_id = app_item->folder_id(); 63 changed = true; 64 } 65 if (sync_item->item_name != app_item->name()) { 66 sync_item->item_name = app_item->name(); 67 changed = true; 68 } 69 if (!sync_item->item_ordinal.IsValid() || 70 !app_item->position().Equals(sync_item->item_ordinal)) { 71 sync_item->item_ordinal = app_item->position(); 72 changed = true; 73 } 74 // TODO(stevenjb): Set page_ordinal. 75 return changed; 76} 77 78void GetSyncSpecificsFromSyncItem(const AppListSyncableService::SyncItem* item, 79 sync_pb::AppListSpecifics* specifics) { 80 DCHECK(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()); 89} 90 91syncer::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, 96 item->item_id, 97 specifics); 98} 99 100bool AppIsDefault(ExtensionService* service, const std::string& id) { 101 return service && extensions::ExtensionPrefs::Get(service->profile()) 102 ->WasInstalledByDefault(id); 103} 104 105void UninstallExtension(ExtensionService* service, const std::string& id) { 106 if (service && service->GetInstalledExtension(id)) 107 service->UninstallExtension(id, false, NULL); 108} 109 110bool 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; 117 } else { 118 LOG(ERROR) << "Unrecognized model type: " << item_type; 119 return false; 120 } 121 return true; 122} 123 124} // namespace 125 126// AppListSyncableService::SyncItem 127 128AppListSyncableService::SyncItem::SyncItem( 129 const std::string& id, 130 sync_pb::AppListSpecifics::AppListItemType type) 131 : item_id(id), 132 item_type(type) { 133} 134 135AppListSyncableService::SyncItem::~SyncItem() { 136} 137 138// AppListSyncableService::ModelObserver 139 140class AppListSyncableService::ModelObserver : public AppListModelObserver { 141 public: 142 explicit ModelObserver(AppListSyncableService* owner) 143 : owner_(owner) { 144 DVLOG(2) << owner_ << ": ModelObserver Added"; 145 owner_->model()->AddObserver(this); 146 } 147 148 virtual ~ModelObserver() { 149 owner_->model()->RemoveObserver(this); 150 DVLOG(2) << owner_ << ": ModelObserver Removed"; 151 } 152 153 private: 154 // AppListModelObserver 155 virtual void OnAppListItemAdded(AppListItem* item) OVERRIDE { 156 DVLOG(2) << owner_ << " OnAppListItemAdded: " << item->ToDebugString(); 157 owner_->AddOrUpdateFromSyncItem(item); 158 } 159 160 virtual void OnAppListItemWillBeDeleted(AppListItem* item) OVERRIDE { 161 DVLOG(2) << owner_ << " OnAppListItemDeleted: " << item->ToDebugString(); 162 owner_->RemoveSyncItem(item->id()); 163 } 164 165 virtual void OnAppListItemUpdated(AppListItem* item) OVERRIDE { 166 DVLOG(2) << owner_ << " OnAppListItemUpdated: " << item->ToDebugString(); 167 owner_->UpdateSyncItem(item); 168 } 169 170 AppListSyncableService* owner_; 171 172 DISALLOW_COPY_AND_ASSIGN(ModelObserver); 173}; 174 175// AppListSyncableService 176 177AppListSyncableService::AppListSyncableService( 178 Profile* profile, 179 extensions::ExtensionSystem* extension_system) 180 : profile_(profile), 181 extension_system_(extension_system), 182 model_(new AppListModel) { 183 if (!extension_system) { 184 LOG(ERROR) << "AppListSyncableService created with no ExtensionSystem"; 185 return; 186 } 187 188 oem_folder_name_ = 189 l10n_util::GetStringUTF8(IDS_APP_LIST_OEM_DEFAULT_FOLDER_NAME); 190 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()) { 196 BuildModel(); 197 return; 198 } 199 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)); 203} 204 205AppListSyncableService::~AppListSyncableService() { 206 // Remove observers. 207 model_observer_.reset(); 208 209 STLDeleteContainerPairSecondPointers(sync_items_.begin(), sync_items_.end()); 210} 211 212void 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); 220 if (service) 221 controller = service->GetControllerDelegate(); 222 apps_builder_.reset(new ExtensionAppModelBuilder(controller)); 223 DCHECK(profile_); 224 // TODO(stevenjb): Correctly handle OTR profiles for Guest mode. 225 if (!profile_->IsOffTheRecord() && SyncAppListEnabled()) { 226 DVLOG(1) << this << ": AppListSyncableService: InitializeWithService."; 227 SyncStarted(); 228 apps_builder_->InitializeWithService(this); 229 } else { 230 DVLOG(1) << this << ": AppListSyncableService: InitializeWithProfile."; 231 apps_builder_->InitializeWithProfile(profile_, model_.get()); 232 } 233} 234 235void AppListSyncableService::Observe( 236 int type, 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(); 242 BuildModel(); 243} 244 245const AppListSyncableService::SyncItem* 246AppListSyncableService::GetSyncItem(const std::string& id) const { 247 SyncItemMap::const_iterator iter = sync_items_.find(id); 248 if (iter != sync_items_.end()) 249 return iter->second; 250 return NULL; 251} 252 253void AppListSyncableService::SetOemFolderName(const std::string& name) { 254 oem_folder_name_ = name; 255 AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId); 256 if (oem_folder) 257 model_->SetItemName(oem_folder, oem_folder_name_); 258} 259 260void AppListSyncableService::AddItem(scoped_ptr<AppListItem> app_item) { 261 SyncItem* sync_item = FindOrAddSyncItem(app_item.get()); 262 if (!sync_item) 263 return; // Item is not valid. 264 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(); 269 } else { 270 folder_id = sync_item->parent_id; 271 DVLOG(1) << this << ": AddItem: " << sync_item->ToString() 272 << " Folder: '" << folder_id << "'"; 273 } 274 model_->AddItemToFolder(app_item.Pass(), folder_id); 275} 276 277AppListSyncableService::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"; 282 return NULL; 283 } 284 SyncItem* sync_item = FindSyncItem(item_id); 285 if (sync_item) { 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(); 290 return sync_item; 291 } 292 293 if (RemoveDefaultApp(app_item, sync_item)) 294 return NULL; 295 296 // Fall through. The REMOVE_DEFAULT_APP entry has been deleted, now a new 297 // App entry can be added. 298 } 299 300 return CreateSyncItemFromAppItem(app_item); 301} 302 303AppListSyncableService::SyncItem* 304AppListSyncableService::CreateSyncItemFromAppItem(AppListItem* app_item) { 305 sync_pb::AppListSpecifics::AppListItemType type; 306 if (!GetAppListItemType(app_item, &type)) 307 return NULL; 308 SyncItem* sync_item = CreateSyncItem(app_item->id(), type); 309 UpdateSyncItemFromAppItem(app_item, sync_item); 310 SendSyncChange(sync_item, SyncChange::ACTION_ADD); 311 return sync_item; 312} 313 314void AppListSyncableService::AddOrUpdateFromSyncItem(AppListItem* app_item) { 315 SyncItem* sync_item = FindSyncItem(app_item->id()); 316 if (sync_item) { 317 UpdateAppItemFromSyncItem(sync_item, app_item); 318 return; 319 } 320 CreateSyncItemFromAppItem(app_item); 321} 322 323bool AppListSyncableService::RemoveDefaultApp(AppListItem* item, 324 SyncItem* sync_item) { 325 CHECK_EQ(sync_item->item_type, 326 sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP); 327 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()); 335 return true; 336 } 337 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); 342 return false; 343} 344 345void AppListSyncableService::DeleteSyncItem(SyncItem* sync_item) { 346 if (SyncStarted()) { 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)); 352 } 353 std::string item_id = sync_item->item_id; 354 delete sync_item; 355 sync_items_.erase(item_id); 356} 357 358void AppListSyncableService::UpdateSyncItem(AppListItem* app_item) { 359 SyncItem* sync_item = FindSyncItem(app_item->id()); 360 if (!sync_item) { 361 LOG(ERROR) << "UpdateItem: no sync item: " << app_item->id(); 362 return; 363 } 364 bool changed = UpdateSyncItemFromAppItem(app_item, sync_item); 365 if (!changed) { 366 DVLOG(2) << this << " - Update: SYNC NO CHANGE: " << sync_item->ToString(); 367 return; 368 } 369 SendSyncChange(sync_item, SyncChange::ACTION_UPDATE); 370} 371 372void AppListSyncableService::RemoveItem(const std::string& id) { 373 RemoveSyncItem(id); 374 model_->DeleteItem(id); 375 PruneEmptySyncFolders(); 376} 377 378void 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); 385} 386 387void 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."; 392 return; 393 } 394 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."; 401 return; 402 } 403 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); 412 return; 413 } 414 415 DeleteSyncItem(sync_item); 416} 417 418void AppListSyncableService::ResolveFolderPositions() { 419 if (!app_list::switches::IsFolderUIEnabled()) 420 return; 421 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) 426 continue; 427 AppListItem* app_item = model_->FindItem(sync_item->item_id); 428 if (!app_item) 429 continue; 430 UpdateAppItemFromSyncItem(sync_item, app_item); 431 } 432} 433 434void AppListSyncableService::PruneEmptySyncFolders() { 435 if (!app_list::switches::IsFolderUIEnabled()) 436 return; 437 438 std::set<std::string> parent_ids; 439 for (SyncItemMap::iterator iter = sync_items_.begin(); 440 iter != sync_items_.end(); ++iter) { 441 parent_ids.insert(iter->second->parent_id); 442 } 443 for (SyncItemMap::iterator iter = sync_items_.begin(); 444 iter != sync_items_.end(); ) { 445 SyncItem* sync_item = (iter++)->second; 446 if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER) 447 continue; 448 if (!ContainsKey(parent_ids, sync_item->item_id)) 449 DeleteSyncItem(sync_item); 450 } 451} 452 453// AppListSyncableService syncer::SyncableService 454 455syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing( 456 syncer::ModelType type, 457 const syncer::SyncDataList& initial_sync_data, 458 scoped_ptr<syncer::SyncChangeProcessor> sync_processor, 459 scoped_ptr<syncer::SyncErrorFactory> error_handler) { 460 DCHECK(!sync_processor_.get()); 461 DCHECK(sync_processor.get()); 462 DCHECK(error_handler.get()); 463 464 sync_processor_ = sync_processor.Pass(); 465 sync_error_handler_ = error_handler.Pass(); 466 467 syncer::SyncMergeResult result = syncer::SyncMergeResult(type); 468 result.set_num_items_before_association(sync_items_.size()); 469 DVLOG(1) << this << ": MergeDataAndStartSyncing: " 470 << initial_sync_data.size(); 471 472 // Copy all sync items to |unsynced_items|. 473 std::set<std::string> unsynced_items; 474 for (SyncItemMap::const_iterator iter = sync_items_.begin(); 475 iter != sync_items_.end(); ++iter) { 476 unsynced_items.insert(iter->first); 477 } 478 479 // Create SyncItem entries for initial_sync_data. 480 size_t new_items = 0, updated_items = 0; 481 for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin(); 482 iter != initial_sync_data.end(); ++iter) { 483 const syncer::SyncData& data = *iter; 484 DVLOG(2) << this << " Initial Sync Item: " 485 << data.GetSpecifics().app_list().item_id() 486 << " Type: " << data.GetSpecifics().app_list().item_type(); 487 DCHECK_EQ(syncer::APP_LIST, data.GetDataType()); 488 if (ProcessSyncItemSpecifics(data.GetSpecifics().app_list())) 489 ++new_items; 490 else 491 ++updated_items; 492 unsynced_items.erase(data.GetSpecifics().app_list().item_id()); 493 } 494 495 result.set_num_items_after_association(sync_items_.size()); 496 result.set_num_items_added(new_items); 497 result.set_num_items_deleted(0); 498 result.set_num_items_modified(updated_items); 499 500 // Send unsynced items. Does not affect |result|. 501 syncer::SyncChangeList change_list; 502 for (std::set<std::string>::iterator iter = unsynced_items.begin(); 503 iter != unsynced_items.end(); ++iter) { 504 SyncItem* sync_item = FindSyncItem(*iter); 505 DVLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString(); 506 change_list.push_back(SyncChange(FROM_HERE, SyncChange::ACTION_ADD, 507 GetSyncDataFromSyncItem(sync_item))); 508 } 509 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list); 510 511 // Adding items may have created folders without setting their positions 512 // since we haven't started observing the item list yet. Resolve those. 513 ResolveFolderPositions(); 514 515 // Start observing app list model changes. 516 model_observer_.reset(new ModelObserver(this)); 517 518 return result; 519} 520 521void AppListSyncableService::StopSyncing(syncer::ModelType type) { 522 DCHECK_EQ(type, syncer::APP_LIST); 523 524 sync_processor_.reset(); 525 sync_error_handler_.reset(); 526} 527 528syncer::SyncDataList AppListSyncableService::GetAllSyncData( 529 syncer::ModelType type) const { 530 DCHECK_EQ(syncer::APP_LIST, type); 531 532 DVLOG(1) << this << ": GetAllSyncData: " << sync_items_.size(); 533 syncer::SyncDataList list; 534 for (SyncItemMap::const_iterator iter = sync_items_.begin(); 535 iter != sync_items_.end(); ++iter) { 536 DVLOG(2) << this << " -> SYNC: " << iter->second->ToString(); 537 list.push_back(GetSyncDataFromSyncItem(iter->second)); 538 } 539 return list; 540} 541 542syncer::SyncError AppListSyncableService::ProcessSyncChanges( 543 const tracked_objects::Location& from_here, 544 const syncer::SyncChangeList& change_list) { 545 if (!sync_processor_.get()) { 546 return syncer::SyncError(FROM_HERE, 547 syncer::SyncError::DATATYPE_ERROR, 548 "App List syncable service is not started.", 549 syncer::APP_LIST); 550 } 551 552 // Don't observe the model while processing incoming sync changes. 553 model_observer_.reset(); 554 555 DVLOG(1) << this << ": ProcessSyncChanges: " << change_list.size(); 556 for (syncer::SyncChangeList::const_iterator iter = change_list.begin(); 557 iter != change_list.end(); ++iter) { 558 const SyncChange& change = *iter; 559 DVLOG(2) << this << " Change: " 560 << change.sync_data().GetSpecifics().app_list().item_id() 561 << " (" << change.change_type() << ")"; 562 if (change.change_type() == SyncChange::ACTION_ADD || 563 change.change_type() == SyncChange::ACTION_UPDATE) { 564 ProcessSyncItemSpecifics(change.sync_data().GetSpecifics().app_list()); 565 } else if (change.change_type() == SyncChange::ACTION_DELETE) { 566 DeleteSyncItemSpecifics(change.sync_data().GetSpecifics().app_list()); 567 } else { 568 LOG(ERROR) << "Invalid sync change"; 569 } 570 } 571 572 // Continue observing app list model changes. 573 model_observer_.reset(new ModelObserver(this)); 574 575 return syncer::SyncError(); 576} 577 578// AppListSyncableService private 579 580bool AppListSyncableService::ProcessSyncItemSpecifics( 581 const sync_pb::AppListSpecifics& specifics) { 582 const std::string& item_id = specifics.item_id(); 583 if (item_id.empty()) { 584 LOG(ERROR) << "AppList item with empty ID"; 585 return false; 586 } 587 SyncItem* sync_item = FindSyncItem(item_id); 588 if (sync_item) { 589 // If an item of the same type exists, update it. 590 if (sync_item->item_type == specifics.item_type()) { 591 UpdateSyncItemFromSync(specifics, sync_item); 592 ProcessExistingSyncItem(sync_item); 593 DVLOG(2) << this << " <- SYNC UPDATE: " << sync_item->ToString(); 594 return false; 595 } 596 // Otherwise, one of the entries should be TYPE_REMOVE_DEFAULT_APP. 597 if (sync_item->item_type != 598 sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP && 599 specifics.item_type() != 600 sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) { 601 LOG(ERROR) << "Synced item type: " << specifics.item_type() 602 << " != existing sync item type: " << sync_item->item_type 603 << " Deleting item from model!"; 604 model_->DeleteItem(item_id); 605 } 606 DVLOG(2) << this << " - ProcessSyncItem: Delete existing entry: " 607 << sync_item->ToString(); 608 delete sync_item; 609 sync_items_.erase(item_id); 610 } 611 612 sync_item = CreateSyncItem(item_id, specifics.item_type()); 613 UpdateSyncItemFromSync(specifics, sync_item); 614 ProcessNewSyncItem(sync_item); 615 DVLOG(2) << this << " <- SYNC ADD: " << sync_item->ToString(); 616 return true; 617} 618 619void AppListSyncableService::ProcessNewSyncItem(SyncItem* sync_item) { 620 DVLOG(2) << "ProcessNewSyncItem: " << sync_item->ToString(); 621 switch (sync_item->item_type) { 622 case sync_pb::AppListSpecifics::TYPE_APP: { 623 // New apps are added through ExtensionAppModelBuilder. 624 // TODO(stevenjb): Determine how to handle app items in sync that 625 // are not installed (e.g. default / OEM apps). 626 return; 627 } 628 case sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP: { 629 DVLOG(1) << this << ": Uninstall: " << sync_item->ToString(); 630 UninstallExtension(extension_system_->extension_service(), 631 sync_item->item_id); 632 return; 633 } 634 case sync_pb::AppListSpecifics::TYPE_FOLDER: { 635 AppListItem* app_item = model_->FindItem(sync_item->item_id); 636 if (!app_item) 637 return; // Don't create new folders here, the model will do that. 638 UpdateAppItemFromSyncItem(sync_item, app_item); 639 return; 640 } 641 case sync_pb::AppListSpecifics::TYPE_URL: { 642 // TODO(stevenjb): Implement 643 LOG(WARNING) << "TYPE_URL not supported"; 644 return; 645 } 646 } 647 NOTREACHED() << "Unrecognized sync item type: " << sync_item->ToString(); 648} 649 650void AppListSyncableService::ProcessExistingSyncItem(SyncItem* sync_item) { 651 if (sync_item->item_type == 652 sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) { 653 return; 654 } 655 DVLOG(2) << "ProcessExistingSyncItem: " << sync_item->ToString(); 656 AppListItem* app_item = model_->FindItem(sync_item->item_id); 657 DVLOG(2) << " AppItem: " << app_item->ToDebugString(); 658 if (!app_item) { 659 LOG(ERROR) << "Item not found in model: " << sync_item->ToString(); 660 return; 661 } 662 // This is the only place where sync can cause an item to change folders. 663 if (app_list::switches::IsFolderUIEnabled() && 664 app_item->folder_id() != sync_item->parent_id && 665 !AppIsOem(app_item->id())) { 666 DVLOG(2) << " Moving Item To Folder: " << sync_item->parent_id; 667 model_->MoveItemToFolder(app_item, sync_item->parent_id); 668 } 669 UpdateAppItemFromSyncItem(sync_item, app_item); 670} 671 672void AppListSyncableService::UpdateAppItemFromSyncItem( 673 const AppListSyncableService::SyncItem* sync_item, 674 AppListItem* app_item) { 675 if (!app_item->position().Equals(sync_item->item_ordinal)) 676 model_->SetItemPosition(app_item, sync_item->item_ordinal); 677 // Only update the item name if it is a Folder or the name is empty. 678 if (sync_item->item_name != app_item->name() && 679 sync_item->item_id != kOemFolderId && 680 (app_item->GetItemType() == AppListFolderItem::kItemType || 681 app_item->name().empty())) { 682 model_->SetItemName(app_item, sync_item->item_name); 683 } 684} 685 686bool AppListSyncableService::SyncStarted() { 687 if (sync_processor_.get()) 688 return true; 689 if (flare_.is_null()) { 690 DVLOG(2) << this << ": SyncStarted: Flare."; 691 flare_ = sync_start_util::GetFlareForSyncableService(profile_->GetPath()); 692 flare_.Run(syncer::APP_LIST); 693 } 694 return false; 695} 696 697void AppListSyncableService::SendSyncChange( 698 SyncItem* sync_item, 699 SyncChange::SyncChangeType sync_change_type) { 700 if (!SyncStarted()) { 701 DVLOG(2) << this << " - SendSyncChange: SYNC NOT STARTED: " 702 << sync_item->ToString(); 703 return; 704 } 705 if (sync_change_type == SyncChange::ACTION_ADD) 706 DVLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString(); 707 else 708 DVLOG(2) << this << " -> SYNC UPDATE: " << sync_item->ToString(); 709 SyncChange sync_change(FROM_HERE, sync_change_type, 710 GetSyncDataFromSyncItem(sync_item)); 711 sync_processor_->ProcessSyncChanges( 712 FROM_HERE, syncer::SyncChangeList(1, sync_change)); 713} 714 715AppListSyncableService::SyncItem* 716AppListSyncableService::FindSyncItem(const std::string& item_id) { 717 SyncItemMap::iterator iter = sync_items_.find(item_id); 718 if (iter == sync_items_.end()) 719 return NULL; 720 return iter->second; 721} 722 723AppListSyncableService::SyncItem* 724AppListSyncableService::CreateSyncItem( 725 const std::string& item_id, 726 sync_pb::AppListSpecifics::AppListItemType item_type) { 727 DCHECK(!ContainsKey(sync_items_, item_id)); 728 SyncItem* sync_item = new SyncItem(item_id, item_type); 729 sync_items_[item_id] = sync_item; 730 return sync_item; 731} 732 733void AppListSyncableService::DeleteSyncItemSpecifics( 734 const sync_pb::AppListSpecifics& specifics) { 735 const std::string& item_id = specifics.item_id(); 736 if (item_id.empty()) { 737 LOG(ERROR) << "Delete AppList item with empty ID"; 738 return; 739 } 740 DVLOG(2) << this << ": DeleteSyncItemSpecifics: " << item_id.substr(0, 8); 741 SyncItemMap::iterator iter = sync_items_.find(item_id); 742 if (iter == sync_items_.end()) 743 return; 744 sync_pb::AppListSpecifics::AppListItemType item_type = 745 iter->second->item_type; 746 DVLOG(2) << this << " <- SYNC DELETE: " << iter->second->ToString(); 747 delete iter->second; 748 sync_items_.erase(iter); 749 // Only delete apps from the model. Folders will be deleted when all 750 // children have been deleted. 751 if (item_type == sync_pb::AppListSpecifics::TYPE_APP) 752 model_->DeleteItem(item_id); 753} 754 755std::string AppListSyncableService::FindOrCreateOemFolder() { 756 AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId); 757 if (!oem_folder) { 758 scoped_ptr<AppListFolderItem> new_folder(new AppListFolderItem( 759 kOemFolderId, AppListFolderItem::FOLDER_TYPE_OEM)); 760 oem_folder = static_cast<AppListFolderItem*>( 761 model_->AddItem(new_folder.PassAs<app_list::AppListItem>())); 762 } 763 model_->SetItemName(oem_folder, oem_folder_name_); 764 return oem_folder->id(); 765} 766 767bool AppListSyncableService::AppIsOem(const std::string& id) { 768 if (!extension_system_->extension_service()) 769 return false; 770 const extensions::Extension* extension = 771 extension_system_->extension_service()->GetExtensionById(id, true); 772 return extension && extension->was_installed_by_oem(); 773} 774 775std::string AppListSyncableService::SyncItem::ToString() const { 776 std::string res = item_id.substr(0, 8); 777 if (item_type == sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) { 778 res += " { RemoveDefault }"; 779 } else { 780 res += " { " + item_name + " }"; 781 res += " [" + item_ordinal.ToDebugString() + "]"; 782 if (!parent_id.empty()) 783 res += " <" + parent_id.substr(0, 8) + ">"; 784 } 785 return res; 786} 787 788} // namespace app_list 789