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 "ui/message_center/notification_list.h" 6 7 #include "base/bind.h" 8 #include "base/logging.h" 9 #include "base/stl_util.h" 10 #include "base/time/time.h" 11 #include "base/values.h" 12 #include "ui/gfx/image/image.h" 13 #include "ui/message_center/message_center_style.h" 14 #include "ui/message_center/notification.h" 15 #include "ui/message_center/notification_blocker.h" 16 #include "ui/message_center/notification_types.h" 17 18 namespace message_center { 19 20 namespace { 21 22 bool ShouldShowNotificationAsPopup( 23 const Notification& notification, 24 const NotificationBlockers& blockers) { 25 for (size_t i = 0; i < blockers.size(); ++i) { 26 if (!blockers[i]->ShouldShowNotificationAsPopup(notification.notifier_id())) 27 return false; 28 } 29 return true; 30 } 31 32 } // namespace 33 34 bool ComparePriorityTimestampSerial::operator()(Notification* n1, 35 Notification* n2) { 36 if (n1->priority() > n2->priority()) // Higher pri go first. 37 return true; 38 if (n1->priority() < n2->priority()) 39 return false; 40 return CompareTimestampSerial()(n1, n2); 41 } 42 43 bool CompareTimestampSerial::operator()(Notification* n1, Notification* n2) { 44 if (n1->timestamp() > n2->timestamp()) // Newer come first. 45 return true; 46 if (n1->timestamp() < n2->timestamp()) 47 return false; 48 if (n1->serial_number() > n2->serial_number()) // Newer come first. 49 return true; 50 if (n1->serial_number() < n2->serial_number()) 51 return false; 52 return false; 53 } 54 55 NotificationList::NotificationList() 56 : message_center_visible_(false), 57 quiet_mode_(false) { 58 } 59 60 NotificationList::~NotificationList() { 61 STLDeleteContainerPointers(notifications_.begin(), notifications_.end()); 62 } 63 64 void NotificationList::SetMessageCenterVisible( 65 bool visible, 66 std::set<std::string>* updated_ids) { 67 if (message_center_visible_ == visible) 68 return; 69 70 message_center_visible_ = visible; 71 72 if (!visible) 73 return; 74 75 for (Notifications::iterator iter = notifications_.begin(); 76 iter != notifications_.end(); ++iter) { 77 Notification* notification = *iter; 78 bool was_popup = notification->shown_as_popup(); 79 bool was_read = notification->IsRead(); 80 if (notification->priority() < SYSTEM_PRIORITY) 81 notification->set_shown_as_popup(true); 82 notification->set_is_read(true); 83 if (updated_ids && !(was_popup && was_read)) 84 updated_ids->insert(notification->id()); 85 } 86 } 87 88 void NotificationList::AddNotification(scoped_ptr<Notification> notification) { 89 PushNotification(notification.Pass()); 90 } 91 92 void NotificationList::UpdateNotificationMessage( 93 const std::string& old_id, 94 scoped_ptr<Notification> new_notification) { 95 Notifications::iterator iter = GetNotification(old_id); 96 if (iter == notifications_.end()) 97 return; 98 99 new_notification->CopyState(*iter); 100 101 // Handles priority promotion. If the notification is already dismissed but 102 // the updated notification has higher priority, it should re-appear as a 103 // toast. Notifications coming from websites through the Web Notification API 104 // will always re-appear on update. 105 if ((*iter)->priority() < new_notification->priority() || 106 new_notification->notifier_id().type == NotifierId::WEB_PAGE) { 107 new_notification->set_is_read(false); 108 new_notification->set_shown_as_popup(false); 109 } 110 111 // Do not use EraseNotification and PushNotification, since we don't want to 112 // change unread counts nor to update is_read/shown_as_popup states. 113 Notification* old = *iter; 114 notifications_.erase(iter); 115 delete old; 116 117 // We really don't want duplicate IDs. 118 DCHECK(GetNotification(new_notification->id()) == notifications_.end()); 119 notifications_.insert(new_notification.release()); 120 } 121 122 void NotificationList::RemoveNotification(const std::string& id) { 123 EraseNotification(GetNotification(id)); 124 } 125 126 NotificationList::Notifications NotificationList::GetNotificationsByNotifierId( 127 const NotifierId& notifier_id) { 128 Notifications notifications; 129 for (Notifications::iterator iter = notifications_.begin(); 130 iter != notifications_.end(); ++iter) { 131 if ((*iter)->notifier_id() == notifier_id) 132 notifications.insert(*iter); 133 } 134 return notifications; 135 } 136 137 bool NotificationList::SetNotificationIcon(const std::string& notification_id, 138 const gfx::Image& image) { 139 Notifications::iterator iter = GetNotification(notification_id); 140 if (iter == notifications_.end()) 141 return false; 142 (*iter)->set_icon(image); 143 return true; 144 } 145 146 bool NotificationList::SetNotificationImage(const std::string& notification_id, 147 const gfx::Image& image) { 148 Notifications::iterator iter = GetNotification(notification_id); 149 if (iter == notifications_.end()) 150 return false; 151 (*iter)->set_image(image); 152 return true; 153 } 154 155 bool NotificationList::SetNotificationButtonIcon( 156 const std::string& notification_id, int button_index, 157 const gfx::Image& image) { 158 Notifications::iterator iter = GetNotification(notification_id); 159 if (iter == notifications_.end()) 160 return false; 161 (*iter)->SetButtonIcon(button_index, image); 162 return true; 163 } 164 165 bool NotificationList::HasNotificationOfType(const std::string& id, 166 const NotificationType type) { 167 Notifications::iterator iter = GetNotification(id); 168 if (iter == notifications_.end()) 169 return false; 170 171 return (*iter)->type() == type; 172 } 173 174 bool NotificationList::HasPopupNotifications( 175 const NotificationBlockers& blockers) { 176 for (Notifications::iterator iter = notifications_.begin(); 177 iter != notifications_.end(); ++iter) { 178 if ((*iter)->priority() < DEFAULT_PRIORITY) 179 break; 180 if (!ShouldShowNotificationAsPopup(**iter, blockers)) 181 continue; 182 if (!(*iter)->shown_as_popup()) 183 return true; 184 } 185 return false; 186 } 187 188 NotificationList::PopupNotifications NotificationList::GetPopupNotifications( 189 const NotificationBlockers& blockers, 190 std::list<std::string>* blocked_ids) { 191 PopupNotifications result; 192 size_t default_priority_popup_count = 0; 193 194 // Collect notifications that should be shown as popups. Start from oldest. 195 for (Notifications::const_reverse_iterator iter = notifications_.rbegin(); 196 iter != notifications_.rend(); iter++) { 197 if ((*iter)->shown_as_popup()) 198 continue; 199 200 // No popups for LOW/MIN priority. 201 if ((*iter)->priority() < DEFAULT_PRIORITY) 202 continue; 203 204 if (!ShouldShowNotificationAsPopup(**iter, blockers)) { 205 if (blocked_ids) 206 blocked_ids->push_back((*iter)->id()); 207 continue; 208 } 209 210 // Checking limits. No limits for HIGH/MAX priority. DEFAULT priority 211 // will return at most kMaxVisiblePopupNotifications entries. If the 212 // popup entries are more, older entries are used. see crbug.com/165768 213 if ((*iter)->priority() == DEFAULT_PRIORITY && 214 default_priority_popup_count++ >= kMaxVisiblePopupNotifications) { 215 continue; 216 } 217 218 result.insert(*iter); 219 } 220 return result; 221 } 222 223 void NotificationList::MarkSinglePopupAsShown( 224 const std::string& id, bool mark_notification_as_read) { 225 Notifications::iterator iter = GetNotification(id); 226 DCHECK(iter != notifications_.end()); 227 228 if ((*iter)->shown_as_popup()) 229 return; 230 231 // System notification is marked as shown only when marked as read. 232 if ((*iter)->priority() != SYSTEM_PRIORITY || mark_notification_as_read) 233 (*iter)->set_shown_as_popup(true); 234 235 // The popup notification is already marked as read when it's displayed. 236 // Set the is_read() back to false if necessary. 237 if (!mark_notification_as_read) 238 (*iter)->set_is_read(false); 239 } 240 241 void NotificationList::MarkSinglePopupAsDisplayed(const std::string& id) { 242 Notifications::iterator iter = GetNotification(id); 243 if (iter == notifications_.end()) 244 return; 245 246 if ((*iter)->shown_as_popup()) 247 return; 248 249 if (!(*iter)->IsRead()) 250 (*iter)->set_is_read(true); 251 } 252 253 NotificationDelegate* NotificationList::GetNotificationDelegate( 254 const std::string& id) { 255 Notifications::iterator iter = GetNotification(id); 256 if (iter == notifications_.end()) 257 return NULL; 258 return (*iter)->delegate(); 259 } 260 261 void NotificationList::SetQuietMode(bool quiet_mode) { 262 quiet_mode_ = quiet_mode; 263 if (quiet_mode_) { 264 for (Notifications::iterator iter = notifications_.begin(); 265 iter != notifications_.end(); 266 ++iter) { 267 (*iter)->set_shown_as_popup(true); 268 } 269 } 270 } 271 272 Notification* NotificationList::GetNotificationById(const std::string& id) { 273 Notifications::iterator iter = GetNotification(id); 274 if (iter != notifications_.end()) 275 return *iter; 276 return NULL; 277 } 278 279 NotificationList::Notifications NotificationList::GetVisibleNotifications( 280 const NotificationBlockers& blockers) const { 281 Notifications result; 282 for (Notifications::const_iterator iter = notifications_.begin(); 283 iter != notifications_.end(); ++iter) { 284 bool should_show = true; 285 for (size_t i = 0; i < blockers.size(); ++i) { 286 if (!blockers[i]->ShouldShowNotification((*iter)->notifier_id())) { 287 should_show = false; 288 break; 289 } 290 } 291 if (should_show) 292 result.insert(*iter); 293 } 294 295 return result; 296 } 297 298 size_t NotificationList::NotificationCount( 299 const NotificationBlockers& blockers) const { 300 return GetVisibleNotifications(blockers).size(); 301 } 302 303 size_t NotificationList::UnreadCount( 304 const NotificationBlockers& blockers) const { 305 Notifications notifications = GetVisibleNotifications(blockers); 306 size_t unread_count = 0; 307 for (Notifications::const_iterator iter = notifications.begin(); 308 iter != notifications.end(); ++iter) { 309 if (!(*iter)->IsRead()) 310 ++unread_count; 311 } 312 return unread_count; 313 } 314 315 NotificationList::Notifications::iterator NotificationList::GetNotification( 316 const std::string& id) { 317 for (Notifications::iterator iter = notifications_.begin(); 318 iter != notifications_.end(); ++iter) { 319 if ((*iter)->id() == id) 320 return iter; 321 } 322 return notifications_.end(); 323 } 324 325 void NotificationList::EraseNotification(Notifications::iterator iter) { 326 delete *iter; 327 notifications_.erase(iter); 328 } 329 330 void NotificationList::PushNotification(scoped_ptr<Notification> notification) { 331 // Ensure that notification.id is unique by erasing any existing 332 // notification with the same id (shouldn't normally happen). 333 Notifications::iterator iter = GetNotification(notification->id()); 334 bool state_inherited = false; 335 if (iter != notifications_.end()) { 336 notification->CopyState(*iter); 337 state_inherited = true; 338 EraseNotification(iter); 339 } 340 // Add the notification to the the list and mark it unread and unshown. 341 if (!state_inherited) { 342 // TODO(mukai): needs to distinguish if a notification is dismissed by 343 // the quiet mode or user operation. 344 notification->set_is_read(false); 345 notification->set_shown_as_popup(message_center_visible_ 346 || quiet_mode_ 347 || notification->shown_as_popup()); 348 } 349 // Take ownership. The notification can only be removed from the list 350 // in EraseNotification(), which will delete it. 351 notifications_.insert(notification.release()); 352 } 353 354 } // namespace message_center 355