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 "chrome/browser/notifications/balloon_notification_ui_manager.h"
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"
21 // A class which represents a notification waiting to be shown.
22 class QueuedNotification
{
24 QueuedNotification(const Notification
& notification
, Profile
* profile
)
25 : notification_(notification
),
29 const Notification
& notification() const { return notification_
; }
30 Profile
* profile() const { return profile_
; }
32 void Replace(const Notification
& new_notification
) {
33 notification_
= new_notification
;
37 // The notification to be shown.
38 Notification notification_
;
40 // Non owned pointer to the user's profile.
43 DISALLOW_COPY_AND_ASSIGN(QueuedNotification
);
46 BalloonNotificationUIManager::BalloonNotificationUIManager(
47 PrefService
* local_state
)
48 : NotificationPrefsManager(local_state
),
49 // Passes NULL to blockers since |message_center| is not used from balloon
51 screen_lock_blocker_(NULL
),
52 fullscreen_blocker_(NULL
),
53 system_observer_(this) {
55 prefs::kDesktopNotificationPosition
,
58 &BalloonNotificationUIManager::OnDesktopNotificationPositionChanged
,
59 base::Unretained(this)));
62 BalloonNotificationUIManager::~BalloonNotificationUIManager() {
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);
77 void BalloonNotificationUIManager::Add(const Notification
& notification
,
79 if (Update(notification
, profile
)) {
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();
90 bool BalloonNotificationUIManager::Update(const Notification
& notification
,
92 const GURL
& origin
= notification
.origin_url();
93 const base::string16
& replace_id
= notification
.replace_id();
95 if (replace_id
.empty())
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
);
110 return UpdateNotification(notification
, profile
);
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());
121 return balloon_collection_
->FindById(id
);
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
);
133 // If it has been shown, remove it from the balloon collections.
134 return balloon_collection_
->RemoveById(id
);
137 std::set
<std::string
>
138 BalloonNotificationUIManager::GetAllIdsByProfileAndSourceOrigin(
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());
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());
159 return notification_ids
;
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
) {
173 loopiter
= show_queue_
.erase(loopiter
);
176 return balloon_collection_
->RemoveBySourceOrigin(source
) || removed
;
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
) {
189 loopiter
= show_queue_
.erase(loopiter
);
192 return balloon_collection_
->RemoveByProfile(profile
) || removed
;
195 void BalloonNotificationUIManager::CancelAll() {
196 balloon_collection_
->RemoveAll();
199 BalloonCollection
* BalloonNotificationUIManager::balloon_collection() {
200 return balloon_collection_
.get();
203 NotificationPrefsManager
* BalloonNotificationUIManager::prefs_manager() {
207 bool BalloonNotificationUIManager::ShowNotification(
208 const Notification
& notification
,
210 if (!balloon_collection_
->HasSpace())
212 balloon_collection_
->Add(notification
, profile
);
216 void BalloonNotificationUIManager::OnBalloonSpaceChanged() {
217 CheckAndShowNotifications();
220 void BalloonNotificationUIManager::OnBlockingStateChanged(
221 message_center::NotificationBlocker
* blocker
) {
222 CheckAndShowNotifications();
225 bool BalloonNotificationUIManager::UpdateNotification(
226 const Notification
& notification
,
228 const GURL
& origin
= notification
.origin_url();
229 const base::string16
& replace_id
= notification
.replace_id();
231 DCHECK(!replace_id
.empty());
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
);
248 BalloonCollection::PositionPreference
249 BalloonNotificationUIManager::GetPositionPreference() const {
250 return static_cast<BalloonCollection::PositionPreference
>(
251 position_pref_
.GetValue());
254 void BalloonNotificationUIManager::SetPositionPreference(
255 BalloonCollection::PositionPreference preference
) {
256 position_pref_
.SetValue(static_cast<int>(preference
));
257 balloon_collection_
->SetPositionPreference(preference
);
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()) {
270 void BalloonNotificationUIManager::OnDesktopNotificationPositionChanged() {
271 balloon_collection_
->SetPositionPreference(
272 static_cast<BalloonCollection::PositionPreference
>(
273 position_pref_
.GetValue()));
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
);
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@";
297 return static_cast<BalloonNotificationUIManager
*>(
298 g_browser_process
->notification_ui_manager());
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());