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