• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ShouldShowNotificationAsPopup(const Notification & notification,const NotificationBlockers & blockers)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 
operator ()(Notification * n1,Notification * n2)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 
operator ()(Notification * n1,Notification * 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;
53 }
54 
NotificationList()55 NotificationList::NotificationList()
56     : message_center_visible_(false),
57       quiet_mode_(false) {
58 }
59 
~NotificationList()60 NotificationList::~NotificationList() {
61   STLDeleteContainerPointers(notifications_.begin(), notifications_.end());
62 }
63 
SetMessageCenterVisible(bool visible,std::set<std::string> * updated_ids)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 
AddNotification(scoped_ptr<Notification> notification)88 void NotificationList::AddNotification(scoped_ptr<Notification> notification) {
89   PushNotification(notification.Pass());
90 }
91 
UpdateNotificationMessage(const std::string & old_id,scoped_ptr<Notification> new_notification)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 
RemoveNotification(const std::string & id)122 void NotificationList::RemoveNotification(const std::string& id) {
123   EraseNotification(GetNotification(id));
124 }
125 
GetNotificationsByNotifierId(const NotifierId & notifier_id)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 
SetNotificationIcon(const std::string & notification_id,const gfx::Image & image)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 
SetNotificationImage(const std::string & notification_id,const gfx::Image & image)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 
SetNotificationButtonIcon(const std::string & notification_id,int button_index,const gfx::Image & image)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 
HasNotificationOfType(const std::string & id,const NotificationType type)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 
HasPopupNotifications(const NotificationBlockers & blockers)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 
GetPopupNotifications(const NotificationBlockers & blockers,std::list<std::string> * blocked_ids)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 
MarkSinglePopupAsShown(const std::string & id,bool mark_notification_as_read)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 
MarkSinglePopupAsDisplayed(const std::string & id)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 
GetNotificationDelegate(const std::string & id)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 
SetQuietMode(bool quiet_mode)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 
GetNotificationById(const std::string & id)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 
GetVisibleNotifications(const NotificationBlockers & blockers) const279 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 
NotificationCount(const NotificationBlockers & blockers) const298 size_t NotificationList::NotificationCount(
299     const NotificationBlockers& blockers) const {
300   return GetVisibleNotifications(blockers).size();
301 }
302 
UnreadCount(const NotificationBlockers & blockers) const303 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 
GetNotification(const std::string & id)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 
EraseNotification(Notifications::iterator iter)325 void NotificationList::EraseNotification(Notifications::iterator iter) {
326   delete *iter;
327   notifications_.erase(iter);
328 }
329 
PushNotification(scoped_ptr<Notification> notification)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