Fix a crash in the LegacyRenderWidgetHostHWND destruction code path which occurs...
[chromium-blink-merge.git] / ui / message_center / notification_list.cc
blobeac4345657ed1727eed327b2df899a0dddc33a5f
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.
5 #include "ui/message_center/notification_list.h"
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"
18 namespace message_center {
20 namespace {
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;
29 return true;
32 } // namespace
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);
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;
55 NotificationList::NotificationList()
56 : message_center_visible_(false),
57 quiet_mode_(false) {
60 NotificationList::~NotificationList() {
61 STLDeleteContainerPointers(notifications_.begin(), notifications_.end());
64 void NotificationList::SetMessageCenterVisible(
65 bool visible,
66 std::set<std::string>* updated_ids) {
67 if (message_center_visible_ == visible)
68 return;
70 message_center_visible_ = visible;
72 if (!visible)
73 return;
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());
88 void NotificationList::AddNotification(scoped_ptr<Notification> notification) {
89 PushNotification(notification.Pass());
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;
99 new_notification->CopyState(*iter);
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.
104 if ((*iter)->priority() < new_notification->priority()) {
105 new_notification->set_is_read(false);
106 new_notification->set_shown_as_popup(false);
109 // Do not use EraseNotification and PushNotification, since we don't want to
110 // change unread counts nor to update is_read/shown_as_popup states.
111 Notification* old = *iter;
112 notifications_.erase(iter);
113 delete old;
115 // We really don't want duplicate IDs.
116 DCHECK(GetNotification(new_notification->id()) == notifications_.end());
117 notifications_.insert(new_notification.release());
120 void NotificationList::RemoveNotification(const std::string& id) {
121 EraseNotification(GetNotification(id));
124 NotificationList::Notifications NotificationList::GetNotificationsByNotifierId(
125 const NotifierId& notifier_id) {
126 Notifications notifications;
127 for (Notifications::iterator iter = notifications_.begin();
128 iter != notifications_.end(); ++iter) {
129 if ((*iter)->notifier_id() == notifier_id)
130 notifications.insert(*iter);
132 return notifications;
135 bool NotificationList::SetNotificationIcon(const std::string& notification_id,
136 const gfx::Image& image) {
137 Notifications::iterator iter = GetNotification(notification_id);
138 if (iter == notifications_.end())
139 return false;
140 (*iter)->set_icon(image);
141 return true;
144 bool NotificationList::SetNotificationImage(const std::string& notification_id,
145 const gfx::Image& image) {
146 Notifications::iterator iter = GetNotification(notification_id);
147 if (iter == notifications_.end())
148 return false;
149 (*iter)->set_image(image);
150 return true;
153 bool NotificationList::SetNotificationButtonIcon(
154 const std::string& notification_id, int button_index,
155 const gfx::Image& image) {
156 Notifications::iterator iter = GetNotification(notification_id);
157 if (iter == notifications_.end())
158 return false;
159 (*iter)->SetButtonIcon(button_index, image);
160 return true;
163 bool NotificationList::HasNotificationOfType(const std::string& id,
164 const NotificationType type) {
165 Notifications::iterator iter = GetNotification(id);
166 if (iter == notifications_.end())
167 return false;
169 return (*iter)->type() == type;
172 bool NotificationList::HasPopupNotifications(
173 const NotificationBlockers& blockers) {
174 for (Notifications::iterator iter = notifications_.begin();
175 iter != notifications_.end(); ++iter) {
176 if ((*iter)->priority() < DEFAULT_PRIORITY)
177 break;
178 if (!ShouldShowNotificationAsPopup(**iter, blockers))
179 continue;
180 if (!(*iter)->shown_as_popup())
181 return true;
183 return false;
186 NotificationList::PopupNotifications NotificationList::GetPopupNotifications(
187 const NotificationBlockers& blockers,
188 std::list<std::string>* blocked_ids) {
189 PopupNotifications result;
190 size_t default_priority_popup_count = 0;
192 // Collect notifications that should be shown as popups. Start from oldest.
193 for (Notifications::const_reverse_iterator iter = notifications_.rbegin();
194 iter != notifications_.rend(); iter++) {
195 if ((*iter)->shown_as_popup())
196 continue;
198 // No popups for LOW/MIN priority.
199 if ((*iter)->priority() < DEFAULT_PRIORITY)
200 continue;
202 if (!ShouldShowNotificationAsPopup(**iter, blockers)) {
203 if (blocked_ids)
204 blocked_ids->push_back((*iter)->id());
205 continue;
208 // Checking limits. No limits for HIGH/MAX priority. DEFAULT priority
209 // will return at most kMaxVisiblePopupNotifications entries. If the
210 // popup entries are more, older entries are used. see crbug.com/165768
211 if ((*iter)->priority() == DEFAULT_PRIORITY &&
212 default_priority_popup_count++ >= kMaxVisiblePopupNotifications) {
213 continue;
216 result.insert(*iter);
218 return result;
221 void NotificationList::MarkSinglePopupAsShown(
222 const std::string& id, bool mark_notification_as_read) {
223 Notifications::iterator iter = GetNotification(id);
224 DCHECK(iter != notifications_.end());
226 if ((*iter)->shown_as_popup())
227 return;
229 // System notification is marked as shown only when marked as read.
230 if ((*iter)->priority() != SYSTEM_PRIORITY || mark_notification_as_read)
231 (*iter)->set_shown_as_popup(true);
233 // The popup notification is already marked as read when it's displayed.
234 // Set the is_read() back to false if necessary.
235 if (!mark_notification_as_read)
236 (*iter)->set_is_read(false);
239 void NotificationList::MarkSinglePopupAsDisplayed(const std::string& id) {
240 Notifications::iterator iter = GetNotification(id);
241 if (iter == notifications_.end())
242 return;
244 if ((*iter)->shown_as_popup())
245 return;
247 if (!(*iter)->IsRead())
248 (*iter)->set_is_read(true);
251 NotificationDelegate* NotificationList::GetNotificationDelegate(
252 const std::string& id) {
253 Notifications::iterator iter = GetNotification(id);
254 if (iter == notifications_.end())
255 return NULL;
256 return (*iter)->delegate();
259 void NotificationList::SetQuietMode(bool quiet_mode) {
260 quiet_mode_ = quiet_mode;
261 if (quiet_mode_) {
262 for (Notifications::iterator iter = notifications_.begin();
263 iter != notifications_.end();
264 ++iter) {
265 (*iter)->set_shown_as_popup(true);
270 Notification* NotificationList::GetNotificationById(const std::string& id) {
271 Notifications::iterator iter = GetNotification(id);
272 if (iter != notifications_.end())
273 return *iter;
274 return NULL;
277 NotificationList::Notifications NotificationList::GetVisibleNotifications(
278 const NotificationBlockers& blockers) const {
279 Notifications result;
280 for (Notifications::const_iterator iter = notifications_.begin();
281 iter != notifications_.end(); ++iter) {
282 bool should_show = true;
283 for (size_t i = 0; i < blockers.size(); ++i) {
284 if (!blockers[i]->ShouldShowNotification((*iter)->notifier_id())) {
285 should_show = false;
286 break;
289 if (should_show)
290 result.insert(*iter);
293 return result;
296 size_t NotificationList::NotificationCount(
297 const NotificationBlockers& blockers) const {
298 return GetVisibleNotifications(blockers).size();
301 size_t NotificationList::UnreadCount(
302 const NotificationBlockers& blockers) const {
303 Notifications notifications = GetVisibleNotifications(blockers);
304 size_t unread_count = 0;
305 for (Notifications::const_iterator iter = notifications.begin();
306 iter != notifications.end(); ++iter) {
307 if (!(*iter)->IsRead())
308 ++unread_count;
310 return unread_count;
313 NotificationList::Notifications::iterator NotificationList::GetNotification(
314 const std::string& id) {
315 for (Notifications::iterator iter = notifications_.begin();
316 iter != notifications_.end(); ++iter) {
317 if ((*iter)->id() == id)
318 return iter;
320 return notifications_.end();
323 void NotificationList::EraseNotification(Notifications::iterator iter) {
324 delete *iter;
325 notifications_.erase(iter);
328 void NotificationList::PushNotification(scoped_ptr<Notification> notification) {
329 // Ensure that notification.id is unique by erasing any existing
330 // notification with the same id (shouldn't normally happen).
331 Notifications::iterator iter = GetNotification(notification->id());
332 bool state_inherited = false;
333 if (iter != notifications_.end()) {
334 notification->CopyState(*iter);
335 state_inherited = true;
336 EraseNotification(iter);
338 // Add the notification to the the list and mark it unread and unshown.
339 if (!state_inherited) {
340 // TODO(mukai): needs to distinguish if a notification is dismissed by
341 // the quiet mode or user operation.
342 notification->set_is_read(false);
343 notification->set_shown_as_popup(message_center_visible_
344 || quiet_mode_
345 || notification->shown_as_popup());
347 // Take ownership. The notification can only be removed from the list
348 // in EraseNotification(), which will delete it.
349 notifications_.insert(notification.release());
352 } // namespace message_center