Update mojo surfaces bindings and mojo/cc/ glue
[chromium-blink-merge.git] / chrome / browser / notifications / message_center_notification_manager.cc
blob2dff84140698940ede0f3affcceec03eaa793a53
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 "base/stl_util.h"
12 #include "chrome/browser/chrome_notification_types.h"
13 #include "chrome/browser/extensions/api/notification_provider/notification_provider_api.h"
14 #include "chrome/browser/notifications/desktop_notification_service.h"
15 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
16 #include "chrome/browser/notifications/fullscreen_notification_blocker.h"
17 #include "chrome/browser/notifications/message_center_settings_controller.h"
18 #include "chrome/browser/notifications/notification.h"
19 #include "chrome/browser/notifications/notification_conversion_helper.h"
20 #include "chrome/browser/notifications/screen_lock_notification_blocker.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/browser_finder.h"
23 #include "chrome/browser/ui/chrome_pages.h"
24 #include "chrome/browser/ui/host_desktop.h"
25 #include "chrome/common/extensions/api/notification_provider.h"
26 #include "chrome/common/pref_names.h"
27 #include "content/public/browser/notification_service.h"
28 #include "content/public/browser/web_contents.h"
29 #include "content/public/common/url_constants.h"
30 #include "extensions/browser/extension_registry.h"
31 #include "extensions/browser/extension_system.h"
32 #include "extensions/browser/info_map.h"
33 #include "extensions/common/extension_set.h"
34 #include "extensions/common/permissions/permissions_data.h"
35 #include "ui/gfx/image/image_skia.h"
36 #include "ui/message_center/message_center_style.h"
37 #include "ui/message_center/message_center_tray.h"
38 #include "ui/message_center/message_center_types.h"
39 #include "ui/message_center/notifier_settings.h"
41 #if defined(OS_CHROMEOS)
42 #include "chrome/browser/notifications/login_state_notification_blocker_chromeos.h"
43 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
44 #endif
46 #if defined(USE_ASH)
47 #include "ash/shell.h"
48 #include "ash/system/web_notification/web_notification_tray.h"
49 #endif
51 #if defined(OS_WIN)
52 // The first-run balloon will be shown |kFirstRunIdleDelaySeconds| after all
53 // popups go away and the user has notifications in the message center.
54 const int kFirstRunIdleDelaySeconds = 1;
55 #endif
57 MessageCenterNotificationManager::MessageCenterNotificationManager(
58 message_center::MessageCenter* message_center,
59 PrefService* local_state,
60 scoped_ptr<message_center::NotifierSettingsProvider> settings_provider)
61 : message_center_(message_center),
62 #if defined(OS_WIN)
63 first_run_idle_timeout_(
64 base::TimeDelta::FromSeconds(kFirstRunIdleDelaySeconds)),
65 weak_factory_(this),
66 #endif
67 settings_provider_(settings_provider.Pass()),
68 system_observer_(this),
69 stats_collector_(message_center),
70 google_now_stats_collector_(message_center) {
71 #if defined(OS_WIN)
72 first_run_pref_.Init(prefs::kMessageCenterShowedFirstRunBalloon, local_state);
73 #endif
75 message_center_->AddObserver(this);
76 message_center_->SetNotifierSettingsProvider(settings_provider_.get());
78 #if defined(OS_CHROMEOS)
79 blockers_.push_back(
80 new LoginStateNotificationBlockerChromeOS(message_center));
81 #else
82 blockers_.push_back(new ScreenLockNotificationBlocker(message_center));
83 #endif
84 blockers_.push_back(new FullscreenNotificationBlocker(message_center));
86 #if defined(OS_WIN) || defined(OS_MACOSX) \
87 || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
88 // On Windows, Linux and Mac, the notification manager owns the tray icon and
89 // views.Other platforms have global ownership and Create will return NULL.
90 tray_.reset(message_center::CreateMessageCenterTray());
91 #endif
92 registrar_.Add(this,
93 chrome::NOTIFICATION_FULLSCREEN_CHANGED,
94 content::NotificationService::AllSources());
97 MessageCenterNotificationManager::~MessageCenterNotificationManager() {
98 message_center_->SetNotifierSettingsProvider(NULL);
99 message_center_->RemoveObserver(this);
101 STLDeleteContainerPairSecondPointers(profile_notifications_.begin(),
102 profile_notifications_.end());
103 profile_notifications_.clear();
106 void MessageCenterNotificationManager::RegisterPrefs(
107 PrefRegistrySimple* registry) {
108 registry->RegisterBooleanPref(prefs::kMessageCenterShowedFirstRunBalloon,
109 false);
110 registry->RegisterBooleanPref(prefs::kMessageCenterShowIcon, true);
111 registry->RegisterBooleanPref(prefs::kMessageCenterForcedOnTaskbar, false);
114 ////////////////////////////////////////////////////////////////////////////////
115 // NotificationUIManager
117 void MessageCenterNotificationManager::Add(const Notification& notification,
118 Profile* profile) {
119 if (Update(notification, profile))
120 return;
122 DesktopNotificationServiceFactory::GetForProfile(profile)->
123 ShowWelcomeNotificationIfNecessary(notification);
125 // WARNING: You MUST update the message center via the notification within a
126 // ProfileNotification object or the profile ID will not be correctly set for
127 // ChromeOS.
128 ProfileNotification* profile_notification(
129 new ProfileNotification(profile, notification, message_center_));
130 AddProfileNotification(profile_notification);
132 // TODO(liyanhou): Change the logic to only send notifications to one party.
133 // Currently, if there is an app with notificationProvider permission,
134 // notifications will go to both message center and the app.
135 // Change it to send notifications to message center only when the user chose
136 // default message center (extension_id.empty()).
138 // If there exist apps/extensions that have notificationProvider permission,
139 // route notifications to one of the apps/extensions.
140 std::string extension_id = GetExtensionTakingOverNotifications(profile);
141 if (!extension_id.empty())
142 profile_notification->AddToAlternateProvider(extension_id);
144 message_center_->AddNotification(make_scoped_ptr(
145 new message_center::Notification(profile_notification->notification())));
146 profile_notification->StartDownloads();
149 bool MessageCenterNotificationManager::Update(const Notification& notification,
150 Profile* profile) {
151 const base::string16& replace_id = notification.replace_id();
152 if (replace_id.empty())
153 return false;
155 const GURL origin_url = notification.origin_url();
156 DCHECK(origin_url.is_valid());
158 // Since replace_id is provided by arbitrary JS, we need to use origin_url
159 // (which is an app url in case of app/extension) to scope the replace ids
160 // in the given profile.
161 for (NotificationMap::iterator iter = profile_notifications_.begin();
162 iter != profile_notifications_.end(); ++iter) {
163 ProfileNotification* old_notification = (*iter).second;
164 if (old_notification->notification().replace_id() == replace_id &&
165 old_notification->notification().origin_url() == origin_url &&
166 old_notification->profile() == profile) {
167 // Changing the type from non-progress to progress does not count towards
168 // the immediate update allowed in the message center.
169 std::string old_id =
170 old_notification->notification().delegate_id();
172 // Add/remove notification in the local list but just update the same
173 // one in MessageCenter.
174 delete old_notification;
175 profile_notifications_.erase(old_id);
176 ProfileNotification* new_notification =
177 new ProfileNotification(profile, notification, message_center_);
178 profile_notifications_[notification.delegate_id()] = new_notification;
180 // TODO(liyanhou): Add routing updated notifications to alternative
181 // providers.
183 // WARNING: You MUST use AddProfileNotification or update the message
184 // center via the notification within a ProfileNotification object or the
185 // profile ID will not be correctly set for ChromeOS.
186 message_center_->UpdateNotification(
187 old_id,
188 make_scoped_ptr(new message_center::Notification(
189 new_notification->notification())));
191 new_notification->StartDownloads();
192 return true;
195 return false;
198 const Notification* MessageCenterNotificationManager::FindById(
199 const std::string& id) const {
200 NotificationMap::const_iterator iter = profile_notifications_.find(id);
201 if (iter == profile_notifications_.end())
202 return NULL;
203 return &(iter->second->notification());
206 bool MessageCenterNotificationManager::CancelById(const std::string& id) {
207 // See if this ID hasn't been shown yet.
208 // If it has been shown, remove it.
209 NotificationMap::iterator iter = profile_notifications_.find(id);
210 if (iter == profile_notifications_.end())
211 return false;
213 RemoveProfileNotification(iter->second);
214 message_center_->RemoveNotification(id, /* by_user */ false);
215 return true;
218 std::set<std::string>
219 MessageCenterNotificationManager::GetAllIdsByProfileAndSourceOrigin(
220 Profile* profile,
221 const GURL& source) {
223 std::set<std::string> notification_ids;
224 for (NotificationMap::iterator iter = profile_notifications_.begin();
225 iter != profile_notifications_.end(); iter++) {
226 if ((*iter).second->notification().origin_url() == source &&
227 profile == (*iter).second->profile()) {
228 notification_ids.insert(iter->first);
231 return notification_ids;
234 bool MessageCenterNotificationManager::CancelAllBySourceOrigin(
235 const GURL& source) {
236 // Same pattern as CancelById, but more complicated than the above
237 // because there may be multiple notifications from the same source.
238 bool removed = false;
240 for (NotificationMap::iterator loopiter = profile_notifications_.begin();
241 loopiter != profile_notifications_.end(); ) {
242 NotificationMap::iterator curiter = loopiter++;
243 if ((*curiter).second->notification().origin_url() == source) {
244 const std::string id = curiter->first;
245 RemoveProfileNotification(curiter->second);
246 message_center_->RemoveNotification(id, /* by_user */ false);
247 removed = true;
250 return removed;
253 bool MessageCenterNotificationManager::CancelAllByProfile(Profile* profile) {
254 // Same pattern as CancelAllBySourceOrigin.
255 bool removed = false;
257 for (NotificationMap::iterator loopiter = profile_notifications_.begin();
258 loopiter != profile_notifications_.end(); ) {
259 NotificationMap::iterator curiter = loopiter++;
260 if (profile == (*curiter).second->profile()) {
261 const std::string id = curiter->first;
262 RemoveProfileNotification(curiter->second);
263 message_center_->RemoveNotification(id, /* by_user */ false);
264 removed = true;
267 return removed;
270 void MessageCenterNotificationManager::CancelAll() {
271 message_center_->RemoveAllNotifications(/* by_user */ false);
274 ////////////////////////////////////////////////////////////////////////////////
275 // MessageCenter::Observer
276 void MessageCenterNotificationManager::OnNotificationRemoved(
277 const std::string& notification_id,
278 bool by_user) {
279 NotificationMap::const_iterator iter =
280 profile_notifications_.find(notification_id);
281 if (iter != profile_notifications_.end())
282 RemoveProfileNotification(iter->second);
284 #if defined(OS_WIN)
285 CheckFirstRunTimer();
286 #endif
289 void MessageCenterNotificationManager::OnCenterVisibilityChanged(
290 message_center::Visibility visibility) {
291 #if defined(OS_WIN)
292 if (visibility == message_center::VISIBILITY_TRANSIENT)
293 CheckFirstRunTimer();
294 #endif
297 void MessageCenterNotificationManager::OnNotificationUpdated(
298 const std::string& notification_id) {
299 #if defined(OS_WIN)
300 CheckFirstRunTimer();
301 #endif
304 void MessageCenterNotificationManager::EnsureMessageCenterClosed() {
305 if (tray_.get())
306 tray_->GetMessageCenterTray()->HideMessageCenterBubble();
308 #if defined(USE_ASH)
309 if (ash::Shell::HasInstance()) {
310 ash::WebNotificationTray* tray =
311 ash::Shell::GetInstance()->GetWebNotificationTray();
312 if (tray)
313 tray->GetMessageCenterTray()->HideMessageCenterBubble();
315 #endif
318 void MessageCenterNotificationManager::SetMessageCenterTrayDelegateForTest(
319 message_center::MessageCenterTrayDelegate* delegate) {
320 tray_.reset(delegate);
323 void MessageCenterNotificationManager::Observe(
324 int type,
325 const content::NotificationSource& source,
326 const content::NotificationDetails& details) {
327 if (type == chrome::NOTIFICATION_FULLSCREEN_CHANGED) {
328 const bool is_fullscreen = *content::Details<bool>(details).ptr();
330 if (is_fullscreen && tray_.get() && tray_->GetMessageCenterTray())
331 tray_->GetMessageCenterTray()->HidePopupBubble();
335 ////////////////////////////////////////////////////////////////////////////////
336 // ImageDownloads
338 MessageCenterNotificationManager::ImageDownloads::ImageDownloads(
339 message_center::MessageCenter* message_center,
340 ImageDownloadsObserver* observer)
341 : message_center_(message_center),
342 pending_downloads_(0),
343 observer_(observer) {
346 MessageCenterNotificationManager::ImageDownloads::~ImageDownloads() { }
348 void MessageCenterNotificationManager::ImageDownloads::StartDownloads(
349 const Notification& notification) {
350 // In case all downloads are synchronous, assume a pending download.
351 AddPendingDownload();
353 // Notification primary icon.
354 StartDownloadWithImage(
355 notification,
356 &notification.icon(),
357 notification.icon_url(),
358 base::Bind(&message_center::MessageCenter::SetNotificationIcon,
359 base::Unretained(message_center_),
360 notification.delegate_id()));
362 // Notification image.
363 StartDownloadWithImage(
364 notification,
365 NULL,
366 notification.image_url(),
367 base::Bind(&message_center::MessageCenter::SetNotificationImage,
368 base::Unretained(message_center_),
369 notification.delegate_id()));
371 // Notification button icons.
372 StartDownloadWithImage(
373 notification,
374 NULL,
375 notification.button_one_icon_url(),
376 base::Bind(&message_center::MessageCenter::SetNotificationButtonIcon,
377 base::Unretained(message_center_),
378 notification.delegate_id(),
379 0));
380 StartDownloadWithImage(
381 notification,
382 NULL,
383 notification.button_two_icon_url(),
384 base::Bind(&message_center::MessageCenter::SetNotificationButtonIcon,
385 base::Unretained(message_center_),
386 notification.delegate_id(),
387 1));
389 // This should tell the observer we're done if everything was synchronous.
390 PendingDownloadCompleted();
393 void MessageCenterNotificationManager::ImageDownloads::StartDownloadWithImage(
394 const Notification& notification,
395 const gfx::Image* image,
396 const GURL& url,
397 const SetImageCallback& callback) {
398 // Set the image directly if we have it.
399 if (image && !image->IsEmpty()) {
400 callback.Run(*image);
401 return;
404 // Leave the image null if there's no URL.
405 if (url.is_empty())
406 return;
408 content::WebContents* contents = notification.GetWebContents();
409 if (!contents) {
410 LOG(WARNING) << "Notification needs an image but has no WebContents";
411 return;
414 AddPendingDownload();
416 contents->DownloadImage(
417 url,
418 false, // Not a favicon
419 0, // No maximum size
420 base::Bind(
421 &MessageCenterNotificationManager::ImageDownloads::DownloadComplete,
422 AsWeakPtr(),
423 callback));
426 void MessageCenterNotificationManager::ImageDownloads::DownloadComplete(
427 const SetImageCallback& callback,
428 int download_id,
429 int http_status_code,
430 const GURL& image_url,
431 const std::vector<SkBitmap>& bitmaps,
432 const std::vector<gfx::Size>& original_bitmap_sizes) {
433 PendingDownloadCompleted();
435 if (bitmaps.empty())
436 return;
437 gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmaps[0]);
438 callback.Run(image);
441 // Private methods.
443 void MessageCenterNotificationManager::ImageDownloads::AddPendingDownload() {
444 ++pending_downloads_;
447 void
448 MessageCenterNotificationManager::ImageDownloads::PendingDownloadCompleted() {
449 DCHECK(pending_downloads_ > 0);
450 if (--pending_downloads_ == 0 && observer_)
451 observer_->OnDownloadsCompleted();
454 ////////////////////////////////////////////////////////////////////////////////
455 // ProfileNotification
457 MessageCenterNotificationManager::ProfileNotification::ProfileNotification(
458 Profile* profile,
459 const Notification& notification,
460 message_center::MessageCenter* message_center)
461 : profile_(profile),
462 notification_(notification),
463 downloads_(new ImageDownloads(message_center, this)) {
464 DCHECK(profile);
465 #if defined(OS_CHROMEOS)
466 notification_.set_profile_id(multi_user_util::GetUserIDFromProfile(profile));
467 #endif
470 MessageCenterNotificationManager::ProfileNotification::~ProfileNotification() {
473 void MessageCenterNotificationManager::ProfileNotification::StartDownloads() {
474 downloads_->StartDownloads(notification_);
477 void
478 MessageCenterNotificationManager::ProfileNotification::OnDownloadsCompleted() {
479 notification_.DoneRendering();
482 std::string
483 MessageCenterNotificationManager::ProfileNotification::GetExtensionId() {
484 extensions::InfoMap* extension_info_map =
485 extensions::ExtensionSystem::Get(profile())->info_map();
486 extensions::ExtensionSet extensions;
487 extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin(
488 notification().origin_url(),
489 notification().process_id(),
490 extensions::APIPermission::kNotifications,
491 &extensions);
493 DesktopNotificationService* desktop_service =
494 DesktopNotificationServiceFactory::GetForProfile(profile());
495 for (extensions::ExtensionSet::const_iterator iter = extensions.begin();
496 iter != extensions.end(); ++iter) {
497 if (desktop_service->IsNotifierEnabled(message_center::NotifierId(
498 message_center::NotifierId::APPLICATION, (*iter)->id()))) {
499 return (*iter)->id();
502 return std::string();
505 void
506 MessageCenterNotificationManager::ProfileNotification::AddToAlternateProvider(
507 const std::string extension_id) {
508 // Convert data from Notification type to NotificationOptions type.
509 extensions::api::notifications::NotificationOptions options;
510 NotificationConversionHelper::NotificationToNotificationOptions(notification_,
511 &options);
513 // Send the notification to the alternate provider extension/app.
514 extensions::NotificationProviderEventRouter event_router(profile_);
515 event_router.CreateNotification(extension_id,
516 notification_.notifier_id().id,
517 notification_.delegate_id(),
518 options);
521 ////////////////////////////////////////////////////////////////////////////////
522 // private
524 void MessageCenterNotificationManager::AddProfileNotification(
525 ProfileNotification* profile_notification) {
526 std::string id = profile_notification->notification().delegate_id();
527 // Notification ids should be unique.
528 DCHECK(profile_notifications_.find(id) == profile_notifications_.end());
529 profile_notifications_[id] = profile_notification;
532 void MessageCenterNotificationManager::RemoveProfileNotification(
533 ProfileNotification* profile_notification) {
534 std::string id = profile_notification->notification().delegate_id();
535 profile_notifications_.erase(id);
536 delete profile_notification;
539 MessageCenterNotificationManager::ProfileNotification*
540 MessageCenterNotificationManager::FindProfileNotification(
541 const std::string& id) const {
542 NotificationMap::const_iterator iter = profile_notifications_.find(id);
543 if (iter == profile_notifications_.end())
544 return NULL;
546 return (*iter).second;
549 std::string
550 MessageCenterNotificationManager::GetExtensionTakingOverNotifications(
551 Profile* profile) {
552 // TODO(liyanhou): When additional settings in Chrome Settings is implemented,
553 // change choosing the last app with permission to a user selected app.
554 extensions::ExtensionRegistry* registry =
555 extensions::ExtensionRegistry::Get(profile);
556 DCHECK(registry);
557 std::string extension_id;
558 for (extensions::ExtensionSet::const_iterator it =
559 registry->enabled_extensions().begin();
560 it != registry->enabled_extensions().end();
561 ++it) {
562 if ((*it->get()).permissions_data()->HasAPIPermission(
563 extensions::APIPermission::ID::kNotificationProvider)) {
564 extension_id = (*it->get()).id();
565 return extension_id;
568 return extension_id;