| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/extensions/app_notification_manager.h" | 5 #include "chrome/browser/extensions/app_notification_manager.h" |
| 6 | 6 |
| 7 #include "base/auto_reset.h" | 7 #include "base/auto_reset.h" |
| 8 #include "base/bind.h" | 8 #include "base/bind.h" |
| 9 #include "base/file_path.h" | 9 #include "base/file_path.h" |
| 10 #include "base/location.h" | 10 #include "base/location.h" |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 | 67 |
| 68 AppNotificationManager::AppNotificationManager(Profile* profile) | 68 AppNotificationManager::AppNotificationManager(Profile* profile) |
| 69 : profile_(profile), | 69 : profile_(profile), |
| 70 models_associated_(false), | 70 models_associated_(false), |
| 71 processing_syncer_changes_(false) { | 71 processing_syncer_changes_(false) { |
| 72 registrar_.Add(this, | 72 registrar_.Add(this, |
| 73 chrome::NOTIFICATION_EXTENSION_UNINSTALLED, | 73 chrome::NOTIFICATION_EXTENSION_UNINSTALLED, |
| 74 content::Source<Profile>(profile_)); | 74 content::Source<Profile>(profile_)); |
| 75 } | 75 } |
| 76 | 76 |
| 77 AppNotificationManager::~AppNotificationManager() { | |
| 78 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 79 // Post a task to delete our storage on the file thread. | |
| 80 BrowserThread::DeleteSoon(BrowserThread::FILE, | |
| 81 FROM_HERE, | |
| 82 storage_.release()); | |
| 83 } | |
| 84 | |
| 85 void AppNotificationManager::Init() { | 77 void AppNotificationManager::Init() { |
| 86 FilePath storage_path = profile_->GetPath().AppendASCII("App Notifications"); | 78 FilePath storage_path = profile_->GetPath().AppendASCII("App Notifications"); |
| 87 load_timer_.reset(new PerfTimer()); | 79 load_timer_.reset(new PerfTimer()); |
| 88 BrowserThread::PostTask( | 80 BrowserThread::PostTask( |
| 89 BrowserThread::FILE, | 81 BrowserThread::FILE, |
| 90 FROM_HERE, | 82 FROM_HERE, |
| 91 base::Bind(&AppNotificationManager::LoadOnFileThread, | 83 base::Bind(&AppNotificationManager::LoadOnFileThread, |
| 92 this, storage_path)); | 84 this, storage_path)); |
| 93 } | 85 } |
| 94 | 86 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 135 | 127 |
| 136 const AppNotificationList* AppNotificationManager::GetAll( | 128 const AppNotificationList* AppNotificationManager::GetAll( |
| 137 const std::string& extension_id) const { | 129 const std::string& extension_id) const { |
| 138 if (!loaded()) | 130 if (!loaded()) |
| 139 return NULL; | 131 return NULL; |
| 140 if (ContainsKey(*notifications_, extension_id)) | 132 if (ContainsKey(*notifications_, extension_id)) |
| 141 return &((*notifications_)[extension_id]); | 133 return &((*notifications_)[extension_id]); |
| 142 return NULL; | 134 return NULL; |
| 143 } | 135 } |
| 144 | 136 |
| 145 AppNotificationList& AppNotificationManager::GetAllInternal( | |
| 146 const std::string& extension_id) { | |
| 147 NotificationMap::iterator found = notifications_->find(extension_id); | |
| 148 if (found == notifications_->end()) { | |
| 149 (*notifications_)[extension_id] = AppNotificationList(); | |
| 150 found = notifications_->find(extension_id); | |
| 151 } | |
| 152 CHECK(found != notifications_->end()); | |
| 153 return found->second; | |
| 154 } | |
| 155 | |
| 156 const AppNotification* AppNotificationManager::GetLast( | 137 const AppNotification* AppNotificationManager::GetLast( |
| 157 const std::string& extension_id) { | 138 const std::string& extension_id) { |
| 158 if (!loaded()) | 139 if (!loaded()) |
| 159 return NULL; | 140 return NULL; |
| 160 NotificationMap::iterator found = notifications_->find(extension_id); | 141 NotificationMap::iterator found = notifications_->find(extension_id); |
| 161 if (found == notifications_->end()) | 142 if (found == notifications_->end()) |
| 162 return NULL; | 143 return NULL; |
| 163 const AppNotificationList& list = found->second; | 144 const AppNotificationList& list = found->second; |
| 164 if (list.empty()) | 145 if (list.empty()) |
| 165 return NULL; | 146 return NULL; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 190 } | 171 } |
| 191 | 172 |
| 192 void AppNotificationManager::Observe( | 173 void AppNotificationManager::Observe( |
| 193 int type, | 174 int type, |
| 194 const content::NotificationSource& source, | 175 const content::NotificationSource& source, |
| 195 const content::NotificationDetails& details) { | 176 const content::NotificationDetails& details) { |
| 196 CHECK(type == chrome::NOTIFICATION_EXTENSION_UNINSTALLED); | 177 CHECK(type == chrome::NOTIFICATION_EXTENSION_UNINSTALLED); |
| 197 ClearAll(*content::Details<const std::string>(details).ptr()); | 178 ClearAll(*content::Details<const std::string>(details).ptr()); |
| 198 } | 179 } |
| 199 | 180 |
| 200 void AppNotificationManager::LoadOnFileThread(const FilePath& storage_path) { | |
| 201 PerfTimer timer; | |
| 202 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 203 DCHECK(!loaded()); | |
| 204 | |
| 205 storage_.reset(AppNotificationStorage::Create(storage_path)); | |
| 206 if (!storage_.get()) | |
| 207 return; | |
| 208 scoped_ptr<NotificationMap> result(new NotificationMap()); | |
| 209 std::set<std::string> ids; | |
| 210 if (!storage_->GetExtensionIds(&ids)) | |
| 211 return; | |
| 212 std::set<std::string>::const_iterator i; | |
| 213 for (i = ids.begin(); i != ids.end(); ++i) { | |
| 214 const std::string& id = *i; | |
| 215 AppNotificationList& list = (*result)[id]; | |
| 216 if (!storage_->Get(id, &list)) | |
| 217 result->erase(id); | |
| 218 } | |
| 219 | |
| 220 BrowserThread::PostTask( | |
| 221 BrowserThread::UI, | |
| 222 FROM_HERE, | |
| 223 base::Bind(&AppNotificationManager::HandleLoadResults, | |
| 224 this, result.release())); | |
| 225 | |
| 226 UMA_HISTOGRAM_LONG_TIMES("AppNotification.MgrFileThreadLoadTime", | |
| 227 timer.Elapsed()); | |
| 228 } | |
| 229 | |
| 230 void AppNotificationManager::HandleLoadResults(NotificationMap* map) { | |
| 231 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 232 DCHECK(map); | |
| 233 DCHECK(!loaded()); | |
| 234 notifications_.reset(map); | |
| 235 UMA_HISTOGRAM_LONG_TIMES("AppNotification.MgrLoadDelay", | |
| 236 load_timer_->Elapsed()); | |
| 237 load_timer_.reset(); | |
| 238 | |
| 239 // Generate STATE_CHANGED notifications for extensions that have at | |
| 240 // least one notification loaded. | |
| 241 int app_count = 0; | |
| 242 int notification_count = 0; | |
| 243 NotificationMap::const_iterator i; | |
| 244 for (i = map->begin(); i != map->end(); ++i) { | |
| 245 const std::string& id = i->first; | |
| 246 if (i->second.empty()) | |
| 247 continue; | |
| 248 app_count++; | |
| 249 notification_count += i->second.size(); | |
| 250 content::NotificationService::current()->Notify( | |
| 251 chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, | |
| 252 content::Source<Profile>(profile_), | |
| 253 content::Details<const std::string>(&id)); | |
| 254 } | |
| 255 UMA_HISTOGRAM_COUNTS("AppNotification.MgrLoadAppCount", app_count); | |
| 256 UMA_HISTOGRAM_COUNTS("AppNotification.MgrLoadTotalCount", | |
| 257 notification_count); | |
| 258 | |
| 259 // Generate MANAGER_LOADED notification. | |
| 260 content::NotificationService::current()->Notify( | |
| 261 chrome::NOTIFICATION_APP_NOTIFICATION_MANAGER_LOADED, | |
| 262 content::Source<AppNotificationManager>(this), | |
| 263 content::NotificationService::NoDetails()); | |
| 264 } | |
| 265 | |
| 266 void AppNotificationManager::SaveOnFileThread(const std::string& extension_id, | |
| 267 AppNotificationList* list) { | |
| 268 // Own the |list|. | |
| 269 scoped_ptr<AppNotificationList> scoped_list(list); | |
| 270 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 271 storage_->Set(extension_id, *scoped_list); | |
| 272 } | |
| 273 | |
| 274 void AppNotificationManager::DeleteOnFileThread( | |
| 275 const std::string& extension_id) { | |
| 276 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 277 storage_->Delete(extension_id); | |
| 278 } | |
| 279 | |
| 280 SyncDataList AppNotificationManager::GetAllSyncData( | 181 SyncDataList AppNotificationManager::GetAllSyncData( |
| 281 syncable::ModelType type) const { | 182 syncable::ModelType type) const { |
| 282 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 183 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 283 DCHECK(loaded()); | 184 DCHECK(loaded()); |
| 284 DCHECK_EQ(syncable::APP_NOTIFICATIONS, type); | 185 DCHECK_EQ(syncable::APP_NOTIFICATIONS, type); |
| 285 SyncDataList data; | 186 SyncDataList data; |
| 286 for (NotificationMap::const_iterator iter = notifications_->begin(); | 187 for (NotificationMap::const_iterator iter = notifications_->begin(); |
| 287 iter != notifications_->end(); ++iter) { | 188 iter != notifications_->end(); ++iter) { |
| 288 | 189 |
| 289 // Skip local notifications since they should not be synced. | 190 // Skip local notifications since they should not be synced. |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 446 return error; | 347 return error; |
| 447 } | 348 } |
| 448 | 349 |
| 449 void AppNotificationManager::StopSyncing(syncable::ModelType type) { | 350 void AppNotificationManager::StopSyncing(syncable::ModelType type) { |
| 450 DCHECK_EQ(type, syncable::APP_NOTIFICATIONS); | 351 DCHECK_EQ(type, syncable::APP_NOTIFICATIONS); |
| 451 models_associated_ = false; | 352 models_associated_ = false; |
| 452 sync_processor_.reset(); | 353 sync_processor_.reset(); |
| 453 sync_error_factory_.reset(); | 354 sync_error_factory_.reset(); |
| 454 } | 355 } |
| 455 | 356 |
| 357 AppNotificationManager::~AppNotificationManager() { |
| 358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 359 // Post a task to delete our storage on the file thread. |
| 360 BrowserThread::DeleteSoon(BrowserThread::FILE, |
| 361 FROM_HERE, |
| 362 storage_.release()); |
| 363 } |
| 364 |
| 365 void AppNotificationManager::LoadOnFileThread(const FilePath& storage_path) { |
| 366 PerfTimer timer; |
| 367 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 368 DCHECK(!loaded()); |
| 369 |
| 370 storage_.reset(AppNotificationStorage::Create(storage_path)); |
| 371 if (!storage_.get()) |
| 372 return; |
| 373 scoped_ptr<NotificationMap> result(new NotificationMap()); |
| 374 std::set<std::string> ids; |
| 375 if (!storage_->GetExtensionIds(&ids)) |
| 376 return; |
| 377 std::set<std::string>::const_iterator i; |
| 378 for (i = ids.begin(); i != ids.end(); ++i) { |
| 379 const std::string& id = *i; |
| 380 AppNotificationList& list = (*result)[id]; |
| 381 if (!storage_->Get(id, &list)) |
| 382 result->erase(id); |
| 383 } |
| 384 |
| 385 BrowserThread::PostTask( |
| 386 BrowserThread::UI, |
| 387 FROM_HERE, |
| 388 base::Bind(&AppNotificationManager::HandleLoadResults, |
| 389 this, result.release())); |
| 390 |
| 391 UMA_HISTOGRAM_LONG_TIMES("AppNotification.MgrFileThreadLoadTime", |
| 392 timer.Elapsed()); |
| 393 } |
| 394 |
| 395 void AppNotificationManager::HandleLoadResults(NotificationMap* map) { |
| 396 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 397 DCHECK(map); |
| 398 DCHECK(!loaded()); |
| 399 notifications_.reset(map); |
| 400 UMA_HISTOGRAM_LONG_TIMES("AppNotification.MgrLoadDelay", |
| 401 load_timer_->Elapsed()); |
| 402 load_timer_.reset(); |
| 403 |
| 404 // Generate STATE_CHANGED notifications for extensions that have at |
| 405 // least one notification loaded. |
| 406 int app_count = 0; |
| 407 int notification_count = 0; |
| 408 NotificationMap::const_iterator i; |
| 409 for (i = map->begin(); i != map->end(); ++i) { |
| 410 const std::string& id = i->first; |
| 411 if (i->second.empty()) |
| 412 continue; |
| 413 app_count++; |
| 414 notification_count += i->second.size(); |
| 415 content::NotificationService::current()->Notify( |
| 416 chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, |
| 417 content::Source<Profile>(profile_), |
| 418 content::Details<const std::string>(&id)); |
| 419 } |
| 420 UMA_HISTOGRAM_COUNTS("AppNotification.MgrLoadAppCount", app_count); |
| 421 UMA_HISTOGRAM_COUNTS("AppNotification.MgrLoadTotalCount", |
| 422 notification_count); |
| 423 |
| 424 // Generate MANAGER_LOADED notification. |
| 425 content::NotificationService::current()->Notify( |
| 426 chrome::NOTIFICATION_APP_NOTIFICATION_MANAGER_LOADED, |
| 427 content::Source<AppNotificationManager>(this), |
| 428 content::NotificationService::NoDetails()); |
| 429 } |
| 430 |
| 431 void AppNotificationManager::SaveOnFileThread(const std::string& extension_id, |
| 432 AppNotificationList* list) { |
| 433 // Own the |list|. |
| 434 scoped_ptr<AppNotificationList> scoped_list(list); |
| 435 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 436 storage_->Set(extension_id, *scoped_list); |
| 437 } |
| 438 |
| 439 void AppNotificationManager::DeleteOnFileThread( |
| 440 const std::string& extension_id) { |
| 441 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 442 storage_->Delete(extension_id); |
| 443 } |
| 444 |
| 445 AppNotificationList& AppNotificationManager::GetAllInternal( |
| 446 const std::string& extension_id) { |
| 447 NotificationMap::iterator found = notifications_->find(extension_id); |
| 448 if (found == notifications_->end()) { |
| 449 (*notifications_)[extension_id] = AppNotificationList(); |
| 450 found = notifications_->find(extension_id); |
| 451 } |
| 452 CHECK(found != notifications_->end()); |
| 453 return found->second; |
| 454 } |
| 455 |
| 456 void AppNotificationManager::Remove(const std::string& extension_id, |
| 457 const std::string& guid) { |
| 458 DCHECK(loaded()); |
| 459 AppNotificationList& list = GetAllInternal(extension_id); |
| 460 RemoveByGuid(&list, guid); |
| 461 |
| 462 if (storage_.get()) { |
| 463 BrowserThread::PostTask( |
| 464 BrowserThread::FILE, |
| 465 FROM_HERE, |
| 466 base::Bind(&AppNotificationManager::SaveOnFileThread, |
| 467 this, extension_id, CopyAppNotificationList(list))); |
| 468 } |
| 469 |
| 470 content::NotificationService::current()->Notify( |
| 471 chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, |
| 472 content::Source<Profile>(profile_), |
| 473 content::Details<const std::string>(&extension_id)); |
| 474 } |
| 475 |
| 476 const AppNotification* AppNotificationManager::GetNotification( |
| 477 const std::string& extension_id, const std::string& guid) { |
| 478 DCHECK(loaded()); |
| 479 const AppNotificationList& list = GetAllInternal(extension_id); |
| 480 return FindByGuid(list, guid); |
| 481 } |
| 482 |
| 456 void AppNotificationManager::SyncAddChange(const AppNotification& notif) { | 483 void AppNotificationManager::SyncAddChange(const AppNotification& notif) { |
| 457 // Skip if either: | 484 // Skip if either: |
| 458 // - Notification is marked as local. | 485 // - Notification is marked as local. |
| 459 // - Sync is not enabled by user. | 486 // - Sync is not enabled by user. |
| 460 // - Change is generated from within the manager. | 487 // - Change is generated from within the manager. |
| 461 if (notif.is_local() || !models_associated_ || processing_syncer_changes_) | 488 if (notif.is_local() || !models_associated_ || processing_syncer_changes_) |
| 462 return; | 489 return; |
| 463 | 490 |
| 464 // TODO(munjal): crbug.com/10059. Work with Lingesh/Antony to resolve. | 491 // TODO(munjal): crbug.com/10059. Work with Lingesh/Antony to resolve. |
| 465 | 492 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 498 // Skip notifications marked as local. | 525 // Skip notifications marked as local. |
| 499 if (notif.is_local()) | 526 if (notif.is_local()) |
| 500 continue; | 527 continue; |
| 501 changes.push_back(SyncChange( | 528 changes.push_back(SyncChange( |
| 502 SyncChange::ACTION_DELETE, | 529 SyncChange::ACTION_DELETE, |
| 503 CreateSyncDataFromNotification(notif))); | 530 CreateSyncDataFromNotification(notif))); |
| 504 } | 531 } |
| 505 sync_processor_->ProcessSyncChanges(FROM_HERE, changes); | 532 sync_processor_->ProcessSyncChanges(FROM_HERE, changes); |
| 506 } | 533 } |
| 507 | 534 |
| 508 const AppNotification* AppNotificationManager::GetNotification( | |
| 509 const std::string& extension_id, const std::string& guid) { | |
| 510 DCHECK(loaded()); | |
| 511 const AppNotificationList& list = GetAllInternal(extension_id); | |
| 512 return FindByGuid(list, guid); | |
| 513 } | |
| 514 | |
| 515 void AppNotificationManager::Remove(const std::string& extension_id, | |
| 516 const std::string& guid) { | |
| 517 DCHECK(loaded()); | |
| 518 AppNotificationList& list = GetAllInternal(extension_id); | |
| 519 RemoveByGuid(&list, guid); | |
| 520 | |
| 521 if (storage_.get()) { | |
| 522 BrowserThread::PostTask( | |
| 523 BrowserThread::FILE, | |
| 524 FROM_HERE, | |
| 525 base::Bind(&AppNotificationManager::SaveOnFileThread, | |
| 526 this, extension_id, CopyAppNotificationList(list))); | |
| 527 } | |
| 528 | |
| 529 content::NotificationService::current()->Notify( | |
| 530 chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, | |
| 531 content::Source<Profile>(profile_), | |
| 532 content::Details<const std::string>(&extension_id)); | |
| 533 } | |
| 534 | |
| 535 // static | 535 // static |
| 536 SyncData AppNotificationManager::CreateSyncDataFromNotification( | 536 SyncData AppNotificationManager::CreateSyncDataFromNotification( |
| 537 const AppNotification& notification) { | 537 const AppNotification& notification) { |
| 538 DCHECK(!notification.is_local()); | 538 DCHECK(!notification.is_local()); |
| 539 sync_pb::EntitySpecifics specifics; | 539 sync_pb::EntitySpecifics specifics; |
| 540 sync_pb::AppNotification* notif_specifics = | 540 sync_pb::AppNotification* notif_specifics = |
| 541 specifics.mutable_app_notification(); | 541 specifics.mutable_app_notification(); |
| 542 notif_specifics->set_app_id(notification.extension_id()); | 542 notif_specifics->set_app_id(notification.extension_id()); |
| 543 notif_specifics->set_creation_timestamp_ms( | 543 notif_specifics->set_creation_timestamp_ms( |
| 544 notification.creation_time().ToInternalValue()); | 544 notification.creation_time().ToInternalValue()); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 567 AppNotification* notification = new AppNotification( | 567 AppNotification* notification = new AppNotification( |
| 568 false, base::Time::FromInternalValue(specifics.creation_timestamp_ms()), | 568 false, base::Time::FromInternalValue(specifics.creation_timestamp_ms()), |
| 569 specifics.guid(), specifics.app_id(), | 569 specifics.guid(), specifics.app_id(), |
| 570 specifics.title(), specifics.body_text()); | 570 specifics.title(), specifics.body_text()); |
| 571 if (specifics.has_link_text()) | 571 if (specifics.has_link_text()) |
| 572 notification->set_link_text(specifics.link_text()); | 572 notification->set_link_text(specifics.link_text()); |
| 573 if (specifics.has_link_url()) | 573 if (specifics.has_link_url()) |
| 574 notification->set_link_url(GURL(specifics.link_url())); | 574 notification->set_link_url(GURL(specifics.link_url())); |
| 575 return notification; | 575 return notification; |
| 576 } | 576 } |
| OLD | NEW |