| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/extensions/app_notification_manager.h" | |
| 6 | |
| 7 #include "base/auto_reset.h" | |
| 8 #include "base/bind.h" | |
| 9 #include "base/files/file_path.h" | |
| 10 #include "base/location.h" | |
| 11 #include "base/metrics/histogram.h" | |
| 12 #include "base/perftimer.h" | |
| 13 #include "base/stl_util.h" | |
| 14 #include "base/time.h" | |
| 15 #include "chrome/browser/profiles/profile.h" | |
| 16 #include "chrome/common/chrome_notification_types.h" | |
| 17 #include "chrome/common/extensions/extension.h" | |
| 18 #include "content/public/browser/notification_service.h" | |
| 19 #include "sync/api/sync_error_factory.h" | |
| 20 #include "sync/protocol/app_notification_specifics.pb.h" | |
| 21 #include "sync/protocol/sync.pb.h" | |
| 22 | |
| 23 using content::BrowserThread; | |
| 24 | |
| 25 typedef std::map<std::string, syncer::SyncData> SyncDataMap; | |
| 26 | |
| 27 namespace extensions { | |
| 28 | |
| 29 namespace { | |
| 30 | |
| 31 class GuidComparator | |
| 32 : public std::binary_function<linked_ptr<AppNotification>, | |
| 33 std::string, | |
| 34 bool> { | |
| 35 public: | |
| 36 bool operator() (linked_ptr<AppNotification> notif, | |
| 37 const std::string& guid) const { | |
| 38 return notif->guid() == guid; | |
| 39 } | |
| 40 }; | |
| 41 | |
| 42 const AppNotification* FindByGuid(const AppNotificationList& list, | |
| 43 const std::string& guid) { | |
| 44 AppNotificationList::const_iterator iter = std::find_if( | |
| 45 list.begin(), list.end(), std::bind2nd(GuidComparator(), guid)); | |
| 46 return iter == list.end() ? NULL : iter->get(); | |
| 47 } | |
| 48 | |
| 49 void RemoveByGuid(AppNotificationList* list, const std::string& guid) { | |
| 50 if (!list) | |
| 51 return; | |
| 52 | |
| 53 AppNotificationList::iterator iter = std::find_if( | |
| 54 list->begin(), list->end(), std::bind2nd(GuidComparator(), guid)); | |
| 55 if (iter != list->end()) | |
| 56 list->erase(iter); | |
| 57 } | |
| 58 | |
| 59 void PopulateGuidToSyncDataMap(const syncer::SyncDataList& sync_data, | |
| 60 SyncDataMap* data_map) { | |
| 61 for (syncer::SyncDataList::const_iterator iter = sync_data.begin(); | |
| 62 iter != sync_data.end(); ++iter) { | |
| 63 (*data_map)[iter->GetSpecifics().app_notification().guid()] = *iter; | |
| 64 } | |
| 65 } | |
| 66 } // namespace | |
| 67 | |
| 68 const unsigned int AppNotificationManager::kMaxNotificationPerApp = 5; | |
| 69 | |
| 70 AppNotificationManager::AppNotificationManager(Profile* profile) | |
| 71 : profile_(profile), | |
| 72 models_associated_(false), | |
| 73 processing_syncer_changes_(false) { | |
| 74 registrar_.Add(this, | |
| 75 chrome::NOTIFICATION_EXTENSION_UNINSTALLED, | |
| 76 content::Source<Profile>(profile_)); | |
| 77 } | |
| 78 | |
| 79 void AppNotificationManager::Init() { | |
| 80 base::FilePath storage_path = | |
| 81 profile_->GetPath().AppendASCII("App Notifications"); | |
| 82 load_timer_.reset(new PerfTimer()); | |
| 83 BrowserThread::PostTask( | |
| 84 BrowserThread::FILE, | |
| 85 FROM_HERE, | |
| 86 base::Bind(&AppNotificationManager::LoadOnFileThread, | |
| 87 this, storage_path)); | |
| 88 } | |
| 89 | |
| 90 bool AppNotificationSortPredicate(const linked_ptr<AppNotification> a1, | |
| 91 const linked_ptr<AppNotification> a2) { | |
| 92 return a1.get()->creation_time() < a2.get()->creation_time(); | |
| 93 } | |
| 94 | |
| 95 bool AppNotificationManager::Add(AppNotification* item) { | |
| 96 // Do this first since we own the incoming item and hence want to delete | |
| 97 // it in error paths. | |
| 98 linked_ptr<AppNotification> linked_item(item); | |
| 99 if (!loaded()) | |
| 100 return false; | |
| 101 const std::string& extension_id = item->extension_id(); | |
| 102 AppNotificationList& list = GetAllInternal(extension_id); | |
| 103 list.push_back(linked_item); | |
| 104 | |
| 105 SyncAddChange(*linked_item); | |
| 106 | |
| 107 std::sort(list.begin(), list.end(), AppNotificationSortPredicate); | |
| 108 | |
| 109 if (list.size() > AppNotificationManager::kMaxNotificationPerApp) { | |
| 110 AppNotification* removed = list.begin()->get(); | |
| 111 SyncRemoveChange(*removed); | |
| 112 list.erase(list.begin()); | |
| 113 } | |
| 114 | |
| 115 if (storage_.get()) { | |
| 116 BrowserThread::PostTask( | |
| 117 BrowserThread::FILE, | |
| 118 FROM_HERE, | |
| 119 base::Bind(&AppNotificationManager::SaveOnFileThread, | |
| 120 this, extension_id, CopyAppNotificationList(list))); | |
| 121 } | |
| 122 | |
| 123 content::NotificationService::current()->Notify( | |
| 124 chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, | |
| 125 content::Source<Profile>(profile_), | |
| 126 content::Details<const std::string>(&extension_id)); | |
| 127 | |
| 128 return true; | |
| 129 } | |
| 130 | |
| 131 const AppNotificationList* AppNotificationManager::GetAll( | |
| 132 const std::string& extension_id) const { | |
| 133 if (!loaded()) | |
| 134 return NULL; | |
| 135 if (ContainsKey(*notifications_, extension_id)) | |
| 136 return &((*notifications_)[extension_id]); | |
| 137 return NULL; | |
| 138 } | |
| 139 | |
| 140 const AppNotification* AppNotificationManager::GetLast( | |
| 141 const std::string& extension_id) { | |
| 142 if (!loaded()) | |
| 143 return NULL; | |
| 144 NotificationMap::iterator found = notifications_->find(extension_id); | |
| 145 if (found == notifications_->end()) | |
| 146 return NULL; | |
| 147 const AppNotificationList& list = found->second; | |
| 148 if (list.empty()) | |
| 149 return NULL; | |
| 150 return list.rbegin()->get(); | |
| 151 } | |
| 152 | |
| 153 void AppNotificationManager::ClearAll(const std::string& extension_id) { | |
| 154 if (!loaded()) | |
| 155 return; | |
| 156 NotificationMap::iterator found = notifications_->find(extension_id); | |
| 157 if (found != notifications_->end()) { | |
| 158 SyncClearAllChange(found->second); | |
| 159 notifications_->erase(found); | |
| 160 } | |
| 161 | |
| 162 if (storage_.get()) { | |
| 163 BrowserThread::PostTask( | |
| 164 BrowserThread::FILE, | |
| 165 FROM_HERE, | |
| 166 base::Bind(&AppNotificationManager::DeleteOnFileThread, | |
| 167 this, extension_id)); | |
| 168 } | |
| 169 | |
| 170 content::NotificationService::current()->Notify( | |
| 171 chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, | |
| 172 content::Source<Profile>(profile_), | |
| 173 content::Details<const std::string>(&extension_id)); | |
| 174 } | |
| 175 | |
| 176 void AppNotificationManager::Observe( | |
| 177 int type, | |
| 178 const content::NotificationSource& source, | |
| 179 const content::NotificationDetails& details) { | |
| 180 CHECK(type == chrome::NOTIFICATION_EXTENSION_UNINSTALLED); | |
| 181 ClearAll(content::Details<const Extension>(details).ptr()->id()); | |
| 182 } | |
| 183 | |
| 184 syncer::SyncDataList AppNotificationManager::GetAllSyncData( | |
| 185 syncer::ModelType type) const { | |
| 186 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 187 DCHECK(loaded()); | |
| 188 DCHECK_EQ(syncer::APP_NOTIFICATIONS, type); | |
| 189 syncer::SyncDataList data; | |
| 190 for (NotificationMap::const_iterator iter = notifications_->begin(); | |
| 191 iter != notifications_->end(); ++iter) { | |
| 192 | |
| 193 // Skip local notifications since they should not be synced. | |
| 194 const AppNotificationList list = (*iter).second; | |
| 195 for (AppNotificationList::const_iterator list_iter = list.begin(); | |
| 196 list_iter != list.end(); ++list_iter) { | |
| 197 const AppNotification* notification = (*list_iter).get(); | |
| 198 if (notification->is_local()) { | |
| 199 continue; | |
| 200 } | |
| 201 data.push_back(CreateSyncDataFromNotification(*notification)); | |
| 202 } | |
| 203 } | |
| 204 | |
| 205 return data; | |
| 206 } | |
| 207 | |
| 208 syncer::SyncError AppNotificationManager::ProcessSyncChanges( | |
| 209 const tracked_objects::Location& from_here, | |
| 210 const syncer::SyncChangeList& change_list) { | |
| 211 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 212 DCHECK(loaded()); | |
| 213 if (!models_associated_) { | |
| 214 return sync_error_factory_->CreateAndUploadError( | |
| 215 FROM_HERE, | |
| 216 "Models not yet associated."); | |
| 217 } | |
| 218 | |
| 219 base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true); | |
| 220 | |
| 221 syncer::SyncError error; | |
| 222 for (syncer::SyncChangeList::const_iterator iter = change_list.begin(); | |
| 223 iter != change_list.end(); ++iter) { | |
| 224 syncer::SyncData sync_data = iter->sync_data(); | |
| 225 DCHECK_EQ(syncer::APP_NOTIFICATIONS, sync_data.GetDataType()); | |
| 226 syncer::SyncChange::SyncChangeType change_type = iter->change_type(); | |
| 227 | |
| 228 scoped_ptr<AppNotification> new_notif(CreateNotificationFromSyncData( | |
| 229 sync_data)); | |
| 230 if (!new_notif.get()) { | |
| 231 NOTREACHED() << "Failed to read notification."; | |
| 232 continue; | |
| 233 } | |
| 234 const AppNotification* existing_notif = GetNotification( | |
| 235 new_notif->extension_id(), new_notif->guid()); | |
| 236 if (existing_notif && existing_notif->is_local()) { | |
| 237 NOTREACHED() << "Matched with notification marked as local"; | |
| 238 error = sync_error_factory_->CreateAndUploadError( | |
| 239 FROM_HERE, | |
| 240 "ProcessSyncChanges received a local only notification" + | |
| 241 syncer::SyncChange::ChangeTypeToString(change_type)); | |
| 242 continue; | |
| 243 } | |
| 244 | |
| 245 switch (change_type) { | |
| 246 case syncer::SyncChange::ACTION_ADD: | |
| 247 if (!existing_notif) { | |
| 248 Add(new_notif.release()); | |
| 249 } else { | |
| 250 DLOG(ERROR) << "Got ADD change for an existing item.\n" | |
| 251 << "Existing item: " << existing_notif->ToString() | |
| 252 << "\nItem in ADD change: " << new_notif->ToString(); | |
| 253 } | |
| 254 break; | |
| 255 case syncer::SyncChange::ACTION_DELETE: | |
| 256 if (existing_notif) { | |
| 257 Remove(new_notif->extension_id(), new_notif->guid()); | |
| 258 } else { | |
| 259 // This should never happen. But we are seeting this sometimes, and | |
| 260 // it stops all of sync. See bug http://crbug.com/108088 | |
| 261 // So until we figure out the root cause, log an error and ignore. | |
| 262 DLOG(ERROR) << "Got DELETE change for non-existing item.\n" | |
| 263 << "Item in DELETE change: " << new_notif->ToString(); | |
| 264 } | |
| 265 break; | |
| 266 case syncer::SyncChange::ACTION_UPDATE: | |
| 267 // Although app notifications are immutable from the model perspective, | |
| 268 // sync can send UPDATE changes due to encryption / meta-data changes. | |
| 269 // So ignore UPDATE changes when the exitsing and new notification | |
| 270 // objects are the same. Log an error otherwise. | |
| 271 if (!existing_notif) { | |
| 272 DLOG(ERROR) << "Got UPDATE change for non-existing item." | |
| 273 << "Item in UPDATE change: " << new_notif->ToString(); | |
| 274 } else if (!existing_notif->Equals(*new_notif)) { | |
| 275 DLOG(ERROR) << "Got invalid UPDATE change:" | |
| 276 << "New and existing notifications should be the same.\n" | |
| 277 << "Existing item: " << existing_notif->ToString() << "\n" | |
| 278 << "Item in UPDATE change: " << new_notif->ToString(); | |
| 279 } | |
| 280 break; | |
| 281 default: | |
| 282 break; | |
| 283 } | |
| 284 } | |
| 285 | |
| 286 return error; | |
| 287 } | |
| 288 | |
| 289 syncer::SyncMergeResult AppNotificationManager::MergeDataAndStartSyncing( | |
| 290 syncer::ModelType type, | |
| 291 const syncer::SyncDataList& initial_sync_data, | |
| 292 scoped_ptr<syncer::SyncChangeProcessor> sync_processor, | |
| 293 scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) { | |
| 294 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 295 syncer::SyncMergeResult merge_result(type); | |
| 296 // AppNotificationDataTypeController ensures that modei is fully should before | |
| 297 // this method is called by waiting until the load notification is received | |
| 298 // from AppNotificationManager. | |
| 299 DCHECK(loaded()); | |
| 300 DCHECK_EQ(type, syncer::APP_NOTIFICATIONS); | |
| 301 DCHECK(!sync_processor_.get()); | |
| 302 DCHECK(sync_processor.get()); | |
| 303 DCHECK(sync_error_factory.get()); | |
| 304 sync_processor_ = sync_processor.Pass(); | |
| 305 sync_error_factory_ = sync_error_factory.Pass(); | |
| 306 | |
| 307 // We may add, or remove notifications here, so ensure we don't step on | |
| 308 // our own toes. | |
| 309 base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true); | |
| 310 | |
| 311 SyncDataMap local_data_map; | |
| 312 PopulateGuidToSyncDataMap(GetAllSyncData(syncer::APP_NOTIFICATIONS), | |
| 313 &local_data_map); | |
| 314 | |
| 315 for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin(); | |
| 316 iter != initial_sync_data.end(); ++iter) { | |
| 317 const syncer::SyncData& sync_data = *iter; | |
| 318 DCHECK_EQ(syncer::APP_NOTIFICATIONS, sync_data.GetDataType()); | |
| 319 scoped_ptr<AppNotification> sync_notif(CreateNotificationFromSyncData( | |
| 320 sync_data)); | |
| 321 CHECK(sync_notif.get()); | |
| 322 const AppNotification* local_notif = GetNotification( | |
| 323 sync_notif->extension_id(), sync_notif->guid()); | |
| 324 if (local_notif) { | |
| 325 local_data_map.erase(sync_notif->guid()); | |
| 326 // Local notification should always match with sync notification as | |
| 327 // notifications are immutable. | |
| 328 if (local_notif->is_local() || !sync_notif->Equals(*local_notif)) { | |
| 329 merge_result.set_error(sync_error_factory_->CreateAndUploadError( | |
| 330 FROM_HERE, | |
| 331 "MergeDataAndStartSyncing failed: local notification and sync " | |
| 332 "notification have same guid but different data.")); | |
| 333 return merge_result; | |
| 334 } | |
| 335 } else { | |
| 336 // Sync model has a notification that local model does not, add it. | |
| 337 Add(sync_notif.release()); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 // TODO(munjal): crbug.com/10059. Work with Lingesh/Antony to resolve. | |
| 342 syncer::SyncChangeList new_changes; | |
| 343 for (SyncDataMap::const_iterator iter = local_data_map.begin(); | |
| 344 iter != local_data_map.end(); ++iter) { | |
| 345 new_changes.push_back( | |
| 346 syncer::SyncChange(FROM_HERE, | |
| 347 syncer::SyncChange::ACTION_ADD, | |
| 348 iter->second)); | |
| 349 } | |
| 350 | |
| 351 if (new_changes.size() > 0) { | |
| 352 merge_result.set_error( | |
| 353 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes)); | |
| 354 } | |
| 355 models_associated_ = !merge_result.error().IsSet(); | |
| 356 return merge_result; | |
| 357 } | |
| 358 | |
| 359 void AppNotificationManager::StopSyncing(syncer::ModelType type) { | |
| 360 DCHECK_EQ(type, syncer::APP_NOTIFICATIONS); | |
| 361 models_associated_ = false; | |
| 362 sync_processor_.reset(); | |
| 363 sync_error_factory_.reset(); | |
| 364 } | |
| 365 | |
| 366 AppNotificationManager::~AppNotificationManager() { | |
| 367 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 368 // Post a task to delete our storage on the file thread. | |
| 369 BrowserThread::DeleteSoon(BrowserThread::FILE, | |
| 370 FROM_HERE, | |
| 371 storage_.release()); | |
| 372 } | |
| 373 | |
| 374 void AppNotificationManager::LoadOnFileThread( | |
| 375 const base::FilePath& storage_path) { | |
| 376 PerfTimer timer; | |
| 377 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 378 DCHECK(!loaded()); | |
| 379 | |
| 380 storage_.reset(AppNotificationStorage::Create(storage_path)); | |
| 381 if (!storage_.get()) | |
| 382 return; | |
| 383 scoped_ptr<NotificationMap> result(new NotificationMap()); | |
| 384 std::set<std::string> ids; | |
| 385 if (!storage_->GetExtensionIds(&ids)) | |
| 386 return; | |
| 387 std::set<std::string>::const_iterator i; | |
| 388 for (i = ids.begin(); i != ids.end(); ++i) { | |
| 389 const std::string& id = *i; | |
| 390 AppNotificationList& list = (*result)[id]; | |
| 391 if (!storage_->Get(id, &list)) | |
| 392 result->erase(id); | |
| 393 } | |
| 394 | |
| 395 BrowserThread::PostTask( | |
| 396 BrowserThread::UI, | |
| 397 FROM_HERE, | |
| 398 base::Bind(&AppNotificationManager::HandleLoadResults, | |
| 399 this, result.release())); | |
| 400 | |
| 401 UMA_HISTOGRAM_LONG_TIMES("AppNotification.MgrFileThreadLoadTime", | |
| 402 timer.Elapsed()); | |
| 403 } | |
| 404 | |
| 405 void AppNotificationManager::HandleLoadResults(NotificationMap* map) { | |
| 406 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 407 DCHECK(map); | |
| 408 DCHECK(!loaded()); | |
| 409 notifications_.reset(map); | |
| 410 UMA_HISTOGRAM_LONG_TIMES("AppNotification.MgrLoadDelay", | |
| 411 load_timer_->Elapsed()); | |
| 412 load_timer_.reset(); | |
| 413 | |
| 414 // Generate STATE_CHANGED notifications for extensions that have at | |
| 415 // least one notification loaded. | |
| 416 int app_count = 0; | |
| 417 int notification_count = 0; | |
| 418 NotificationMap::const_iterator i; | |
| 419 for (i = map->begin(); i != map->end(); ++i) { | |
| 420 const std::string& id = i->first; | |
| 421 if (i->second.empty()) | |
| 422 continue; | |
| 423 app_count++; | |
| 424 notification_count += i->second.size(); | |
| 425 content::NotificationService::current()->Notify( | |
| 426 chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, | |
| 427 content::Source<Profile>(profile_), | |
| 428 content::Details<const std::string>(&id)); | |
| 429 } | |
| 430 UMA_HISTOGRAM_COUNTS("AppNotification.MgrLoadAppCount", app_count); | |
| 431 UMA_HISTOGRAM_COUNTS("AppNotification.MgrLoadTotalCount", | |
| 432 notification_count); | |
| 433 | |
| 434 // Generate MANAGER_LOADED notification. | |
| 435 content::NotificationService::current()->Notify( | |
| 436 chrome::NOTIFICATION_APP_NOTIFICATION_MANAGER_LOADED, | |
| 437 content::Source<AppNotificationManager>(this), | |
| 438 content::NotificationService::NoDetails()); | |
| 439 } | |
| 440 | |
| 441 void AppNotificationManager::SaveOnFileThread(const std::string& extension_id, | |
| 442 AppNotificationList* list) { | |
| 443 // Own the |list|. | |
| 444 scoped_ptr<AppNotificationList> scoped_list(list); | |
| 445 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 446 storage_->Set(extension_id, *scoped_list); | |
| 447 } | |
| 448 | |
| 449 void AppNotificationManager::DeleteOnFileThread( | |
| 450 const std::string& extension_id) { | |
| 451 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 452 storage_->Delete(extension_id); | |
| 453 } | |
| 454 | |
| 455 AppNotificationList& AppNotificationManager::GetAllInternal( | |
| 456 const std::string& extension_id) { | |
| 457 NotificationMap::iterator found = notifications_->find(extension_id); | |
| 458 if (found == notifications_->end()) { | |
| 459 (*notifications_)[extension_id] = AppNotificationList(); | |
| 460 found = notifications_->find(extension_id); | |
| 461 } | |
| 462 CHECK(found != notifications_->end()); | |
| 463 return found->second; | |
| 464 } | |
| 465 | |
| 466 void AppNotificationManager::Remove(const std::string& extension_id, | |
| 467 const std::string& guid) { | |
| 468 DCHECK(loaded()); | |
| 469 AppNotificationList& list = GetAllInternal(extension_id); | |
| 470 RemoveByGuid(&list, guid); | |
| 471 | |
| 472 if (storage_.get()) { | |
| 473 BrowserThread::PostTask( | |
| 474 BrowserThread::FILE, | |
| 475 FROM_HERE, | |
| 476 base::Bind(&AppNotificationManager::SaveOnFileThread, | |
| 477 this, extension_id, CopyAppNotificationList(list))); | |
| 478 } | |
| 479 | |
| 480 content::NotificationService::current()->Notify( | |
| 481 chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, | |
| 482 content::Source<Profile>(profile_), | |
| 483 content::Details<const std::string>(&extension_id)); | |
| 484 } | |
| 485 | |
| 486 const AppNotification* AppNotificationManager::GetNotification( | |
| 487 const std::string& extension_id, const std::string& guid) { | |
| 488 DCHECK(loaded()); | |
| 489 const AppNotificationList& list = GetAllInternal(extension_id); | |
| 490 return FindByGuid(list, guid); | |
| 491 } | |
| 492 | |
| 493 void AppNotificationManager::SyncAddChange(const AppNotification& notif) { | |
| 494 // Skip if either: | |
| 495 // - Notification is marked as local. | |
| 496 // - Sync is not enabled by user. | |
| 497 // - Change is generated from within the manager. | |
| 498 if (notif.is_local() || !models_associated_ || processing_syncer_changes_) | |
| 499 return; | |
| 500 | |
| 501 // TODO(munjal): crbug.com/10059. Work with Lingesh/Antony to resolve. | |
| 502 | |
| 503 syncer::SyncChangeList changes; | |
| 504 syncer::SyncData sync_data = CreateSyncDataFromNotification(notif); | |
| 505 changes.push_back( | |
| 506 syncer::SyncChange(FROM_HERE, | |
| 507 syncer::SyncChange::ACTION_ADD, | |
| 508 sync_data)); | |
| 509 sync_processor_->ProcessSyncChanges(FROM_HERE, changes); | |
| 510 } | |
| 511 | |
| 512 void AppNotificationManager::SyncRemoveChange(const AppNotification& notif) { | |
| 513 // Skip if either: | |
| 514 // - Sync is not enabled by user. | |
| 515 // - Change is generated from within the manager. | |
| 516 if (notif.is_local() || !models_associated_) { | |
| 517 return; | |
| 518 } | |
| 519 | |
| 520 syncer::SyncChangeList changes; | |
| 521 syncer::SyncData sync_data = CreateSyncDataFromNotification(notif); | |
| 522 changes.push_back( | |
| 523 syncer::SyncChange(FROM_HERE, | |
| 524 syncer::SyncChange::ACTION_DELETE, | |
| 525 sync_data)); | |
| 526 sync_processor_->ProcessSyncChanges(FROM_HERE, changes); | |
| 527 } | |
| 528 | |
| 529 void AppNotificationManager::SyncClearAllChange( | |
| 530 const AppNotificationList& list) { | |
| 531 // Skip if either: | |
| 532 // - Sync is not enabled by user. | |
| 533 // - Change is generated from within the manager. | |
| 534 if (!models_associated_ || processing_syncer_changes_) | |
| 535 return; | |
| 536 | |
| 537 syncer::SyncChangeList changes; | |
| 538 for (AppNotificationList::const_iterator iter = list.begin(); | |
| 539 iter != list.end(); ++iter) { | |
| 540 const AppNotification& notif = *iter->get(); | |
| 541 // Skip notifications marked as local. | |
| 542 if (notif.is_local()) | |
| 543 continue; | |
| 544 changes.push_back(syncer::SyncChange( | |
| 545 FROM_HERE, | |
| 546 syncer::SyncChange::ACTION_DELETE, | |
| 547 CreateSyncDataFromNotification(notif))); | |
| 548 } | |
| 549 sync_processor_->ProcessSyncChanges(FROM_HERE, changes); | |
| 550 } | |
| 551 | |
| 552 // static | |
| 553 syncer::SyncData AppNotificationManager::CreateSyncDataFromNotification( | |
| 554 const AppNotification& notification) { | |
| 555 DCHECK(!notification.is_local()); | |
| 556 sync_pb::EntitySpecifics specifics; | |
| 557 sync_pb::AppNotification* notif_specifics = | |
| 558 specifics.mutable_app_notification(); | |
| 559 notif_specifics->set_app_id(notification.extension_id()); | |
| 560 notif_specifics->set_creation_timestamp_ms( | |
| 561 notification.creation_time().ToInternalValue()); | |
| 562 notif_specifics->set_body_text(notification.body()); | |
| 563 notif_specifics->set_guid(notification.guid()); | |
| 564 notif_specifics->set_link_text(notification.link_text()); | |
| 565 notif_specifics->set_link_url(notification.link_url().spec()); | |
| 566 notif_specifics->set_title(notification.title()); | |
| 567 return syncer::SyncData::CreateLocalData( | |
| 568 notif_specifics->guid(), notif_specifics->app_id(), specifics); | |
| 569 } | |
| 570 | |
| 571 // static | |
| 572 AppNotification* AppNotificationManager::CreateNotificationFromSyncData( | |
| 573 const syncer::SyncData& sync_data) { | |
| 574 sync_pb::AppNotification specifics = | |
| 575 sync_data.GetSpecifics().app_notification(); | |
| 576 | |
| 577 // Check for mandatory fields. | |
| 578 if (!specifics.has_app_id() || !specifics.has_guid() || | |
| 579 !specifics.has_title() || !specifics.has_body_text() || | |
| 580 !specifics.has_creation_timestamp_ms()) { | |
| 581 return NULL; | |
| 582 } | |
| 583 | |
| 584 AppNotification* notification = new AppNotification( | |
| 585 false, base::Time::FromInternalValue(specifics.creation_timestamp_ms()), | |
| 586 specifics.guid(), specifics.app_id(), | |
| 587 specifics.title(), specifics.body_text()); | |
| 588 if (specifics.has_link_text()) | |
| 589 notification->set_link_text(specifics.link_text()); | |
| 590 if (specifics.has_link_url()) | |
| 591 notification->set_link_url(GURL(specifics.link_url())); | |
| 592 return notification; | |
| 593 } | |
| 594 | |
| 595 } // namespace extensions | |
| OLD | NEW |