message_center_notification_manager.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
10b8d4eb260eec6b3b2c32b88a2bfe400f1cfb0c0zmo@google.com// Copyright (c) 2012 The Chromium Authors. All rights reserved. 20b8d4eb260eec6b3b2c32b88a2bfe400f1cfb0c0zmo@google.com// Use of this source code is governed by a BSD-style license that can be 30b8d4eb260eec6b3b2c32b88a2bfe400f1cfb0c0zmo@google.com// found in the LICENSE file. 40b8d4eb260eec6b3b2c32b88a2bfe400f1cfb0c0zmo@google.com 50b8d4eb260eec6b3b2c32b88a2bfe400f1cfb0c0zmo@google.com#include "chrome/browser/notifications/message_center_notification_manager.h" 60b8d4eb260eec6b3b2c32b88a2bfe400f1cfb0c0zmo@google.com 70dd3b3ff66cdc50882125d21e60112d5161279b4Ehsan Akhgari#include "base/logging.h" 80dd3b3ff66cdc50882125d21e60112d5161279b4Ehsan Akhgari#include "base/memory/scoped_ptr.h" 90dd3b3ff66cdc50882125d21e60112d5161279b4Ehsan Akhgari#include "base/prefs/pref_service.h" 10fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/chrome_notification_types.h" 11fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/extensions/extension_system.h" 12fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/notifications/desktop_notification_service.h" 13fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/notifications/desktop_notification_service_factory.h" 14fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/notifications/fullscreen_notification_blocker.h" 15fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/notifications/message_center_settings_controller.h" 16fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/notifications/notification.h" 17fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/notifications/screen_lock_notification_blocker.h" 18fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/profiles/profile.h" 19fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/ui/browser_finder.h" 20fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/ui/chrome_pages.h" 21fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/ui/host_desktop.h" 22fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/common/extensions/extension_set.h" 23fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/common/pref_names.h" 24fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "content/public/browser/notification_service.h" 25fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "content/public/browser/web_contents.h" 26fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "content/public/common/url_constants.h" 27fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "extensions/browser/info_map.h" 28fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "ui/gfx/image/image_skia.h" 29fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "ui/message_center/message_center_style.h" 30fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "ui/message_center/message_center_tray.h" 31fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "ui/message_center/message_center_types.h" 32fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "ui/message_center/notifier_settings.h" 33fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo 34fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#if defined(OS_CHROMEOS) 35fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/notifications/login_state_notification_blocker_chromeos.h" 36fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/notifications/multi_user_notification_blocker_chromeos.h" 37fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#include "chrome/browser/ui/ash/multi_user/multi_user_util.h" 38fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#endif 39fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo 40fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#if defined(OS_WIN) 41fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo// The first-run balloon will be shown |kFirstRunIdleDelaySeconds| after all 42fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo// popups go away and the user has notifications in the message center. 43fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Moconst int kFirstRunIdleDelaySeconds = 1; 44fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo#endif 45fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo 46fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao MoMessageCenterNotificationManager::MessageCenterNotificationManager( 47fa63e947cb3eccf463648d21a05d5002c9b8adfaZhenyao Mo message_center::MessageCenter* message_center, 480b8d4eb260eec6b3b2c32b88a2bfe400f1cfb0c0zmo@google.com PrefService* local_state, 490b8d4eb260eec6b3b2c32b88a2bfe400f1cfb0c0zmo@google.com scoped_ptr<message_center::NotifierSettingsProvider> settings_provider) 500dd3b3ff66cdc50882125d21e60112d5161279b4Ehsan Akhgari : message_center_(message_center), 51#if defined(OS_WIN) 52 first_run_idle_timeout_( 53 base::TimeDelta::FromSeconds(kFirstRunIdleDelaySeconds)), 54 weak_factory_(this), 55#endif 56 settings_provider_(settings_provider.Pass()), 57 system_observer_(this), 58 stats_collector_(message_center) { 59#if defined(OS_WIN) 60 first_run_pref_.Init(prefs::kMessageCenterShowedFirstRunBalloon, local_state); 61#endif 62 63 message_center_->AddObserver(this); 64 message_center_->SetNotifierSettingsProvider(settings_provider_.get()); 65 66#if defined(OS_CHROMEOS) 67 blockers_.push_back( 68 new LoginStateNotificationBlockerChromeOS(message_center)); 69 blockers_.push_back(new MultiUserNotificationBlockerChromeOS(message_center)); 70#else 71 blockers_.push_back(new ScreenLockNotificationBlocker(message_center)); 72#endif 73 blockers_.push_back(new FullscreenNotificationBlocker(message_center)); 74 75#if defined(OS_WIN) || defined(OS_MACOSX) \ 76 || (defined(USE_AURA) && !defined(USE_ASH)) 77 // On Windows, Linux and Mac, the notification manager owns the tray icon and 78 // views.Other platforms have global ownership and Create will return NULL. 79 tray_.reset(message_center::CreateMessageCenterTray()); 80#endif 81 registrar_.Add(this, 82 chrome::NOTIFICATION_FULLSCREEN_CHANGED, 83 content::NotificationService::AllSources()); 84} 85 86MessageCenterNotificationManager::~MessageCenterNotificationManager() { 87 message_center_->RemoveObserver(this); 88} 89 90//////////////////////////////////////////////////////////////////////////////// 91// NotificationUIManager 92 93void MessageCenterNotificationManager::Add(const Notification& notification, 94 Profile* profile) { 95 if (Update(notification, profile)) 96 return; 97 98 DesktopNotificationServiceFactory::GetForProfile(profile)-> 99 ShowWelcomeNotificationIfNecessary(notification); 100 101 AddProfileNotification( 102 new ProfileNotification(profile, notification, message_center_)); 103} 104 105bool MessageCenterNotificationManager::Update(const Notification& notification, 106 Profile* profile) { 107 const base::string16& replace_id = notification.replace_id(); 108 if (replace_id.empty()) 109 return false; 110 111 const GURL origin_url = notification.origin_url(); 112 DCHECK(origin_url.is_valid()); 113 114 // Since replace_id is provided by arbitrary JS, we need to use origin_url 115 // (which is an app url in case of app/extension) to scope the replace ids 116 // in the given profile. 117 for (NotificationMap::iterator iter = profile_notifications_.begin(); 118 iter != profile_notifications_.end(); ++iter) { 119 ProfileNotification* old_notification = (*iter).second; 120 if (old_notification->notification().replace_id() == replace_id && 121 old_notification->notification().origin_url() == origin_url && 122 old_notification->profile() == profile) { 123 // Changing the type from non-progress to progress does not count towards 124 // the immediate update allowed in the message center. 125 std::string old_id = 126 old_notification->notification().notification_id(); 127 DCHECK(message_center_->HasNotification(old_id)); 128 129 // Add/remove notification in the local list but just update the same 130 // one in MessageCenter. 131 delete old_notification; 132 profile_notifications_.erase(old_id); 133 ProfileNotification* new_notification = 134 new ProfileNotification(profile, notification, message_center_); 135 profile_notifications_[notification.notification_id()] = new_notification; 136 137 // Now pass a copy to message center. 138 scoped_ptr<message_center::Notification> message_center_notification( 139 make_scoped_ptr(new message_center::Notification(notification))); 140 message_center_->UpdateNotification(old_id, 141 message_center_notification.Pass()); 142 143 new_notification->StartDownloads(); 144 return true; 145 } 146 } 147 return false; 148} 149 150const Notification* MessageCenterNotificationManager::FindById( 151 const std::string& id) const { 152 NotificationMap::const_iterator iter = profile_notifications_.find(id); 153 if (iter == profile_notifications_.end()) 154 return NULL; 155 return &(iter->second->notification()); 156} 157 158bool MessageCenterNotificationManager::CancelById(const std::string& id) { 159 // See if this ID hasn't been shown yet. 160 // If it has been shown, remove it. 161 NotificationMap::iterator iter = profile_notifications_.find(id); 162 if (iter == profile_notifications_.end()) 163 return false; 164 165 RemoveProfileNotification(iter->second); 166 message_center_->RemoveNotification(id, /* by_user */ false); 167 return true; 168} 169 170std::set<std::string> 171MessageCenterNotificationManager::GetAllIdsByProfileAndSourceOrigin( 172 Profile* profile, 173 const GURL& source) { 174 175 std::set<std::string> notification_ids; 176 for (NotificationMap::iterator iter = profile_notifications_.begin(); 177 iter != profile_notifications_.end(); iter++) { 178 if ((*iter).second->notification().origin_url() == source && 179 profile == (*iter).second->profile()) { 180 notification_ids.insert(iter->first); 181 } 182 } 183 return notification_ids; 184} 185 186bool MessageCenterNotificationManager::CancelAllBySourceOrigin( 187 const GURL& source) { 188 // Same pattern as CancelById, but more complicated than the above 189 // because there may be multiple notifications from the same source. 190 bool removed = false; 191 192 for (NotificationMap::iterator loopiter = profile_notifications_.begin(); 193 loopiter != profile_notifications_.end(); ) { 194 NotificationMap::iterator curiter = loopiter++; 195 if ((*curiter).second->notification().origin_url() == source) { 196 const std::string id = curiter->first; 197 RemoveProfileNotification(curiter->second); 198 message_center_->RemoveNotification(id, /* by_user */ false); 199 removed = true; 200 } 201 } 202 return removed; 203} 204 205bool MessageCenterNotificationManager::CancelAllByProfile(Profile* profile) { 206 // Same pattern as CancelAllBySourceOrigin. 207 bool removed = false; 208 209 for (NotificationMap::iterator loopiter = profile_notifications_.begin(); 210 loopiter != profile_notifications_.end(); ) { 211 NotificationMap::iterator curiter = loopiter++; 212 if (profile == (*curiter).second->profile()) { 213 const std::string id = curiter->first; 214 RemoveProfileNotification(curiter->second); 215 message_center_->RemoveNotification(id, /* by_user */ false); 216 removed = true; 217 } 218 } 219 return removed; 220} 221 222void MessageCenterNotificationManager::CancelAll() { 223 message_center_->RemoveAllNotifications(/* by_user */ false); 224} 225 226//////////////////////////////////////////////////////////////////////////////// 227// MessageCenter::Observer 228void MessageCenterNotificationManager::OnNotificationRemoved( 229 const std::string& notification_id, 230 bool by_user) { 231 NotificationMap::const_iterator iter = 232 profile_notifications_.find(notification_id); 233 if (iter != profile_notifications_.end()) 234 RemoveProfileNotification(iter->second); 235 236#if defined(OS_WIN) 237 CheckFirstRunTimer(); 238#endif 239} 240 241void MessageCenterNotificationManager::OnCenterVisibilityChanged( 242 message_center::Visibility visibility) { 243#if defined(OS_WIN) 244 if (visibility == message_center::VISIBILITY_TRANSIENT) 245 CheckFirstRunTimer(); 246#endif 247} 248 249void MessageCenterNotificationManager::OnNotificationUpdated( 250 const std::string& notification_id) { 251#if defined(OS_WIN) 252 CheckFirstRunTimer(); 253#endif 254} 255 256void MessageCenterNotificationManager::SetMessageCenterTrayDelegateForTest( 257 message_center::MessageCenterTrayDelegate* delegate) { 258 tray_.reset(delegate); 259} 260 261void MessageCenterNotificationManager::Observe( 262 int type, 263 const content::NotificationSource& source, 264 const content::NotificationDetails& details) { 265 if (type == chrome::NOTIFICATION_FULLSCREEN_CHANGED) { 266 const bool is_fullscreen = *content::Details<bool>(details).ptr(); 267 268 if (is_fullscreen && tray_.get() && tray_->GetMessageCenterTray()) 269 tray_->GetMessageCenterTray()->HidePopupBubble(); 270 } 271} 272 273//////////////////////////////////////////////////////////////////////////////// 274// ImageDownloads 275 276MessageCenterNotificationManager::ImageDownloads::ImageDownloads( 277 message_center::MessageCenter* message_center, 278 ImageDownloadsObserver* observer) 279 : message_center_(message_center), 280 pending_downloads_(0), 281 observer_(observer) { 282} 283 284MessageCenterNotificationManager::ImageDownloads::~ImageDownloads() { } 285 286void MessageCenterNotificationManager::ImageDownloads::StartDownloads( 287 const Notification& notification) { 288 // In case all downloads are synchronous, assume a pending download. 289 AddPendingDownload(); 290 291 // Notification primary icon. 292 StartDownloadWithImage( 293 notification, 294 ¬ification.icon(), 295 notification.icon_url(), 296 base::Bind(&message_center::MessageCenter::SetNotificationIcon, 297 base::Unretained(message_center_), 298 notification.notification_id())); 299 300 // Notification image. 301 StartDownloadWithImage( 302 notification, 303 NULL, 304 notification.image_url(), 305 base::Bind(&message_center::MessageCenter::SetNotificationImage, 306 base::Unretained(message_center_), 307 notification.notification_id())); 308 309 // Notification button icons. 310 StartDownloadWithImage( 311 notification, 312 NULL, 313 notification.button_one_icon_url(), 314 base::Bind(&message_center::MessageCenter::SetNotificationButtonIcon, 315 base::Unretained(message_center_), 316 notification.notification_id(), 317 0)); 318 StartDownloadWithImage( 319 notification, 320 NULL, 321 notification.button_two_icon_url(), 322 base::Bind(&message_center::MessageCenter::SetNotificationButtonIcon, 323 base::Unretained(message_center_), 324 notification.notification_id(), 325 1)); 326 327 // This should tell the observer we're done if everything was synchronous. 328 PendingDownloadCompleted(); 329} 330 331void MessageCenterNotificationManager::ImageDownloads::StartDownloadWithImage( 332 const Notification& notification, 333 const gfx::Image* image, 334 const GURL& url, 335 const SetImageCallback& callback) { 336 // Set the image directly if we have it. 337 if (image && !image->IsEmpty()) { 338 callback.Run(*image); 339 return; 340 } 341 342 // Leave the image null if there's no URL. 343 if (url.is_empty()) 344 return; 345 346 content::RenderViewHost* host = notification.GetRenderViewHost(); 347 if (!host) { 348 LOG(WARNING) << "Notification needs an image but has no RenderViewHost"; 349 return; 350 } 351 352 content::WebContents* contents = 353 content::WebContents::FromRenderViewHost(host); 354 if (!contents) { 355 LOG(WARNING) << "Notification needs an image but has no WebContents"; 356 return; 357 } 358 359 AddPendingDownload(); 360 361 contents->DownloadImage( 362 url, 363 false, // Not a favicon 364 0, // No maximum size 365 base::Bind( 366 &MessageCenterNotificationManager::ImageDownloads::DownloadComplete, 367 AsWeakPtr(), 368 callback)); 369} 370 371void MessageCenterNotificationManager::ImageDownloads::DownloadComplete( 372 const SetImageCallback& callback, 373 int download_id, 374 int http_status_code, 375 const GURL& image_url, 376 const std::vector<SkBitmap>& bitmaps, 377 const std::vector<gfx::Size>& original_bitmap_sizes) { 378 PendingDownloadCompleted(); 379 380 if (bitmaps.empty()) 381 return; 382 gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmaps[0]); 383 callback.Run(image); 384} 385 386// Private methods. 387 388void MessageCenterNotificationManager::ImageDownloads::AddPendingDownload() { 389 ++pending_downloads_; 390} 391 392void 393MessageCenterNotificationManager::ImageDownloads::PendingDownloadCompleted() { 394 DCHECK(pending_downloads_ > 0); 395 if (--pending_downloads_ == 0 && observer_) 396 observer_->OnDownloadsCompleted(); 397} 398 399//////////////////////////////////////////////////////////////////////////////// 400// ProfileNotification 401 402MessageCenterNotificationManager::ProfileNotification::ProfileNotification( 403 Profile* profile, 404 const Notification& notification, 405 message_center::MessageCenter* message_center) 406 : profile_(profile), 407 notification_(notification), 408 downloads_(new ImageDownloads(message_center, this)) { 409 DCHECK(profile); 410#if defined(OS_CHROMEOS) 411 notification_.set_profile_id(multi_user_util::GetUserIDFromProfile(profile)); 412#endif 413} 414 415MessageCenterNotificationManager::ProfileNotification::~ProfileNotification() { 416} 417 418void MessageCenterNotificationManager::ProfileNotification::StartDownloads() { 419 downloads_->StartDownloads(notification_); 420} 421 422void 423MessageCenterNotificationManager::ProfileNotification::OnDownloadsCompleted() { 424 notification_.DoneRendering(); 425} 426 427std::string 428 MessageCenterNotificationManager::ProfileNotification::GetExtensionId() { 429 extensions::InfoMap* extension_info_map = 430 extensions::ExtensionSystem::Get(profile())->info_map(); 431 ExtensionSet extensions; 432 extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin( 433 notification().origin_url(), notification().process_id(), 434 extensions::APIPermission::kNotification, &extensions); 435 436 DesktopNotificationService* desktop_service = 437 DesktopNotificationServiceFactory::GetForProfile(profile()); 438 for (ExtensionSet::const_iterator iter = extensions.begin(); 439 iter != extensions.end(); ++iter) { 440 if (desktop_service->IsNotifierEnabled(message_center::NotifierId( 441 message_center::NotifierId::APPLICATION, (*iter)->id()))) { 442 return (*iter)->id(); 443 } 444 } 445 return std::string(); 446} 447 448//////////////////////////////////////////////////////////////////////////////// 449// private 450 451void MessageCenterNotificationManager::AddProfileNotification( 452 ProfileNotification* profile_notification) { 453 const Notification& notification = profile_notification->notification(); 454 std::string id = notification.notification_id(); 455 // Notification ids should be unique. 456 DCHECK(profile_notifications_.find(id) == profile_notifications_.end()); 457 profile_notifications_[id] = profile_notification; 458 459 // Create the copy for message center, and ensure the extension ID is correct. 460 scoped_ptr<message_center::Notification> message_center_notification( 461 new message_center::Notification(notification)); 462 message_center_->AddNotification(message_center_notification.Pass()); 463 464 profile_notification->StartDownloads(); 465} 466 467void MessageCenterNotificationManager::RemoveProfileNotification( 468 ProfileNotification* profile_notification) { 469 std::string id = profile_notification->notification().notification_id(); 470 profile_notifications_.erase(id); 471 delete profile_notification; 472} 473 474MessageCenterNotificationManager::ProfileNotification* 475 MessageCenterNotificationManager::FindProfileNotification( 476 const std::string& id) const { 477 NotificationMap::const_iterator iter = profile_notifications_.find(id); 478 if (iter == profile_notifications_.end()) 479 return NULL; 480 481 return (*iter).second; 482} 483