Home | History | Annotate | Download | only in notifications
      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/notifications/balloon_notification_ui_manager.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/prefs/pref_service.h"
     10 #include "base/stl_util.h"
     11 #include "chrome/browser/browser_process.h"
     12 #include "chrome/browser/chrome_notification_types.h"
     13 #include "chrome/browser/fullscreen.h"
     14 #include "chrome/browser/idle.h"
     15 #include "chrome/browser/notifications/balloon_collection.h"
     16 #include "chrome/browser/notifications/notification.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/common/pref_names.h"
     19 #include "content/public/browser/notification_service.h"
     20 
     21 // A class which represents a notification waiting to be shown.
     22 class QueuedNotification {
     23  public:
     24   QueuedNotification(const Notification& notification, Profile* profile)
     25       : notification_(notification),
     26         profile_(profile) {
     27   }
     28 
     29   const Notification& notification() const { return notification_; }
     30   Profile* profile() const { return profile_; }
     31 
     32   void Replace(const Notification& new_notification) {
     33     notification_ = new_notification;
     34   }
     35 
     36  private:
     37   // The notification to be shown.
     38   Notification notification_;
     39 
     40   // Non owned pointer to the user's profile.
     41   Profile* profile_;
     42 
     43   DISALLOW_COPY_AND_ASSIGN(QueuedNotification);
     44 };
     45 
     46 BalloonNotificationUIManager::BalloonNotificationUIManager(
     47     PrefService* local_state)
     48     : NotificationPrefsManager(local_state),
     49       // Passes NULL to blockers since |message_center| is not used from balloon
     50       // notifications.
     51       screen_lock_blocker_(NULL),
     52       fullscreen_blocker_(NULL),
     53       system_observer_(this) {
     54   position_pref_.Init(
     55       prefs::kDesktopNotificationPosition,
     56       local_state,
     57       base::Bind(
     58           &BalloonNotificationUIManager::OnDesktopNotificationPositionChanged,
     59           base::Unretained(this)));
     60 }
     61 
     62 BalloonNotificationUIManager::~BalloonNotificationUIManager() {
     63 }
     64 
     65 void BalloonNotificationUIManager::SetBalloonCollection(
     66     BalloonCollection* balloon_collection) {
     67   DCHECK(!balloon_collection_.get() ||
     68          balloon_collection_->GetActiveBalloons().size() == 0);
     69   DCHECK(balloon_collection);
     70   balloon_collection_.reset(balloon_collection);
     71   balloon_collection_->SetPositionPreference(
     72       static_cast<BalloonCollection::PositionPreference>(
     73           position_pref_.GetValue()));
     74   balloon_collection_->set_space_change_listener(this);
     75 }
     76 
     77 void BalloonNotificationUIManager::Add(const Notification& notification,
     78                                        Profile* profile) {
     79   if (Update(notification, profile)) {
     80     return;
     81   }
     82 
     83   VLOG(1) << "Added notification. URL: "
     84           << notification.content_url().spec();
     85   show_queue_.push_back(linked_ptr<QueuedNotification>(
     86       new QueuedNotification(notification, profile)));
     87   CheckAndShowNotifications();
     88 }
     89 
     90 bool BalloonNotificationUIManager::Update(const Notification& notification,
     91                                           Profile* profile) {
     92   const GURL& origin = notification.origin_url();
     93   const base::string16& replace_id = notification.replace_id();
     94 
     95   if (replace_id.empty())
     96     return false;
     97 
     98   // First check the queue of pending notifications for replacement.
     99   // Then check the list of notifications already being shown.
    100   for (NotificationDeque::const_iterator iter = show_queue_.begin();
    101        iter != show_queue_.end(); ++iter) {
    102     if (profile == (*iter)->profile() &&
    103         origin == (*iter)->notification().origin_url() &&
    104         replace_id == (*iter)->notification().replace_id()) {
    105       (*iter)->Replace(notification);
    106       return true;
    107     }
    108   }
    109 
    110   return UpdateNotification(notification, profile);
    111 }
    112 
    113 const Notification* BalloonNotificationUIManager::FindById(
    114     const std::string& id) const {
    115   for (NotificationDeque::const_iterator iter = show_queue_.begin();
    116        iter != show_queue_.end(); ++iter) {
    117     if ((*iter)->notification().notification_id() == id) {
    118       return &((*iter)->notification());
    119     }
    120   }
    121   return balloon_collection_->FindById(id);
    122 }
    123 
    124 bool BalloonNotificationUIManager::CancelById(const std::string& id) {
    125   // See if this ID hasn't been shown yet.
    126   for (NotificationDeque::iterator iter = show_queue_.begin();
    127        iter != show_queue_.end(); ++iter) {
    128     if ((*iter)->notification().notification_id() == id) {
    129       show_queue_.erase(iter);
    130       return true;
    131     }
    132   }
    133   // If it has been shown, remove it from the balloon collections.
    134   return balloon_collection_->RemoveById(id);
    135 }
    136 
    137 std::set<std::string>
    138 BalloonNotificationUIManager::GetAllIdsByProfileAndSourceOrigin(
    139     Profile* profile,
    140     const GURL& source) {
    141   std::set<std::string> notification_ids;
    142   for (NotificationDeque::iterator iter = show_queue_.begin();
    143        iter != show_queue_.end(); iter++) {
    144     if ((*iter)->notification().origin_url() == source &&
    145         profile->IsSameProfile((*iter)->profile())) {
    146       notification_ids.insert((*iter)->notification().notification_id());
    147     }
    148   }
    149 
    150   const BalloonCollection::Balloons& balloons =
    151       balloon_collection_->GetActiveBalloons();
    152   for (BalloonCollection::Balloons::const_iterator iter = balloons.begin();
    153        iter != balloons.end(); ++iter) {
    154     if (profile->IsSameProfile((*iter)->profile()) &&
    155         source == (*iter)->notification().origin_url()) {
    156       notification_ids.insert((*iter)->notification().notification_id());
    157     }
    158   }
    159   return notification_ids;
    160 }
    161 
    162 bool BalloonNotificationUIManager::CancelAllBySourceOrigin(const GURL& source) {
    163   // Same pattern as CancelById, but more complicated than the above
    164   // because there may be multiple notifications from the same source.
    165   bool removed = false;
    166   for (NotificationDeque::iterator loopiter = show_queue_.begin();
    167        loopiter != show_queue_.end(); ) {
    168     if ((*loopiter)->notification().origin_url() != source) {
    169       ++loopiter;
    170       continue;
    171     }
    172 
    173     loopiter = show_queue_.erase(loopiter);
    174     removed = true;
    175   }
    176   return balloon_collection_->RemoveBySourceOrigin(source) || removed;
    177 }
    178 
    179 bool BalloonNotificationUIManager::CancelAllByProfile(Profile* profile) {
    180   // Same pattern as CancelAllBySourceOrigin.
    181   bool removed = false;
    182   for (NotificationDeque::iterator loopiter = show_queue_.begin();
    183        loopiter != show_queue_.end(); ) {
    184     if ((*loopiter)->profile() != profile) {
    185       ++loopiter;
    186       continue;
    187     }
    188 
    189     loopiter = show_queue_.erase(loopiter);
    190     removed = true;
    191   }
    192   return balloon_collection_->RemoveByProfile(profile) || removed;
    193 }
    194 
    195 void BalloonNotificationUIManager::CancelAll() {
    196   balloon_collection_->RemoveAll();
    197 }
    198 
    199 BalloonCollection* BalloonNotificationUIManager::balloon_collection() {
    200   return balloon_collection_.get();
    201 }
    202 
    203 NotificationPrefsManager* BalloonNotificationUIManager::prefs_manager() {
    204   return this;
    205 }
    206 
    207 bool BalloonNotificationUIManager::ShowNotification(
    208     const Notification& notification,
    209     Profile* profile) {
    210   if (!balloon_collection_->HasSpace())
    211     return false;
    212   balloon_collection_->Add(notification, profile);
    213   return true;
    214 }
    215 
    216 void BalloonNotificationUIManager::OnBalloonSpaceChanged() {
    217   CheckAndShowNotifications();
    218 }
    219 
    220 void BalloonNotificationUIManager::OnBlockingStateChanged(
    221     message_center::NotificationBlocker* blocker) {
    222   CheckAndShowNotifications();
    223 }
    224 
    225 bool BalloonNotificationUIManager::UpdateNotification(
    226     const Notification& notification,
    227     Profile* profile) {
    228   const GURL& origin = notification.origin_url();
    229   const base::string16& replace_id = notification.replace_id();
    230 
    231   DCHECK(!replace_id.empty());
    232 
    233   const BalloonCollection::Balloons& balloons =
    234       balloon_collection_->GetActiveBalloons();
    235   for (BalloonCollection::Balloons::const_iterator iter = balloons.begin();
    236        iter != balloons.end(); ++iter) {
    237     if (profile == (*iter)->profile() &&
    238         origin == (*iter)->notification().origin_url() &&
    239         replace_id == (*iter)->notification().replace_id()) {
    240       (*iter)->Update(notification);
    241       return true;
    242     }
    243   }
    244 
    245   return false;
    246 }
    247 
    248 BalloonCollection::PositionPreference
    249 BalloonNotificationUIManager::GetPositionPreference() const {
    250   return static_cast<BalloonCollection::PositionPreference>(
    251       position_pref_.GetValue());
    252 }
    253 
    254 void BalloonNotificationUIManager::SetPositionPreference(
    255     BalloonCollection::PositionPreference preference) {
    256   position_pref_.SetValue(static_cast<int>(preference));
    257   balloon_collection_->SetPositionPreference(preference);
    258 }
    259 
    260 void BalloonNotificationUIManager::CheckAndShowNotifications() {
    261   screen_lock_blocker_.CheckState();
    262   fullscreen_blocker_.CheckState();
    263   if (screen_lock_blocker_.is_locked() ||
    264       fullscreen_blocker_.is_fullscreen_mode()) {
    265     return;
    266   }
    267   ShowNotifications();
    268 }
    269 
    270 void BalloonNotificationUIManager::OnDesktopNotificationPositionChanged() {
    271   balloon_collection_->SetPositionPreference(
    272       static_cast<BalloonCollection::PositionPreference>(
    273           position_pref_.GetValue()));
    274 }
    275 
    276 void BalloonNotificationUIManager::ShowNotifications() {
    277   while (!show_queue_.empty()) {
    278     linked_ptr<QueuedNotification> queued_notification(show_queue_.front());
    279     show_queue_.pop_front();
    280     if (!ShowNotification(queued_notification->notification(),
    281                           queued_notification->profile())) {
    282       show_queue_.push_front(queued_notification);
    283       return;
    284     }
    285   }
    286 }
    287 
    288 // static
    289 BalloonNotificationUIManager*
    290     BalloonNotificationUIManager::GetInstanceForTesting() {
    291   if (NotificationUIManager::DelegatesToMessageCenter()) {
    292     LOG(ERROR) << "Attempt to run a test that requires "
    293                << "BalloonNotificationUIManager while delegating to a "
    294                << "native MessageCenter. Test will fail. Ask dimich@";
    295     return NULL;
    296   }
    297   return static_cast<BalloonNotificationUIManager*>(
    298       g_browser_process->notification_ui_manager());
    299 }
    300 
    301 void BalloonNotificationUIManager::GetQueuedNotificationsForTesting(
    302     std::vector<const Notification*>* notifications) {
    303   for (NotificationDeque::const_iterator iter = show_queue_.begin();
    304        iter != show_queue_.end(); ++iter) {
    305     notifications->push_back(&(*iter)->notification());
    306   }
    307 }
    308