message_center_notification_manager.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/extensions/extension_service.h" 11#include "chrome/browser/notifications/desktop_notification_service.h" 12#include "chrome/browser/notifications/desktop_notification_service_factory.h" 13#include "chrome/browser/notifications/message_center_settings_controller.h" 14#include "chrome/browser/notifications/notification.h" 15#include "chrome/browser/profiles/profile.h" 16#include "chrome/browser/ui/browser_finder.h" 17#include "chrome/browser/ui/chrome_pages.h" 18#include "chrome/browser/ui/host_desktop.h" 19#include "chrome/common/extensions/extension_set.h" 20#include "chrome/common/pref_names.h" 21#include "content/public/browser/web_contents.h" 22#include "ui/message_center/message_center_constants.h" 23#include "ui/message_center/message_center_tray.h" 24 25MessageCenterNotificationManager::MessageCenterNotificationManager( 26 message_center::MessageCenter* message_center) 27 : message_center_(message_center), 28 settings_controller_(new MessageCenterSettingsController) { 29 message_center_->SetDelegate(this); 30 31#if !defined(OS_CHROMEOS) 32 // On Windows, the notification manager owns the tray icon and views. Other 33 // platforms have global ownership and Create will return NULL. 34 tray_.reset(message_center::CreateMessageCenterTray()); 35#endif 36} 37 38MessageCenterNotificationManager::~MessageCenterNotificationManager() { 39} 40 41 42//////////////////////////////////////////////////////////////////////////////// 43// NotificationUIManager 44 45bool MessageCenterNotificationManager::DoesIdExist(const std::string& id) { 46 if (NotificationUIManagerImpl::DoesIdExist(id)) 47 return true; 48 NotificationMap::iterator iter = profile_notifications_.find(id); 49 if (iter == profile_notifications_.end()) 50 return false; 51 return true; 52} 53 54bool MessageCenterNotificationManager::CancelById(const std::string& id) { 55 // See if this ID hasn't been shown yet. 56 if (NotificationUIManagerImpl::CancelById(id)) 57 return true; 58 59 // If it has been shown, remove it. 60 NotificationMap::iterator iter = profile_notifications_.find(id); 61 if (iter == profile_notifications_.end()) 62 return false; 63 64 RemoveProfileNotification((*iter).second, false); 65 return true; 66} 67 68bool MessageCenterNotificationManager::CancelAllBySourceOrigin( 69 const GURL& source) { 70 // Same pattern as CancelById, but more complicated than the above 71 // because there may be multiple notifications from the same source. 72 bool removed = NotificationUIManagerImpl::CancelAllBySourceOrigin(source); 73 74 for (NotificationMap::iterator loopiter = profile_notifications_.begin(); 75 loopiter != profile_notifications_.end(); ) { 76 NotificationMap::iterator curiter = loopiter++; 77 if ((*curiter).second->notification().origin_url() == source) { 78 // This action occurs when extension is unloaded. Closing notifications 79 // is not by user, so |false|. 80 RemoveProfileNotification((*curiter).second, false); 81 removed = true; 82 } 83 } 84 return removed; 85} 86 87bool MessageCenterNotificationManager::CancelAllByProfile(Profile* profile) { 88 // Same pattern as CancelAllBySourceOrigin. 89 bool removed = NotificationUIManagerImpl::CancelAllByProfile(profile); 90 91 for (NotificationMap::iterator loopiter = profile_notifications_.begin(); 92 loopiter != profile_notifications_.end(); ) { 93 NotificationMap::iterator curiter = loopiter++; 94 if ((*curiter).second->profile() == profile) { 95 // This action occurs when profile is unloaded. Closing notifications is 96 // not by user, so |false|. 97 RemoveProfileNotification((*curiter).second, false); 98 removed = true; 99 } 100 } 101 return removed; 102} 103 104void MessageCenterNotificationManager::CancelAll() { 105 NotificationUIManagerImpl::CancelAll(); 106 107 for (NotificationMap::iterator loopiter = profile_notifications_.begin(); 108 loopiter != profile_notifications_.end(); ) { 109 // This action occurs when Chrome is terminating. Closing notifications is 110 // not by user, so |false|. 111 RemoveProfileNotification((*loopiter++).second, false); 112 } 113} 114 115//////////////////////////////////////////////////////////////////////////////// 116// NotificationUIManagerImpl 117 118bool MessageCenterNotificationManager::ShowNotification( 119 const Notification& notification, Profile* profile) { 120 // There is always space in MessageCenter, it never rejects Notifications. 121 AddProfileNotification( 122 new ProfileNotification(profile, notification, message_center_)); 123 return true; 124} 125 126bool MessageCenterNotificationManager::UpdateNotification( 127 const Notification& notification, Profile* profile) { 128 const string16& replace_id = notification.replace_id(); 129 DCHECK(!replace_id.empty()); 130 const GURL origin_url = notification.origin_url(); 131 DCHECK(origin_url.is_valid()); 132 133 // Since replace_id is provided by arbitrary JS, we need to use origin_url 134 // (which is an app url in case of app/extension) to scope the replace ids 135 // in the given profile. 136 for (NotificationMap::iterator iter = profile_notifications_.begin(); 137 iter != profile_notifications_.end(); ++iter) { 138 ProfileNotification* old_notification = (*iter).second; 139 if (old_notification->notification().replace_id() == replace_id && 140 old_notification->notification().origin_url() == origin_url && 141 old_notification->profile()->IsSameProfile(profile)) { 142 std::string old_id = 143 old_notification->notification().notification_id(); 144 DCHECK(message_center_->notification_list()->HasNotification(old_id)); 145 146 // Add/remove notification in the local list but just update the same 147 // one in MessageCenter. 148 old_notification->notification().Close(false); // Not by user. 149 delete old_notification; 150 profile_notifications_.erase(old_id); 151 ProfileNotification* new_notification = 152 new ProfileNotification(profile, notification, message_center_); 153 profile_notifications_[notification.notification_id()] = new_notification; 154 message_center_->UpdateNotification(old_id, 155 notification.notification_id(), 156 notification.title(), 157 notification.body(), 158 notification.optional_fields()); 159 new_notification->StartDownloads(); 160 notification.Display(); 161 return true; 162 } 163 } 164 return false; 165} 166 167//////////////////////////////////////////////////////////////////////////////// 168// MessageCenter::Delegate 169 170void MessageCenterNotificationManager::DisableExtension( 171 const std::string& notification_id) { 172 ProfileNotification* profile_notification = 173 FindProfileNotification(notification_id); 174 std::string extension_id = profile_notification->GetExtensionId(); 175 DCHECK(!extension_id.empty()); // or UI should not have enabled the command. 176 profile_notification->profile()->GetExtensionService()->DisableExtension( 177 extension_id, extensions::Extension::DISABLE_USER_ACTION); 178} 179 180void MessageCenterNotificationManager::DisableNotificationsFromSource( 181 const std::string& notification_id) { 182 ProfileNotification* profile_notification = 183 FindProfileNotification(notification_id); 184 // UI should not have enabled the command if there is no valid source. 185 DCHECK(profile_notification->notification().origin_url().is_valid()); 186 DesktopNotificationService* service = 187 DesktopNotificationServiceFactory::GetForProfile( 188 profile_notification->profile()); 189 service->DenyPermission(profile_notification->notification().origin_url()); 190} 191 192void MessageCenterNotificationManager::NotificationRemoved( 193 const std::string& notification_id, 194 bool by_user) { 195 RemoveProfileNotification(FindProfileNotification(notification_id), by_user); 196} 197 198void MessageCenterNotificationManager::ShowSettings( 199 const std::string& notification_id) { 200 // The per-message-center Settings button passes an empty string. 201 if (notification_id.empty()) { 202 NOTIMPLEMENTED(); 203 return; 204 } 205 206 ProfileNotification* profile_notification = 207 FindProfileNotification(notification_id); 208 Browser* browser = 209 chrome::FindOrCreateTabbedBrowser(profile_notification->profile(), 210 chrome::HOST_DESKTOP_TYPE_NATIVE); 211 if (profile_notification->GetExtensionId().empty()) 212 chrome::ShowContentSettings(browser, CONTENT_SETTINGS_TYPE_NOTIFICATIONS); 213 else 214 chrome::ShowExtensions(browser, std::string()); 215} 216 217void MessageCenterNotificationManager::ShowSettingsDialog( 218 gfx::NativeView context) { 219 settings_controller_->ShowSettingsDialog(context); 220} 221 222void MessageCenterNotificationManager::OnClicked( 223 const std::string& notification_id) { 224 FindProfileNotification(notification_id)->notification().Click(); 225} 226 227void MessageCenterNotificationManager::OnButtonClicked( 228 const std::string& notification_id, int button_index) { 229 FindProfileNotification(notification_id)->notification().ButtonClick( 230 button_index); 231} 232 233//////////////////////////////////////////////////////////////////////////////// 234// ImageDownloads 235 236MessageCenterNotificationManager::ImageDownloads::ImageDownloads( 237 message_center::MessageCenter* message_center) 238 : message_center_(message_center) { 239} 240 241MessageCenterNotificationManager::ImageDownloads::~ImageDownloads() { } 242 243void MessageCenterNotificationManager::ImageDownloads::StartDownloads( 244 const Notification& notification) { 245 // Notification primary icon. 246 StartDownloadWithImage( 247 notification, 248 ¬ification.icon(), 249 notification.icon_url(), 250 message_center::kNotificationIconSize, 251 base::Bind(&message_center::MessageCenter::SetNotificationIcon, 252 base::Unretained(message_center_), 253 notification.notification_id())); 254 255 // Notification image. 256 StartDownloadByKey( 257 notification, 258 message_center::kImageUrlKey, 259 message_center::kNotificationPreferredImageSize, 260 base::Bind(&message_center::MessageCenter::SetNotificationImage, 261 base::Unretained(message_center_), 262 notification.notification_id())); 263 264 // Notification button icons. 265 StartDownloadByKey( 266 notification, 267 message_center::kButtonOneIconUrlKey, 268 message_center::kNotificationButtonIconSize, 269 base::Bind(&message_center::MessageCenter::SetNotificationButtonIcon, 270 base::Unretained(message_center_), 271 notification.notification_id(), 0)); 272 StartDownloadByKey( 273 notification, message_center::kButtonTwoIconUrlKey, 274 message_center::kNotificationButtonIconSize, 275 base::Bind(&message_center::MessageCenter::SetNotificationButtonIcon, 276 base::Unretained(message_center_), 277 notification.notification_id(), 1)); 278} 279 280void MessageCenterNotificationManager::ImageDownloads::StartDownloadWithImage( 281 const Notification& notification, 282 const gfx::Image* image, 283 const GURL& url, 284 int size, 285 const SetImageCallback& callback) { 286 // Set the image directly if we have it. 287 if (image && !image->IsEmpty()) { 288 callback.Run(*image); 289 return; 290 } 291 292 // Leave the image null if there's no URL. 293 if (url.is_empty()) 294 return; 295 296 content::RenderViewHost* host = notification.GetRenderViewHost(); 297 if (!host) { 298 LOG(WARNING) << "Notification needs an image but has no RenderViewHost"; 299 return; 300 } 301 302 content::WebContents* contents = 303 content::WebContents::FromRenderViewHost(host); 304 if (!contents) { 305 LOG(WARNING) << "Notification needs an image but has no WebContents"; 306 return; 307 } 308 309 contents->DownloadFavicon( 310 url, 311 false, 312 size, 313 base::Bind( 314 &MessageCenterNotificationManager::ImageDownloads::DownloadComplete, 315 AsWeakPtr(), 316 callback)); 317} 318 319void MessageCenterNotificationManager::ImageDownloads::StartDownloadByKey( 320 const Notification& notification, 321 const char* key, 322 int size, 323 const SetImageCallback& callback) { 324 const base::DictionaryValue* optional_fields = notification.optional_fields(); 325 if (optional_fields && optional_fields->HasKey(key)) { 326 string16 url; 327 optional_fields->GetString(key, &url); 328 StartDownloadWithImage(notification, NULL, GURL(url), size, callback); 329 } 330} 331 332void MessageCenterNotificationManager::ImageDownloads::DownloadComplete( 333 const SetImageCallback& callback, 334 int download_id, 335 const GURL& image_url, 336 int requested_size, 337 const std::vector<SkBitmap>& bitmaps) { 338 if (bitmaps.empty()) 339 return; 340 gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmaps[0]); 341 callback.Run(image); 342} 343 344//////////////////////////////////////////////////////////////////////////////// 345// ProfileNotification 346 347MessageCenterNotificationManager::ProfileNotification::ProfileNotification( 348 Profile* profile, 349 const Notification& notification, 350 message_center::MessageCenter* message_center) 351 : profile_(profile), 352 notification_(notification), 353 downloads_(new ImageDownloads(message_center)) { 354 DCHECK(profile); 355} 356 357MessageCenterNotificationManager::ProfileNotification::~ProfileNotification() { 358} 359 360void MessageCenterNotificationManager::ProfileNotification::StartDownloads() { 361 downloads_->StartDownloads(notification_); 362} 363 364std::string 365 MessageCenterNotificationManager::ProfileNotification::GetExtensionId() { 366 const ExtensionURLInfo url(notification().origin_url()); 367 const ExtensionService* service = profile()->GetExtensionService(); 368 const extensions::Extension* extension = 369 service->extensions()->GetExtensionOrAppByURL(url); 370 return extension ? extension->id() : std::string(); 371} 372 373//////////////////////////////////////////////////////////////////////////////// 374// private 375 376void MessageCenterNotificationManager::AddProfileNotification( 377 ProfileNotification* profile_notification) { 378 const Notification& notification = profile_notification->notification(); 379 std::string id = notification.notification_id(); 380 // Notification ids should be unique. 381 DCHECK(profile_notifications_.find(id) == profile_notifications_.end()); 382 profile_notifications_[id] = profile_notification; 383 384 message_center_->AddNotification(notification.type(), 385 notification.notification_id(), 386 notification.title(), 387 notification.body(), 388 notification.display_source(), 389 profile_notification->GetExtensionId(), 390 notification.optional_fields()); 391 profile_notification->StartDownloads(); 392 notification.Display(); 393} 394 395void MessageCenterNotificationManager::RemoveProfileNotification( 396 ProfileNotification* profile_notification, 397 bool by_user) { 398 profile_notification->notification().Close(by_user); 399 std::string id = profile_notification->notification().notification_id(); 400 message_center_->RemoveNotification(id); 401 profile_notifications_.erase(id); 402 delete profile_notification; 403} 404 405MessageCenterNotificationManager::ProfileNotification* 406 MessageCenterNotificationManager::FindProfileNotification( 407 const std::string& id) const { 408 NotificationMap::const_iterator iter = profile_notifications_.find(id); 409 // If the notification is shown in UI, it must be in the map. 410 DCHECK(iter != profile_notifications_.end()); 411 DCHECK((*iter).second); 412 return (*iter).second; 413} 414