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/message_center_notification_manager.h"
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/prefs/pref_registry_simple.h"
10 #include "base/prefs/pref_service.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/notifications/desktop_notification_service.h"
13 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
14 #include "chrome/browser/notifications/fullscreen_notification_blocker.h"
15 #include "chrome/browser/notifications/message_center_settings_controller.h"
16 #include "chrome/browser/notifications/notification.h"
17 #include "chrome/browser/notifications/screen_lock_notification_blocker.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser_finder.h"
20 #include "chrome/browser/ui/chrome_pages.h"
21 #include "chrome/browser/ui/host_desktop.h"
22 #include "chrome/common/pref_names.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/common/url_constants.h"
26 #include "extensions/browser/extension_system.h"
27 #include "extensions/browser/info_map.h"
28 #include "extensions/common/extension_set.h"
29 #include "ui/gfx/image/image_skia.h"
30 #include "ui/message_center/message_center_style.h"
31 #include "ui/message_center/message_center_tray.h"
32 #include "ui/message_center/message_center_types.h"
33 #include "ui/message_center/notifier_settings.h"
35 #if defined(OS_CHROMEOS)
36 #include "chrome/browser/notifications/login_state_notification_blocker_chromeos.h"
37 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
41 #include "ash/shell.h"
42 #include "ash/system/web_notification/web_notification_tray.h"
46 // The first-run balloon will be shown |kFirstRunIdleDelaySeconds| after all
47 // popups go away and the user has notifications in the message center.
48 const int kFirstRunIdleDelaySeconds
= 1;
51 MessageCenterNotificationManager::MessageCenterNotificationManager(
52 message_center::MessageCenter
* message_center
,
53 PrefService
* local_state
,
54 scoped_ptr
<message_center::NotifierSettingsProvider
> settings_provider
)
55 : message_center_(message_center
),
57 first_run_idle_timeout_(
58 base::TimeDelta::FromSeconds(kFirstRunIdleDelaySeconds
)),
61 settings_provider_(settings_provider
.Pass()),
62 system_observer_(this),
63 stats_collector_(message_center
) {
65 first_run_pref_
.Init(prefs::kMessageCenterShowedFirstRunBalloon
, local_state
);
68 message_center_
->AddObserver(this);
69 message_center_
->SetNotifierSettingsProvider(settings_provider_
.get());
71 #if defined(OS_CHROMEOS)
73 new LoginStateNotificationBlockerChromeOS(message_center
));
75 blockers_
.push_back(new ScreenLockNotificationBlocker(message_center
));
77 blockers_
.push_back(new FullscreenNotificationBlocker(message_center
));
79 #if defined(OS_WIN) || defined(OS_MACOSX) \
80 || (defined(USE_AURA) && !defined(USE_ASH))
81 // On Windows, Linux and Mac, the notification manager owns the tray icon and
82 // views.Other platforms have global ownership and Create will return NULL.
83 tray_
.reset(message_center::CreateMessageCenterTray());
86 chrome::NOTIFICATION_FULLSCREEN_CHANGED
,
87 content::NotificationService::AllSources());
90 MessageCenterNotificationManager::~MessageCenterNotificationManager() {
91 message_center_
->RemoveObserver(this);
94 void MessageCenterNotificationManager::RegisterPrefs(
95 PrefRegistrySimple
* registry
) {
96 registry
->RegisterBooleanPref(prefs::kMessageCenterShowedFirstRunBalloon
,
98 registry
->RegisterBooleanPref(prefs::kMessageCenterShowIcon
, true);
99 registry
->RegisterBooleanPref(prefs::kMessageCenterForcedOnTaskbar
, false);
102 ////////////////////////////////////////////////////////////////////////////////
103 // NotificationUIManager
105 void MessageCenterNotificationManager::Add(const Notification
& notification
,
107 if (Update(notification
, profile
))
110 DesktopNotificationServiceFactory::GetForProfile(profile
)->
111 ShowWelcomeNotificationIfNecessary(notification
);
113 AddProfileNotification(
114 new ProfileNotification(profile
, notification
, message_center_
));
117 bool MessageCenterNotificationManager::Update(const Notification
& notification
,
119 const base::string16
& replace_id
= notification
.replace_id();
120 if (replace_id
.empty())
123 const GURL origin_url
= notification
.origin_url();
124 DCHECK(origin_url
.is_valid());
126 // Since replace_id is provided by arbitrary JS, we need to use origin_url
127 // (which is an app url in case of app/extension) to scope the replace ids
128 // in the given profile.
129 for (NotificationMap::iterator iter
= profile_notifications_
.begin();
130 iter
!= profile_notifications_
.end(); ++iter
) {
131 ProfileNotification
* old_notification
= (*iter
).second
;
132 if (old_notification
->notification().replace_id() == replace_id
&&
133 old_notification
->notification().origin_url() == origin_url
&&
134 old_notification
->profile() == profile
) {
135 // Changing the type from non-progress to progress does not count towards
136 // the immediate update allowed in the message center.
138 old_notification
->notification().notification_id();
139 DCHECK(message_center_
->HasNotification(old_id
));
141 // Add/remove notification in the local list but just update the same
142 // one in MessageCenter.
143 delete old_notification
;
144 profile_notifications_
.erase(old_id
);
145 ProfileNotification
* new_notification
=
146 new ProfileNotification(profile
, notification
, message_center_
);
147 profile_notifications_
[notification
.notification_id()] = new_notification
;
149 // Now pass a copy to message center.
150 scoped_ptr
<message_center::Notification
> message_center_notification(
151 make_scoped_ptr(new message_center::Notification(notification
)));
152 message_center_
->UpdateNotification(old_id
,
153 message_center_notification
.Pass());
155 new_notification
->StartDownloads();
162 const Notification
* MessageCenterNotificationManager::FindById(
163 const std::string
& id
) const {
164 NotificationMap::const_iterator iter
= profile_notifications_
.find(id
);
165 if (iter
== profile_notifications_
.end())
167 return &(iter
->second
->notification());
170 bool MessageCenterNotificationManager::CancelById(const std::string
& id
) {
171 // See if this ID hasn't been shown yet.
172 // If it has been shown, remove it.
173 NotificationMap::iterator iter
= profile_notifications_
.find(id
);
174 if (iter
== profile_notifications_
.end())
177 RemoveProfileNotification(iter
->second
);
178 message_center_
->RemoveNotification(id
, /* by_user */ false);
182 std::set
<std::string
>
183 MessageCenterNotificationManager::GetAllIdsByProfileAndSourceOrigin(
185 const GURL
& source
) {
187 std::set
<std::string
> notification_ids
;
188 for (NotificationMap::iterator iter
= profile_notifications_
.begin();
189 iter
!= profile_notifications_
.end(); iter
++) {
190 if ((*iter
).second
->notification().origin_url() == source
&&
191 profile
== (*iter
).second
->profile()) {
192 notification_ids
.insert(iter
->first
);
195 return notification_ids
;
198 bool MessageCenterNotificationManager::CancelAllBySourceOrigin(
199 const GURL
& source
) {
200 // Same pattern as CancelById, but more complicated than the above
201 // because there may be multiple notifications from the same source.
202 bool removed
= false;
204 for (NotificationMap::iterator loopiter
= profile_notifications_
.begin();
205 loopiter
!= profile_notifications_
.end(); ) {
206 NotificationMap::iterator curiter
= loopiter
++;
207 if ((*curiter
).second
->notification().origin_url() == source
) {
208 const std::string id
= curiter
->first
;
209 RemoveProfileNotification(curiter
->second
);
210 message_center_
->RemoveNotification(id
, /* by_user */ false);
217 bool MessageCenterNotificationManager::CancelAllByProfile(Profile
* profile
) {
218 // Same pattern as CancelAllBySourceOrigin.
219 bool removed
= false;
221 for (NotificationMap::iterator loopiter
= profile_notifications_
.begin();
222 loopiter
!= profile_notifications_
.end(); ) {
223 NotificationMap::iterator curiter
= loopiter
++;
224 if (profile
== (*curiter
).second
->profile()) {
225 const std::string id
= curiter
->first
;
226 RemoveProfileNotification(curiter
->second
);
227 message_center_
->RemoveNotification(id
, /* by_user */ false);
234 void MessageCenterNotificationManager::CancelAll() {
235 message_center_
->RemoveAllNotifications(/* by_user */ false);
238 ////////////////////////////////////////////////////////////////////////////////
239 // MessageCenter::Observer
240 void MessageCenterNotificationManager::OnNotificationRemoved(
241 const std::string
& notification_id
,
243 NotificationMap::const_iterator iter
=
244 profile_notifications_
.find(notification_id
);
245 if (iter
!= profile_notifications_
.end())
246 RemoveProfileNotification(iter
->second
);
249 CheckFirstRunTimer();
253 void MessageCenterNotificationManager::OnCenterVisibilityChanged(
254 message_center::Visibility visibility
) {
256 if (visibility
== message_center::VISIBILITY_TRANSIENT
)
257 CheckFirstRunTimer();
261 void MessageCenterNotificationManager::OnNotificationUpdated(
262 const std::string
& notification_id
) {
264 CheckFirstRunTimer();
268 void MessageCenterNotificationManager::EnsureMessageCenterClosed() {
270 tray_
->GetMessageCenterTray()->HideMessageCenterBubble();
273 if (ash::Shell::HasInstance()) {
274 ash::WebNotificationTray
* tray
=
275 ash::Shell::GetInstance()->GetWebNotificationTray();
277 tray
->GetMessageCenterTray()->HideMessageCenterBubble();
282 void MessageCenterNotificationManager::SetMessageCenterTrayDelegateForTest(
283 message_center::MessageCenterTrayDelegate
* delegate
) {
284 tray_
.reset(delegate
);
287 void MessageCenterNotificationManager::Observe(
289 const content::NotificationSource
& source
,
290 const content::NotificationDetails
& details
) {
291 if (type
== chrome::NOTIFICATION_FULLSCREEN_CHANGED
) {
292 const bool is_fullscreen
= *content::Details
<bool>(details
).ptr();
294 if (is_fullscreen
&& tray_
.get() && tray_
->GetMessageCenterTray())
295 tray_
->GetMessageCenterTray()->HidePopupBubble();
299 ////////////////////////////////////////////////////////////////////////////////
302 MessageCenterNotificationManager::ImageDownloads::ImageDownloads(
303 message_center::MessageCenter
* message_center
,
304 ImageDownloadsObserver
* observer
)
305 : message_center_(message_center
),
306 pending_downloads_(0),
307 observer_(observer
) {
310 MessageCenterNotificationManager::ImageDownloads::~ImageDownloads() { }
312 void MessageCenterNotificationManager::ImageDownloads::StartDownloads(
313 const Notification
& notification
) {
314 // In case all downloads are synchronous, assume a pending download.
315 AddPendingDownload();
317 // Notification primary icon.
318 StartDownloadWithImage(
320 ¬ification
.icon(),
321 notification
.icon_url(),
322 base::Bind(&message_center::MessageCenter::SetNotificationIcon
,
323 base::Unretained(message_center_
),
324 notification
.notification_id()));
326 // Notification image.
327 StartDownloadWithImage(
330 notification
.image_url(),
331 base::Bind(&message_center::MessageCenter::SetNotificationImage
,
332 base::Unretained(message_center_
),
333 notification
.notification_id()));
335 // Notification button icons.
336 StartDownloadWithImage(
339 notification
.button_one_icon_url(),
340 base::Bind(&message_center::MessageCenter::SetNotificationButtonIcon
,
341 base::Unretained(message_center_
),
342 notification
.notification_id(),
344 StartDownloadWithImage(
347 notification
.button_two_icon_url(),
348 base::Bind(&message_center::MessageCenter::SetNotificationButtonIcon
,
349 base::Unretained(message_center_
),
350 notification
.notification_id(),
353 // This should tell the observer we're done if everything was synchronous.
354 PendingDownloadCompleted();
357 void MessageCenterNotificationManager::ImageDownloads::StartDownloadWithImage(
358 const Notification
& notification
,
359 const gfx::Image
* image
,
361 const SetImageCallback
& callback
) {
362 // Set the image directly if we have it.
363 if (image
&& !image
->IsEmpty()) {
364 callback
.Run(*image
);
368 // Leave the image null if there's no URL.
372 content::WebContents
* contents
= notification
.GetWebContents();
374 LOG(WARNING
) << "Notification needs an image but has no WebContents";
378 AddPendingDownload();
380 contents
->DownloadImage(
382 false, // Not a favicon
383 0, // No maximum size
385 &MessageCenterNotificationManager::ImageDownloads::DownloadComplete
,
390 void MessageCenterNotificationManager::ImageDownloads::DownloadComplete(
391 const SetImageCallback
& callback
,
393 int http_status_code
,
394 const GURL
& image_url
,
395 const std::vector
<SkBitmap
>& bitmaps
,
396 const std::vector
<gfx::Size
>& original_bitmap_sizes
) {
397 PendingDownloadCompleted();
401 gfx::Image image
= gfx::Image::CreateFrom1xBitmap(bitmaps
[0]);
407 void MessageCenterNotificationManager::ImageDownloads::AddPendingDownload() {
408 ++pending_downloads_
;
412 MessageCenterNotificationManager::ImageDownloads::PendingDownloadCompleted() {
413 DCHECK(pending_downloads_
> 0);
414 if (--pending_downloads_
== 0 && observer_
)
415 observer_
->OnDownloadsCompleted();
418 ////////////////////////////////////////////////////////////////////////////////
419 // ProfileNotification
421 MessageCenterNotificationManager::ProfileNotification::ProfileNotification(
423 const Notification
& notification
,
424 message_center::MessageCenter
* message_center
)
426 notification_(notification
),
427 downloads_(new ImageDownloads(message_center
, this)) {
429 #if defined(OS_CHROMEOS)
430 notification_
.set_profile_id(multi_user_util::GetUserIDFromProfile(profile
));
434 MessageCenterNotificationManager::ProfileNotification::~ProfileNotification() {
437 void MessageCenterNotificationManager::ProfileNotification::StartDownloads() {
438 downloads_
->StartDownloads(notification_
);
442 MessageCenterNotificationManager::ProfileNotification::OnDownloadsCompleted() {
443 notification_
.DoneRendering();
447 MessageCenterNotificationManager::ProfileNotification::GetExtensionId() {
448 extensions::InfoMap
* extension_info_map
=
449 extensions::ExtensionSystem::Get(profile())->info_map();
450 extensions::ExtensionSet extensions
;
451 extension_info_map
->GetExtensionsWithAPIPermissionForSecurityOrigin(
452 notification().origin_url(), notification().process_id(),
453 extensions::APIPermission::kNotification
, &extensions
);
455 DesktopNotificationService
* desktop_service
=
456 DesktopNotificationServiceFactory::GetForProfile(profile());
457 for (extensions::ExtensionSet::const_iterator iter
= extensions
.begin();
458 iter
!= extensions
.end(); ++iter
) {
459 if (desktop_service
->IsNotifierEnabled(message_center::NotifierId(
460 message_center::NotifierId::APPLICATION
, (*iter
)->id()))) {
461 return (*iter
)->id();
464 return std::string();
467 ////////////////////////////////////////////////////////////////////////////////
470 void MessageCenterNotificationManager::AddProfileNotification(
471 ProfileNotification
* profile_notification
) {
472 const Notification
& notification
= profile_notification
->notification();
473 std::string id
= notification
.notification_id();
474 // Notification ids should be unique.
475 DCHECK(profile_notifications_
.find(id
) == profile_notifications_
.end());
476 profile_notifications_
[id
] = profile_notification
;
478 // Create the copy for message center, and ensure the extension ID is correct.
479 scoped_ptr
<message_center::Notification
> message_center_notification(
480 new message_center::Notification(notification
));
481 message_center_
->AddNotification(message_center_notification
.Pass());
483 profile_notification
->StartDownloads();
486 void MessageCenterNotificationManager::RemoveProfileNotification(
487 ProfileNotification
* profile_notification
) {
488 std::string id
= profile_notification
->notification().notification_id();
489 profile_notifications_
.erase(id
);
490 delete profile_notification
;
493 MessageCenterNotificationManager::ProfileNotification
*
494 MessageCenterNotificationManager::FindProfileNotification(
495 const std::string
& id
) const {
496 NotificationMap::const_iterator iter
= profile_notifications_
.find(id
);
497 if (iter
== profile_notifications_
.end())
500 return (*iter
).second
;