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