Index: chrome/browser/extensions/app_notification_manager.cc |
diff --git a/chrome/browser/extensions/app_notification_manager.cc b/chrome/browser/extensions/app_notification_manager.cc |
deleted file mode 100644 |
index 2432cde0c363721c05fa2a3542756934b8898912..0000000000000000000000000000000000000000 |
--- a/chrome/browser/extensions/app_notification_manager.cc |
+++ /dev/null |
@@ -1,595 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "chrome/browser/extensions/app_notification_manager.h" |
- |
-#include "base/auto_reset.h" |
-#include "base/bind.h" |
-#include "base/files/file_path.h" |
-#include "base/location.h" |
-#include "base/metrics/histogram.h" |
-#include "base/perftimer.h" |
-#include "base/stl_util.h" |
-#include "base/time.h" |
-#include "chrome/browser/profiles/profile.h" |
-#include "chrome/common/chrome_notification_types.h" |
-#include "chrome/common/extensions/extension.h" |
-#include "content/public/browser/notification_service.h" |
-#include "sync/api/sync_error_factory.h" |
-#include "sync/protocol/app_notification_specifics.pb.h" |
-#include "sync/protocol/sync.pb.h" |
- |
-using content::BrowserThread; |
- |
-typedef std::map<std::string, syncer::SyncData> SyncDataMap; |
- |
-namespace extensions { |
- |
-namespace { |
- |
-class GuidComparator |
- : public std::binary_function<linked_ptr<AppNotification>, |
- std::string, |
- bool> { |
- public: |
- bool operator() (linked_ptr<AppNotification> notif, |
- const std::string& guid) const { |
- return notif->guid() == guid; |
- } |
-}; |
- |
-const AppNotification* FindByGuid(const AppNotificationList& list, |
- const std::string& guid) { |
- AppNotificationList::const_iterator iter = std::find_if( |
- list.begin(), list.end(), std::bind2nd(GuidComparator(), guid)); |
- return iter == list.end() ? NULL : iter->get(); |
-} |
- |
-void RemoveByGuid(AppNotificationList* list, const std::string& guid) { |
- if (!list) |
- return; |
- |
- AppNotificationList::iterator iter = std::find_if( |
- list->begin(), list->end(), std::bind2nd(GuidComparator(), guid)); |
- if (iter != list->end()) |
- list->erase(iter); |
-} |
- |
-void PopulateGuidToSyncDataMap(const syncer::SyncDataList& sync_data, |
- SyncDataMap* data_map) { |
- for (syncer::SyncDataList::const_iterator iter = sync_data.begin(); |
- iter != sync_data.end(); ++iter) { |
- (*data_map)[iter->GetSpecifics().app_notification().guid()] = *iter; |
- } |
-} |
-} // namespace |
- |
-const unsigned int AppNotificationManager::kMaxNotificationPerApp = 5; |
- |
-AppNotificationManager::AppNotificationManager(Profile* profile) |
- : profile_(profile), |
- models_associated_(false), |
- processing_syncer_changes_(false) { |
- registrar_.Add(this, |
- chrome::NOTIFICATION_EXTENSION_UNINSTALLED, |
- content::Source<Profile>(profile_)); |
-} |
- |
-void AppNotificationManager::Init() { |
- base::FilePath storage_path = |
- profile_->GetPath().AppendASCII("App Notifications"); |
- load_timer_.reset(new PerfTimer()); |
- BrowserThread::PostTask( |
- BrowserThread::FILE, |
- FROM_HERE, |
- base::Bind(&AppNotificationManager::LoadOnFileThread, |
- this, storage_path)); |
-} |
- |
-bool AppNotificationSortPredicate(const linked_ptr<AppNotification> a1, |
- const linked_ptr<AppNotification> a2) { |
- return a1.get()->creation_time() < a2.get()->creation_time(); |
-} |
- |
-bool AppNotificationManager::Add(AppNotification* item) { |
- // Do this first since we own the incoming item and hence want to delete |
- // it in error paths. |
- linked_ptr<AppNotification> linked_item(item); |
- if (!loaded()) |
- return false; |
- const std::string& extension_id = item->extension_id(); |
- AppNotificationList& list = GetAllInternal(extension_id); |
- list.push_back(linked_item); |
- |
- SyncAddChange(*linked_item); |
- |
- std::sort(list.begin(), list.end(), AppNotificationSortPredicate); |
- |
- if (list.size() > AppNotificationManager::kMaxNotificationPerApp) { |
- AppNotification* removed = list.begin()->get(); |
- SyncRemoveChange(*removed); |
- list.erase(list.begin()); |
- } |
- |
- if (storage_.get()) { |
- BrowserThread::PostTask( |
- BrowserThread::FILE, |
- FROM_HERE, |
- base::Bind(&AppNotificationManager::SaveOnFileThread, |
- this, extension_id, CopyAppNotificationList(list))); |
- } |
- |
- content::NotificationService::current()->Notify( |
- chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, |
- content::Source<Profile>(profile_), |
- content::Details<const std::string>(&extension_id)); |
- |
- return true; |
-} |
- |
-const AppNotificationList* AppNotificationManager::GetAll( |
- const std::string& extension_id) const { |
- if (!loaded()) |
- return NULL; |
- if (ContainsKey(*notifications_, extension_id)) |
- return &((*notifications_)[extension_id]); |
- return NULL; |
-} |
- |
-const AppNotification* AppNotificationManager::GetLast( |
- const std::string& extension_id) { |
- if (!loaded()) |
- return NULL; |
- NotificationMap::iterator found = notifications_->find(extension_id); |
- if (found == notifications_->end()) |
- return NULL; |
- const AppNotificationList& list = found->second; |
- if (list.empty()) |
- return NULL; |
- return list.rbegin()->get(); |
-} |
- |
-void AppNotificationManager::ClearAll(const std::string& extension_id) { |
- if (!loaded()) |
- return; |
- NotificationMap::iterator found = notifications_->find(extension_id); |
- if (found != notifications_->end()) { |
- SyncClearAllChange(found->second); |
- notifications_->erase(found); |
- } |
- |
- if (storage_.get()) { |
- BrowserThread::PostTask( |
- BrowserThread::FILE, |
- FROM_HERE, |
- base::Bind(&AppNotificationManager::DeleteOnFileThread, |
- this, extension_id)); |
- } |
- |
- content::NotificationService::current()->Notify( |
- chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, |
- content::Source<Profile>(profile_), |
- content::Details<const std::string>(&extension_id)); |
-} |
- |
-void AppNotificationManager::Observe( |
- int type, |
- const content::NotificationSource& source, |
- const content::NotificationDetails& details) { |
- CHECK(type == chrome::NOTIFICATION_EXTENSION_UNINSTALLED); |
- ClearAll(content::Details<const Extension>(details).ptr()->id()); |
-} |
- |
-syncer::SyncDataList AppNotificationManager::GetAllSyncData( |
- syncer::ModelType type) const { |
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- DCHECK(loaded()); |
- DCHECK_EQ(syncer::APP_NOTIFICATIONS, type); |
- syncer::SyncDataList data; |
- for (NotificationMap::const_iterator iter = notifications_->begin(); |
- iter != notifications_->end(); ++iter) { |
- |
- // Skip local notifications since they should not be synced. |
- const AppNotificationList list = (*iter).second; |
- for (AppNotificationList::const_iterator list_iter = list.begin(); |
- list_iter != list.end(); ++list_iter) { |
- const AppNotification* notification = (*list_iter).get(); |
- if (notification->is_local()) { |
- continue; |
- } |
- data.push_back(CreateSyncDataFromNotification(*notification)); |
- } |
- } |
- |
- return data; |
-} |
- |
-syncer::SyncError AppNotificationManager::ProcessSyncChanges( |
- const tracked_objects::Location& from_here, |
- const syncer::SyncChangeList& change_list) { |
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- DCHECK(loaded()); |
- if (!models_associated_) { |
- return sync_error_factory_->CreateAndUploadError( |
- FROM_HERE, |
- "Models not yet associated."); |
- } |
- |
- base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true); |
- |
- syncer::SyncError error; |
- for (syncer::SyncChangeList::const_iterator iter = change_list.begin(); |
- iter != change_list.end(); ++iter) { |
- syncer::SyncData sync_data = iter->sync_data(); |
- DCHECK_EQ(syncer::APP_NOTIFICATIONS, sync_data.GetDataType()); |
- syncer::SyncChange::SyncChangeType change_type = iter->change_type(); |
- |
- scoped_ptr<AppNotification> new_notif(CreateNotificationFromSyncData( |
- sync_data)); |
- if (!new_notif.get()) { |
- NOTREACHED() << "Failed to read notification."; |
- continue; |
- } |
- const AppNotification* existing_notif = GetNotification( |
- new_notif->extension_id(), new_notif->guid()); |
- if (existing_notif && existing_notif->is_local()) { |
- NOTREACHED() << "Matched with notification marked as local"; |
- error = sync_error_factory_->CreateAndUploadError( |
- FROM_HERE, |
- "ProcessSyncChanges received a local only notification" + |
- syncer::SyncChange::ChangeTypeToString(change_type)); |
- continue; |
- } |
- |
- switch (change_type) { |
- case syncer::SyncChange::ACTION_ADD: |
- if (!existing_notif) { |
- Add(new_notif.release()); |
- } else { |
- DLOG(ERROR) << "Got ADD change for an existing item.\n" |
- << "Existing item: " << existing_notif->ToString() |
- << "\nItem in ADD change: " << new_notif->ToString(); |
- } |
- break; |
- case syncer::SyncChange::ACTION_DELETE: |
- if (existing_notif) { |
- Remove(new_notif->extension_id(), new_notif->guid()); |
- } else { |
- // This should never happen. But we are seeting this sometimes, and |
- // it stops all of sync. See bug http://crbug.com/108088 |
- // So until we figure out the root cause, log an error and ignore. |
- DLOG(ERROR) << "Got DELETE change for non-existing item.\n" |
- << "Item in DELETE change: " << new_notif->ToString(); |
- } |
- break; |
- case syncer::SyncChange::ACTION_UPDATE: |
- // Although app notifications are immutable from the model perspective, |
- // sync can send UPDATE changes due to encryption / meta-data changes. |
- // So ignore UPDATE changes when the exitsing and new notification |
- // objects are the same. Log an error otherwise. |
- if (!existing_notif) { |
- DLOG(ERROR) << "Got UPDATE change for non-existing item." |
- << "Item in UPDATE change: " << new_notif->ToString(); |
- } else if (!existing_notif->Equals(*new_notif)) { |
- DLOG(ERROR) << "Got invalid UPDATE change:" |
- << "New and existing notifications should be the same.\n" |
- << "Existing item: " << existing_notif->ToString() << "\n" |
- << "Item in UPDATE change: " << new_notif->ToString(); |
- } |
- break; |
- default: |
- break; |
- } |
- } |
- |
- return error; |
-} |
- |
-syncer::SyncMergeResult AppNotificationManager::MergeDataAndStartSyncing( |
- syncer::ModelType type, |
- const syncer::SyncDataList& initial_sync_data, |
- scoped_ptr<syncer::SyncChangeProcessor> sync_processor, |
- scoped_ptr<syncer::SyncErrorFactory> sync_error_factory) { |
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- syncer::SyncMergeResult merge_result(type); |
- // AppNotificationDataTypeController ensures that modei is fully should before |
- // this method is called by waiting until the load notification is received |
- // from AppNotificationManager. |
- DCHECK(loaded()); |
- DCHECK_EQ(type, syncer::APP_NOTIFICATIONS); |
- DCHECK(!sync_processor_.get()); |
- DCHECK(sync_processor.get()); |
- DCHECK(sync_error_factory.get()); |
- sync_processor_ = sync_processor.Pass(); |
- sync_error_factory_ = sync_error_factory.Pass(); |
- |
- // We may add, or remove notifications here, so ensure we don't step on |
- // our own toes. |
- base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true); |
- |
- SyncDataMap local_data_map; |
- PopulateGuidToSyncDataMap(GetAllSyncData(syncer::APP_NOTIFICATIONS), |
- &local_data_map); |
- |
- for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin(); |
- iter != initial_sync_data.end(); ++iter) { |
- const syncer::SyncData& sync_data = *iter; |
- DCHECK_EQ(syncer::APP_NOTIFICATIONS, sync_data.GetDataType()); |
- scoped_ptr<AppNotification> sync_notif(CreateNotificationFromSyncData( |
- sync_data)); |
- CHECK(sync_notif.get()); |
- const AppNotification* local_notif = GetNotification( |
- sync_notif->extension_id(), sync_notif->guid()); |
- if (local_notif) { |
- local_data_map.erase(sync_notif->guid()); |
- // Local notification should always match with sync notification as |
- // notifications are immutable. |
- if (local_notif->is_local() || !sync_notif->Equals(*local_notif)) { |
- merge_result.set_error(sync_error_factory_->CreateAndUploadError( |
- FROM_HERE, |
- "MergeDataAndStartSyncing failed: local notification and sync " |
- "notification have same guid but different data.")); |
- return merge_result; |
- } |
- } else { |
- // Sync model has a notification that local model does not, add it. |
- Add(sync_notif.release()); |
- } |
- } |
- |
- // TODO(munjal): crbug.com/10059. Work with Lingesh/Antony to resolve. |
- syncer::SyncChangeList new_changes; |
- for (SyncDataMap::const_iterator iter = local_data_map.begin(); |
- iter != local_data_map.end(); ++iter) { |
- new_changes.push_back( |
- syncer::SyncChange(FROM_HERE, |
- syncer::SyncChange::ACTION_ADD, |
- iter->second)); |
- } |
- |
- if (new_changes.size() > 0) { |
- merge_result.set_error( |
- sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes)); |
- } |
- models_associated_ = !merge_result.error().IsSet(); |
- return merge_result; |
-} |
- |
-void AppNotificationManager::StopSyncing(syncer::ModelType type) { |
- DCHECK_EQ(type, syncer::APP_NOTIFICATIONS); |
- models_associated_ = false; |
- sync_processor_.reset(); |
- sync_error_factory_.reset(); |
-} |
- |
-AppNotificationManager::~AppNotificationManager() { |
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- // Post a task to delete our storage on the file thread. |
- BrowserThread::DeleteSoon(BrowserThread::FILE, |
- FROM_HERE, |
- storage_.release()); |
-} |
- |
-void AppNotificationManager::LoadOnFileThread( |
- const base::FilePath& storage_path) { |
- PerfTimer timer; |
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
- DCHECK(!loaded()); |
- |
- storage_.reset(AppNotificationStorage::Create(storage_path)); |
- if (!storage_.get()) |
- return; |
- scoped_ptr<NotificationMap> result(new NotificationMap()); |
- std::set<std::string> ids; |
- if (!storage_->GetExtensionIds(&ids)) |
- return; |
- std::set<std::string>::const_iterator i; |
- for (i = ids.begin(); i != ids.end(); ++i) { |
- const std::string& id = *i; |
- AppNotificationList& list = (*result)[id]; |
- if (!storage_->Get(id, &list)) |
- result->erase(id); |
- } |
- |
- BrowserThread::PostTask( |
- BrowserThread::UI, |
- FROM_HERE, |
- base::Bind(&AppNotificationManager::HandleLoadResults, |
- this, result.release())); |
- |
- UMA_HISTOGRAM_LONG_TIMES("AppNotification.MgrFileThreadLoadTime", |
- timer.Elapsed()); |
-} |
- |
-void AppNotificationManager::HandleLoadResults(NotificationMap* map) { |
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
- DCHECK(map); |
- DCHECK(!loaded()); |
- notifications_.reset(map); |
- UMA_HISTOGRAM_LONG_TIMES("AppNotification.MgrLoadDelay", |
- load_timer_->Elapsed()); |
- load_timer_.reset(); |
- |
- // Generate STATE_CHANGED notifications for extensions that have at |
- // least one notification loaded. |
- int app_count = 0; |
- int notification_count = 0; |
- NotificationMap::const_iterator i; |
- for (i = map->begin(); i != map->end(); ++i) { |
- const std::string& id = i->first; |
- if (i->second.empty()) |
- continue; |
- app_count++; |
- notification_count += i->second.size(); |
- content::NotificationService::current()->Notify( |
- chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, |
- content::Source<Profile>(profile_), |
- content::Details<const std::string>(&id)); |
- } |
- UMA_HISTOGRAM_COUNTS("AppNotification.MgrLoadAppCount", app_count); |
- UMA_HISTOGRAM_COUNTS("AppNotification.MgrLoadTotalCount", |
- notification_count); |
- |
- // Generate MANAGER_LOADED notification. |
- content::NotificationService::current()->Notify( |
- chrome::NOTIFICATION_APP_NOTIFICATION_MANAGER_LOADED, |
- content::Source<AppNotificationManager>(this), |
- content::NotificationService::NoDetails()); |
-} |
- |
-void AppNotificationManager::SaveOnFileThread(const std::string& extension_id, |
- AppNotificationList* list) { |
- // Own the |list|. |
- scoped_ptr<AppNotificationList> scoped_list(list); |
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
- storage_->Set(extension_id, *scoped_list); |
-} |
- |
-void AppNotificationManager::DeleteOnFileThread( |
- const std::string& extension_id) { |
- CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
- storage_->Delete(extension_id); |
-} |
- |
-AppNotificationList& AppNotificationManager::GetAllInternal( |
- const std::string& extension_id) { |
- NotificationMap::iterator found = notifications_->find(extension_id); |
- if (found == notifications_->end()) { |
- (*notifications_)[extension_id] = AppNotificationList(); |
- found = notifications_->find(extension_id); |
- } |
- CHECK(found != notifications_->end()); |
- return found->second; |
-} |
- |
-void AppNotificationManager::Remove(const std::string& extension_id, |
- const std::string& guid) { |
- DCHECK(loaded()); |
- AppNotificationList& list = GetAllInternal(extension_id); |
- RemoveByGuid(&list, guid); |
- |
- if (storage_.get()) { |
- BrowserThread::PostTask( |
- BrowserThread::FILE, |
- FROM_HERE, |
- base::Bind(&AppNotificationManager::SaveOnFileThread, |
- this, extension_id, CopyAppNotificationList(list))); |
- } |
- |
- content::NotificationService::current()->Notify( |
- chrome::NOTIFICATION_APP_NOTIFICATION_STATE_CHANGED, |
- content::Source<Profile>(profile_), |
- content::Details<const std::string>(&extension_id)); |
-} |
- |
-const AppNotification* AppNotificationManager::GetNotification( |
- const std::string& extension_id, const std::string& guid) { |
- DCHECK(loaded()); |
- const AppNotificationList& list = GetAllInternal(extension_id); |
- return FindByGuid(list, guid); |
-} |
- |
-void AppNotificationManager::SyncAddChange(const AppNotification& notif) { |
- // Skip if either: |
- // - Notification is marked as local. |
- // - Sync is not enabled by user. |
- // - Change is generated from within the manager. |
- if (notif.is_local() || !models_associated_ || processing_syncer_changes_) |
- return; |
- |
- // TODO(munjal): crbug.com/10059. Work with Lingesh/Antony to resolve. |
- |
- syncer::SyncChangeList changes; |
- syncer::SyncData sync_data = CreateSyncDataFromNotification(notif); |
- changes.push_back( |
- syncer::SyncChange(FROM_HERE, |
- syncer::SyncChange::ACTION_ADD, |
- sync_data)); |
- sync_processor_->ProcessSyncChanges(FROM_HERE, changes); |
-} |
- |
-void AppNotificationManager::SyncRemoveChange(const AppNotification& notif) { |
- // Skip if either: |
- // - Sync is not enabled by user. |
- // - Change is generated from within the manager. |
- if (notif.is_local() || !models_associated_) { |
- return; |
- } |
- |
- syncer::SyncChangeList changes; |
- syncer::SyncData sync_data = CreateSyncDataFromNotification(notif); |
- changes.push_back( |
- syncer::SyncChange(FROM_HERE, |
- syncer::SyncChange::ACTION_DELETE, |
- sync_data)); |
- sync_processor_->ProcessSyncChanges(FROM_HERE, changes); |
-} |
- |
-void AppNotificationManager::SyncClearAllChange( |
- const AppNotificationList& list) { |
- // Skip if either: |
- // - Sync is not enabled by user. |
- // - Change is generated from within the manager. |
- if (!models_associated_ || processing_syncer_changes_) |
- return; |
- |
- syncer::SyncChangeList changes; |
- for (AppNotificationList::const_iterator iter = list.begin(); |
- iter != list.end(); ++iter) { |
- const AppNotification& notif = *iter->get(); |
- // Skip notifications marked as local. |
- if (notif.is_local()) |
- continue; |
- changes.push_back(syncer::SyncChange( |
- FROM_HERE, |
- syncer::SyncChange::ACTION_DELETE, |
- CreateSyncDataFromNotification(notif))); |
- } |
- sync_processor_->ProcessSyncChanges(FROM_HERE, changes); |
-} |
- |
-// static |
-syncer::SyncData AppNotificationManager::CreateSyncDataFromNotification( |
- const AppNotification& notification) { |
- DCHECK(!notification.is_local()); |
- sync_pb::EntitySpecifics specifics; |
- sync_pb::AppNotification* notif_specifics = |
- specifics.mutable_app_notification(); |
- notif_specifics->set_app_id(notification.extension_id()); |
- notif_specifics->set_creation_timestamp_ms( |
- notification.creation_time().ToInternalValue()); |
- notif_specifics->set_body_text(notification.body()); |
- notif_specifics->set_guid(notification.guid()); |
- notif_specifics->set_link_text(notification.link_text()); |
- notif_specifics->set_link_url(notification.link_url().spec()); |
- notif_specifics->set_title(notification.title()); |
- return syncer::SyncData::CreateLocalData( |
- notif_specifics->guid(), notif_specifics->app_id(), specifics); |
-} |
- |
-// static |
-AppNotification* AppNotificationManager::CreateNotificationFromSyncData( |
- const syncer::SyncData& sync_data) { |
- sync_pb::AppNotification specifics = |
- sync_data.GetSpecifics().app_notification(); |
- |
- // Check for mandatory fields. |
- if (!specifics.has_app_id() || !specifics.has_guid() || |
- !specifics.has_title() || !specifics.has_body_text() || |
- !specifics.has_creation_timestamp_ms()) { |
- return NULL; |
- } |
- |
- AppNotification* notification = new AppNotification( |
- false, base::Time::FromInternalValue(specifics.creation_timestamp_ms()), |
- specifics.guid(), specifics.app_id(), |
- specifics.title(), specifics.body_text()); |
- if (specifics.has_link_text()) |
- notification->set_link_text(specifics.link_text()); |
- if (specifics.has_link_url()) |
- notification->set_link_url(GURL(specifics.link_url())); |
- return notification; |
-} |
- |
-} // namespace extensions |