• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/notification_ui_manager.h"
6 
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/stl_util-inl.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/fullscreen.h"
12 #include "chrome/browser/idle.h"
13 #include "chrome/browser/notifications/balloon_collection.h"
14 #include "chrome/browser/notifications/notification.h"
15 #include "chrome/browser/prefs/pref_service.h"
16 #include "chrome/common/pref_names.h"
17 #include "content/browser/site_instance.h"
18 #include "content/common/notification_service.h"
19 #include "content/common/notification_type.h"
20 
21 namespace {
22 const int kUserStatePollingIntervalSeconds = 1;
23 }
24 
25 // A class which represents a notification waiting to be shown.
26 class QueuedNotification {
27  public:
QueuedNotification(const Notification & notification,Profile * profile)28   QueuedNotification(const Notification& notification, Profile* profile)
29       : notification_(notification),
30         profile_(profile) {
31   }
32 
notification() const33   const Notification& notification() const { return notification_; }
profile() const34   Profile* profile() const { return profile_; }
35 
Replace(const Notification & new_notification)36   void Replace(const Notification& new_notification) {
37     notification_ = new_notification;
38   }
39 
40  private:
41   // The notification to be shown.
42   Notification notification_;
43 
44   // Non owned pointer to the user's profile.
45   Profile* profile_;
46 
47   DISALLOW_COPY_AND_ASSIGN(QueuedNotification);
48 };
49 
NotificationUIManager(PrefService * local_state)50 NotificationUIManager::NotificationUIManager(PrefService* local_state)
51     : balloon_collection_(NULL),
52       is_user_active_(true) {
53   registrar_.Add(this, NotificationType::APP_TERMINATING,
54                  NotificationService::AllSources());
55   position_pref_.Init(prefs::kDesktopNotificationPosition, local_state, this);
56 #if defined(OS_MACOSX)
57   InitFullScreenMonitor();
58 #endif
59 }
60 
~NotificationUIManager()61 NotificationUIManager::~NotificationUIManager() {
62   STLDeleteElements(&show_queue_);
63 #if defined(OS_MACOSX)
64   StopFullScreenMonitor();
65 #endif
66 }
67 
68 // static
Create(PrefService * local_state)69 NotificationUIManager* NotificationUIManager::Create(PrefService* local_state) {
70   BalloonCollection* balloons = BalloonCollection::Create();
71   NotificationUIManager* instance = new NotificationUIManager(local_state);
72   instance->Initialize(balloons);
73   balloons->set_space_change_listener(instance);
74   return instance;
75 }
76 
77 // static
RegisterPrefs(PrefService * prefs)78 void NotificationUIManager::RegisterPrefs(PrefService* prefs) {
79   prefs->RegisterIntegerPref(prefs::kDesktopNotificationPosition,
80                              BalloonCollection::DEFAULT_POSITION);
81 }
82 
Initialize(BalloonCollection * balloon_collection)83 void NotificationUIManager::Initialize(
84     BalloonCollection* balloon_collection) {
85   DCHECK(!balloon_collection_.get());
86   DCHECK(balloon_collection);
87   balloon_collection_.reset(balloon_collection);
88   balloon_collection_->SetPositionPreference(
89       static_cast<BalloonCollection::PositionPreference>(
90           position_pref_.GetValue()));
91 }
92 
Add(const Notification & notification,Profile * profile)93 void NotificationUIManager::Add(const Notification& notification,
94                                 Profile* profile) {
95   if (TryReplacement(notification)) {
96     return;
97   }
98 
99   VLOG(1) << "Added notification. URL: "
100           << notification.content_url().spec();
101   show_queue_.push_back(
102       new QueuedNotification(notification, profile));
103   CheckAndShowNotifications();
104 }
105 
CancelById(const std::string & id)106 bool NotificationUIManager::CancelById(const std::string& id) {
107   // See if this ID hasn't been shown yet.
108   NotificationDeque::iterator iter;
109   for (iter = show_queue_.begin(); iter != show_queue_.end(); ++iter) {
110     if ((*iter)->notification().notification_id() == id) {
111       show_queue_.erase(iter);
112       return true;
113     }
114   }
115   // If it has been shown, remove it from the balloon collections.
116   return balloon_collection_->RemoveById(id);
117 }
118 
CancelAllBySourceOrigin(const GURL & source)119 bool NotificationUIManager::CancelAllBySourceOrigin(const GURL& source) {
120   // Same pattern as CancelById, but more complicated than the above
121   // because there may be multiple notifications from the same source.
122   bool removed = false;
123   NotificationDeque::iterator iter;
124   for (iter = show_queue_.begin(); iter != show_queue_.end();) {
125     if ((*iter)->notification().origin_url() == source) {
126       iter = show_queue_.erase(iter);
127       removed = true;
128     } else {
129       ++iter;
130     }
131   }
132 
133   return balloon_collection_->RemoveBySourceOrigin(source) || removed;
134 }
135 
CancelAll()136 void NotificationUIManager::CancelAll() {
137   STLDeleteElements(&show_queue_);
138   balloon_collection_->RemoveAll();
139 }
140 
CheckAndShowNotifications()141 void NotificationUIManager::CheckAndShowNotifications() {
142   CheckUserState();
143   if (is_user_active_)
144     ShowNotifications();
145 }
146 
CheckUserState()147 void NotificationUIManager::CheckUserState() {
148   bool is_user_active_previously = is_user_active_;
149   is_user_active_ = CalculateIdleState(0) != IDLE_STATE_LOCKED &&
150                     !IsFullScreenMode();
151   if (is_user_active_ == is_user_active_previously)
152     return;
153 
154   if (is_user_active_) {
155     user_state_check_timer_.Stop();
156     // We need to show any postponed nofications when the user becomes active
157     // again.
158     ShowNotifications();
159   } else if (!user_state_check_timer_.IsRunning()) {
160     // Start a timer to detect the moment at which the user becomes active.
161     user_state_check_timer_.Start(
162         base::TimeDelta::FromSeconds(kUserStatePollingIntervalSeconds), this,
163         &NotificationUIManager::CheckUserState);
164   }
165 }
166 
ShowNotifications()167 void NotificationUIManager::ShowNotifications() {
168   while (!show_queue_.empty() && balloon_collection_->HasSpace()) {
169     scoped_ptr<QueuedNotification> queued_notification(show_queue_.front());
170     show_queue_.pop_front();
171     balloon_collection_->Add(queued_notification->notification(),
172                              queued_notification->profile());
173   }
174 }
175 
OnBalloonSpaceChanged()176 void NotificationUIManager::OnBalloonSpaceChanged() {
177   CheckAndShowNotifications();
178 }
179 
TryReplacement(const Notification & notification)180 bool NotificationUIManager::TryReplacement(const Notification& notification) {
181   const GURL& origin = notification.origin_url();
182   const string16& replace_id = notification.replace_id();
183 
184   if (replace_id.empty())
185     return false;
186 
187   // First check the queue of pending notifications for replacement.
188   // Then check the list of notifications already being shown.
189   NotificationDeque::iterator iter;
190   for (iter = show_queue_.begin(); iter != show_queue_.end(); ++iter) {
191     if (origin == (*iter)->notification().origin_url() &&
192         replace_id == (*iter)->notification().replace_id()) {
193       (*iter)->Replace(notification);
194       return true;
195     }
196   }
197 
198   BalloonCollection::Balloons::iterator balloon_iter;
199   BalloonCollection::Balloons balloons =
200       balloon_collection_->GetActiveBalloons();
201   for (balloon_iter = balloons.begin();
202        balloon_iter != balloons.end();
203        ++balloon_iter) {
204     if (origin == (*balloon_iter)->notification().origin_url() &&
205         replace_id == (*balloon_iter)->notification().replace_id()) {
206       (*balloon_iter)->Update(notification);
207       return true;
208     }
209   }
210 
211   return false;
212 }
213 
214 BalloonCollection::PositionPreference
GetPositionPreference()215 NotificationUIManager::GetPositionPreference() {
216   LOG(INFO) << "Current position preference: " << position_pref_.GetValue();
217 
218   return static_cast<BalloonCollection::PositionPreference>(
219       position_pref_.GetValue());
220 }
221 
SetPositionPreference(BalloonCollection::PositionPreference preference)222 void NotificationUIManager::SetPositionPreference(
223     BalloonCollection::PositionPreference preference) {
224   LOG(INFO) << "Setting position preference: " << preference;
225   position_pref_.SetValue(static_cast<int>(preference));
226   balloon_collection_->SetPositionPreference(preference);
227 }
228 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)229 void NotificationUIManager::Observe(NotificationType type,
230                                     const NotificationSource& source,
231                                     const NotificationDetails& details) {
232   if (type == NotificationType::APP_TERMINATING) {
233     CancelAll();
234   } else if (type == NotificationType::PREF_CHANGED) {
235     std::string* name = Details<std::string>(details).ptr();
236     if (*name == prefs::kDesktopNotificationPosition)
237       balloon_collection_->SetPositionPreference(
238           static_cast<BalloonCollection::PositionPreference>(
239               position_pref_.GetValue()));
240   } else {
241     NOTREACHED();
242   }
243 }
244