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