desktop_notification_service.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/desktop_notification_service.h" 6 7#include "base/metrics/histogram.h" 8#include "base/prefs/scoped_user_pref_update.h" 9#include "base/strings/utf_string_conversions.h" 10#include "base/threading/thread.h" 11#include "chrome/browser/browser_process.h" 12#include "chrome/browser/chrome_notification_types.h" 13#include "chrome/browser/content_settings/content_settings_details.h" 14#include "chrome/browser/content_settings/content_settings_provider.h" 15#include "chrome/browser/content_settings/host_content_settings_map.h" 16#include "chrome/browser/extensions/api/notifications/notifications_api.h" 17#include "chrome/browser/extensions/extension_service.h" 18#include "chrome/browser/infobars/confirm_infobar_delegate.h" 19#include "chrome/browser/infobars/infobar.h" 20#include "chrome/browser/infobars/infobar_service.h" 21#include "chrome/browser/notifications/desktop_notification_service_factory.h" 22#include "chrome/browser/notifications/notification.h" 23#include "chrome/browser/notifications/notification_object_proxy.h" 24#include "chrome/browser/notifications/notification_ui_manager.h" 25#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h" 26#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h" 27#include "chrome/browser/profiles/profile.h" 28#include "chrome/browser/ui/browser.h" 29#include "chrome/browser/ui/website_settings/permission_bubble_manager.h" 30#include "chrome/browser/ui/website_settings/permission_bubble_request.h" 31#include "chrome/common/content_settings.h" 32#include "chrome/common/content_settings_pattern.h" 33#include "chrome/common/pref_names.h" 34#include "chrome/common/url_constants.h" 35#include "components/user_prefs/pref_registry_syncable.h" 36#include "content/public/browser/browser_thread.h" 37#include "content/public/browser/notification_service.h" 38#include "content/public/browser/render_view_host.h" 39#include "content/public/browser/web_contents.h" 40#include "content/public/common/show_desktop_notification_params.h" 41#include "extensions/browser/event_router.h" 42#include "extensions/browser/extension_system.h" 43#include "extensions/browser/info_map.h" 44#include "extensions/common/constants.h" 45#include "extensions/common/extension.h" 46#include "extensions/common/extension_set.h" 47#include "grit/browser_resources.h" 48#include "grit/chromium_strings.h" 49#include "grit/generated_resources.h" 50#include "grit/theme_resources.h" 51#include "net/base/escape.h" 52#include "ui/base/l10n/l10n_util.h" 53#include "ui/base/resource/resource_bundle.h" 54#include "ui/base/webui/web_ui_util.h" 55#include "ui/message_center/message_center_util.h" 56#include "ui/message_center/notifier_settings.h" 57 58using content::BrowserThread; 59using content::RenderViewHost; 60using content::WebContents; 61using message_center::NotifierId; 62using blink::WebTextDirection; 63 64const char kChromeNowExtensionID[] = "pafkbggdmjlpgkdkcbjmhmfcdpncadgh"; 65 66// NotificationPermissionRequest --------------------------------------- 67 68class NotificationPermissionRequest : public PermissionBubbleRequest { 69 public: 70 NotificationPermissionRequest( 71 DesktopNotificationService* notification_service, 72 const GURL& origin, 73 base::string16 display_name, 74 int process_id, 75 int route_id, 76 int callback_context); 77 virtual ~NotificationPermissionRequest(); 78 79 // PermissionBubbleDelegate: 80 virtual base::string16 GetMessageText() const OVERRIDE; 81 virtual base::string16 GetMessageTextFragment() const OVERRIDE; 82 virtual base::string16 GetAlternateAcceptButtonText() const OVERRIDE; 83 virtual base::string16 GetAlternateDenyButtonText() const OVERRIDE; 84 virtual void PermissionGranted() OVERRIDE; 85 virtual void PermissionDenied() OVERRIDE; 86 virtual void Cancelled() OVERRIDE; 87 virtual void RequestFinished() OVERRIDE; 88 89 private: 90 // The notification service to be used. 91 DesktopNotificationService* notification_service_; 92 93 // The origin we are asking for permissions on. 94 GURL origin_; 95 96 // The display name for the origin to be displayed. Will be different from 97 // origin_ for extensions. 98 base::string16 display_name_; 99 100 // The callback information that tells us how to respond to javascript via 101 // the correct RenderView. 102 int process_id_; 103 int route_id_; 104 int callback_context_; 105 106 // Whether the user clicked one of the buttons. 107 bool action_taken_; 108 109 DISALLOW_COPY_AND_ASSIGN(NotificationPermissionRequest); 110}; 111 112NotificationPermissionRequest::NotificationPermissionRequest( 113 DesktopNotificationService* notification_service, 114 const GURL& origin, 115 base::string16 display_name, 116 int process_id, 117 int route_id, 118 int callback_context) 119 : notification_service_(notification_service), 120 origin_(origin), 121 display_name_(display_name), 122 process_id_(process_id), 123 route_id_(route_id), 124 callback_context_(callback_context), 125 action_taken_(false) {} 126 127NotificationPermissionRequest::~NotificationPermissionRequest() {} 128 129base::string16 NotificationPermissionRequest::GetMessageText() const { 130 return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS, 131 display_name_); 132} 133 134base::string16 135NotificationPermissionRequest::GetMessageTextFragment() const { 136 return l10n_util::GetStringUTF16(IDS_NOTIFICATION_PERMISSIONS_FRAGMENT); 137} 138 139base::string16 140NotificationPermissionRequest::GetAlternateAcceptButtonText() const { 141 return l10n_util::GetStringUTF16(IDS_NOTIFICATION_PERMISSION_YES); 142} 143 144base::string16 145NotificationPermissionRequest::GetAlternateDenyButtonText() const { 146 return l10n_util::GetStringUTF16(IDS_NOTIFICATION_PERMISSION_NO); 147} 148 149void NotificationPermissionRequest::PermissionGranted() { 150 action_taken_ = true; 151 UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1); 152 notification_service_->GrantPermission(origin_); 153} 154 155void NotificationPermissionRequest::PermissionDenied() { 156 action_taken_ = true; 157 UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1); 158 notification_service_->DenyPermission(origin_); 159} 160 161void NotificationPermissionRequest::Cancelled() { 162} 163 164void NotificationPermissionRequest::RequestFinished() { 165 if (!action_taken_) 166 UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Ignored", 1); 167 168 RenderViewHost* host = RenderViewHost::FromID(process_id_, route_id_); 169 if (host) 170 host->DesktopNotificationPermissionRequestDone(callback_context_); 171 172 delete this; 173} 174 175 176// NotificationPermissionInfoBarDelegate -------------------------------------- 177 178// The delegate for the infobar shown when an origin requests notification 179// permissions. 180class NotificationPermissionInfoBarDelegate : public ConfirmInfoBarDelegate { 181 public: 182 // Creates a notification permission infobar and delegate and adds the infobar 183 // to |infobar_service|. 184 static void Create(InfoBarService* infobar_service, 185 DesktopNotificationService* notification_service, 186 const GURL& origin, 187 const base::string16& display_name, 188 int process_id, 189 int route_id, 190 int callback_context); 191 192 private: 193 NotificationPermissionInfoBarDelegate( 194 DesktopNotificationService* notification_service, 195 const GURL& origin, 196 const base::string16& display_name, 197 int process_id, 198 int route_id, 199 int callback_context); 200 virtual ~NotificationPermissionInfoBarDelegate(); 201 202 // ConfirmInfoBarDelegate: 203 virtual int GetIconID() const OVERRIDE; 204 virtual Type GetInfoBarType() const OVERRIDE; 205 virtual base::string16 GetMessageText() const OVERRIDE; 206 virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE; 207 virtual bool Accept() OVERRIDE; 208 virtual bool Cancel() OVERRIDE; 209 210 // The origin we are asking for permissions on. 211 GURL origin_; 212 213 // The display name for the origin to be displayed. Will be different from 214 // origin_ for extensions. 215 base::string16 display_name_; 216 217 // The notification service to be used. 218 DesktopNotificationService* notification_service_; 219 220 // The callback information that tells us how to respond to javascript via 221 // the correct RenderView. 222 int process_id_; 223 int route_id_; 224 int callback_context_; 225 226 // Whether the user clicked one of the buttons. 227 bool action_taken_; 228 229 DISALLOW_COPY_AND_ASSIGN(NotificationPermissionInfoBarDelegate); 230}; 231 232// static 233void NotificationPermissionInfoBarDelegate::Create( 234 InfoBarService* infobar_service, 235 DesktopNotificationService* notification_service, 236 const GURL& origin, 237 const base::string16& display_name, 238 int process_id, 239 int route_id, 240 int callback_context) { 241 infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar( 242 scoped_ptr<ConfirmInfoBarDelegate>( 243 new NotificationPermissionInfoBarDelegate( 244 notification_service, origin, display_name, process_id, route_id, 245 callback_context)))); 246} 247 248NotificationPermissionInfoBarDelegate::NotificationPermissionInfoBarDelegate( 249 DesktopNotificationService* notification_service, 250 const GURL& origin, 251 const base::string16& display_name, 252 int process_id, 253 int route_id, 254 int callback_context) 255 : ConfirmInfoBarDelegate(), 256 origin_(origin), 257 display_name_(display_name), 258 notification_service_(notification_service), 259 process_id_(process_id), 260 route_id_(route_id), 261 callback_context_(callback_context), 262 action_taken_(false) { 263} 264 265NotificationPermissionInfoBarDelegate:: 266 ~NotificationPermissionInfoBarDelegate() { 267 if (!action_taken_) 268 UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Ignored", 1); 269 270 RenderViewHost* host = RenderViewHost::FromID(process_id_, route_id_); 271 if (host) 272 host->DesktopNotificationPermissionRequestDone(callback_context_); 273} 274 275int NotificationPermissionInfoBarDelegate::GetIconID() const { 276 return IDR_INFOBAR_DESKTOP_NOTIFICATIONS; 277} 278 279InfoBarDelegate::Type 280 NotificationPermissionInfoBarDelegate::GetInfoBarType() const { 281 return PAGE_ACTION_TYPE; 282} 283 284base::string16 NotificationPermissionInfoBarDelegate::GetMessageText() const { 285 return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS, 286 display_name_); 287} 288 289base::string16 NotificationPermissionInfoBarDelegate::GetButtonLabel( 290 InfoBarButton button) const { 291 return l10n_util::GetStringUTF16((button == BUTTON_OK) ? 292 IDS_NOTIFICATION_PERMISSION_YES : IDS_NOTIFICATION_PERMISSION_NO); 293} 294 295bool NotificationPermissionInfoBarDelegate::Accept() { 296 UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1); 297 notification_service_->GrantPermission(origin_); 298 action_taken_ = true; 299 return true; 300} 301 302bool NotificationPermissionInfoBarDelegate::Cancel() { 303 UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1); 304 notification_service_->DenyPermission(origin_); 305 action_taken_ = true; 306 return true; 307} 308 309 310// DesktopNotificationService ------------------------------------------------- 311 312// static 313void DesktopNotificationService::RegisterProfilePrefs( 314 user_prefs::PrefRegistrySyncable* registry) { 315 registry->RegisterListPref( 316 prefs::kMessageCenterDisabledExtensionIds, 317 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 318 registry->RegisterListPref( 319 prefs::kMessageCenterDisabledSystemComponentIds, 320 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 321 registry->RegisterListPref( 322 prefs::kMessageCenterEnabledSyncNotifierIds, 323 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 324 ExtensionWelcomeNotification::RegisterProfilePrefs(registry); 325} 326 327// static 328base::string16 DesktopNotificationService::CreateDataUrl( 329 const GURL& icon_url, 330 const base::string16& title, 331 const base::string16& body, 332 WebTextDirection dir) { 333 int resource; 334 std::vector<std::string> subst; 335 if (icon_url.is_valid()) { 336 resource = IDR_NOTIFICATION_ICON_HTML; 337 subst.push_back(icon_url.spec()); 338 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(title))); 339 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(body))); 340 // icon float position 341 subst.push_back(dir == blink::WebTextDirectionRightToLeft ? 342 "right" : "left"); 343 } else if (title.empty() || body.empty()) { 344 resource = IDR_NOTIFICATION_1LINE_HTML; 345 base::string16 line = title.empty() ? body : title; 346 // Strings are div names in the template file. 347 base::string16 line_name = 348 title.empty() ? base::ASCIIToUTF16("description") 349 : base::ASCIIToUTF16("title"); 350 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(line_name))); 351 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(line))); 352 } else { 353 resource = IDR_NOTIFICATION_2LINE_HTML; 354 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(title))); 355 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(body))); 356 } 357 // body text direction 358 subst.push_back(dir == blink::WebTextDirectionRightToLeft ? 359 "rtl" : "ltr"); 360 361 return CreateDataUrl(resource, subst); 362} 363 364// static 365base::string16 DesktopNotificationService::CreateDataUrl( 366 int resource, const std::vector<std::string>& subst) { 367 const base::StringPiece template_html( 368 ResourceBundle::GetSharedInstance().GetRawDataResource( 369 resource)); 370 371 if (template_html.empty()) { 372 NOTREACHED() << "unable to load template. ID: " << resource; 373 return base::string16(); 374 } 375 376 std::string data = ReplaceStringPlaceholders(template_html, subst, NULL); 377 return base::UTF8ToUTF16("data:text/html;charset=utf-8," + 378 net::EscapeQueryParamValue(data, false)); 379} 380 381// static 382std::string DesktopNotificationService::AddNotification( 383 const GURL& origin_url, 384 const base::string16& title, 385 const base::string16& message, 386 const GURL& icon_url, 387 const base::string16& replace_id, 388 NotificationDelegate* delegate, 389 Profile* profile) { 390 if (message_center::IsRichNotificationEnabled()) { 391 // For message center create a non-HTML notification with |icon_url|. 392 Notification notification(origin_url, icon_url, title, message, 393 blink::WebTextDirectionDefault, 394 base::string16(), replace_id, delegate); 395 g_browser_process->notification_ui_manager()->Add(notification, profile); 396 return notification.notification_id(); 397 } 398 399 // Generate a data URL embedding the icon URL, title, and message. 400 GURL content_url(CreateDataUrl( 401 icon_url, title, message, blink::WebTextDirectionDefault)); 402 Notification notification( 403 GURL(), content_url, base::string16(), replace_id, delegate); 404 g_browser_process->notification_ui_manager()->Add(notification, profile); 405 return notification.notification_id(); 406} 407 408// static 409std::string DesktopNotificationService::AddIconNotification( 410 const GURL& origin_url, 411 const base::string16& title, 412 const base::string16& message, 413 const gfx::Image& icon, 414 const base::string16& replace_id, 415 NotificationDelegate* delegate, 416 Profile* profile) { 417 if (message_center::IsRichNotificationEnabled()) { 418 // For message center create a non-HTML notification with |icon|. 419 Notification notification(origin_url, icon, title, message, 420 blink::WebTextDirectionDefault, 421 base::string16(), replace_id, delegate); 422 g_browser_process->notification_ui_manager()->Add(notification, profile); 423 return notification.notification_id(); 424 } 425 426 GURL icon_url; 427 if (!icon.IsEmpty()) 428 icon_url = GURL(webui::GetBitmapDataUrl(*icon.ToSkBitmap())); 429 return AddNotification( 430 origin_url, title, message, icon_url, replace_id, delegate, profile); 431} 432 433// static 434void DesktopNotificationService::RemoveNotification( 435 const std::string& notification_id) { 436 g_browser_process->notification_ui_manager()->CancelById(notification_id); 437} 438 439DesktopNotificationService::DesktopNotificationService( 440 Profile* profile, 441 NotificationUIManager* ui_manager) 442 : profile_(profile), 443 ui_manager_(ui_manager) { 444 OnStringListPrefChanged( 445 prefs::kMessageCenterDisabledExtensionIds, &disabled_extension_ids_); 446 OnStringListPrefChanged( 447 prefs::kMessageCenterDisabledSystemComponentIds, 448 &disabled_system_component_ids_); 449 OnStringListPrefChanged( 450 prefs::kMessageCenterEnabledSyncNotifierIds, &enabled_sync_notifier_ids_); 451 disabled_extension_id_pref_.Init( 452 prefs::kMessageCenterDisabledExtensionIds, 453 profile_->GetPrefs(), 454 base::Bind( 455 &DesktopNotificationService::OnStringListPrefChanged, 456 base::Unretained(this), 457 base::Unretained(prefs::kMessageCenterDisabledExtensionIds), 458 base::Unretained(&disabled_extension_ids_))); 459 disabled_system_component_id_pref_.Init( 460 prefs::kMessageCenterDisabledSystemComponentIds, 461 profile_->GetPrefs(), 462 base::Bind( 463 &DesktopNotificationService::OnStringListPrefChanged, 464 base::Unretained(this), 465 base::Unretained(prefs::kMessageCenterDisabledSystemComponentIds), 466 base::Unretained(&disabled_system_component_ids_))); 467 enabled_sync_notifier_id_pref_.Init( 468 prefs::kMessageCenterEnabledSyncNotifierIds, 469 profile_->GetPrefs(), 470 base::Bind( 471 &DesktopNotificationService::OnStringListPrefChanged, 472 base::Unretained(this), 473 base::Unretained(prefs::kMessageCenterEnabledSyncNotifierIds), 474 base::Unretained(&enabled_sync_notifier_ids_))); 475 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED, 476 content::Source<Profile>(profile_)); 477} 478 479DesktopNotificationService::~DesktopNotificationService() { 480} 481 482void DesktopNotificationService::GrantPermission(const GURL& origin) { 483 ContentSettingsPattern primary_pattern = 484 ContentSettingsPattern::FromURLNoWildcard(origin); 485 profile_->GetHostContentSettingsMap()->SetContentSetting( 486 primary_pattern, 487 ContentSettingsPattern::Wildcard(), 488 CONTENT_SETTINGS_TYPE_NOTIFICATIONS, 489 NO_RESOURCE_IDENTIFIER, 490 CONTENT_SETTING_ALLOW); 491} 492 493void DesktopNotificationService::DenyPermission(const GURL& origin) { 494 ContentSettingsPattern primary_pattern = 495 ContentSettingsPattern::FromURLNoWildcard(origin); 496 profile_->GetHostContentSettingsMap()->SetContentSetting( 497 primary_pattern, 498 ContentSettingsPattern::Wildcard(), 499 CONTENT_SETTINGS_TYPE_NOTIFICATIONS, 500 NO_RESOURCE_IDENTIFIER, 501 CONTENT_SETTING_BLOCK); 502} 503 504ContentSetting DesktopNotificationService::GetDefaultContentSetting( 505 std::string* provider_id) { 506 return profile_->GetHostContentSettingsMap()->GetDefaultContentSetting( 507 CONTENT_SETTINGS_TYPE_NOTIFICATIONS, provider_id); 508} 509 510void DesktopNotificationService::SetDefaultContentSetting( 511 ContentSetting setting) { 512 profile_->GetHostContentSettingsMap()->SetDefaultContentSetting( 513 CONTENT_SETTINGS_TYPE_NOTIFICATIONS, setting); 514} 515 516void DesktopNotificationService::ResetToDefaultContentSetting() { 517 profile_->GetHostContentSettingsMap()->SetDefaultContentSetting( 518 CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_DEFAULT); 519} 520 521void DesktopNotificationService::GetNotificationsSettings( 522 ContentSettingsForOneType* settings) { 523 profile_->GetHostContentSettingsMap()->GetSettingsForOneType( 524 CONTENT_SETTINGS_TYPE_NOTIFICATIONS, 525 NO_RESOURCE_IDENTIFIER, 526 settings); 527} 528 529void DesktopNotificationService::ClearSetting( 530 const ContentSettingsPattern& pattern) { 531 profile_->GetHostContentSettingsMap()->SetContentSetting( 532 pattern, 533 ContentSettingsPattern::Wildcard(), 534 CONTENT_SETTINGS_TYPE_NOTIFICATIONS, 535 NO_RESOURCE_IDENTIFIER, 536 CONTENT_SETTING_DEFAULT); 537} 538 539void DesktopNotificationService::ResetAllOrigins() { 540 profile_->GetHostContentSettingsMap()->ClearSettingsForOneType( 541 CONTENT_SETTINGS_TYPE_NOTIFICATIONS); 542} 543 544ContentSetting DesktopNotificationService::GetContentSetting( 545 const GURL& origin) { 546 return profile_->GetHostContentSettingsMap()->GetContentSetting( 547 origin, 548 origin, 549 CONTENT_SETTINGS_TYPE_NOTIFICATIONS, 550 NO_RESOURCE_IDENTIFIER); 551} 552 553void DesktopNotificationService::RequestPermission( 554 const GURL& origin, int process_id, int route_id, int callback_context, 555 WebContents* contents) { 556 // If |origin| hasn't been seen before and the default content setting for 557 // notifications is "ask", show an infobar. 558 // The cache can only answer queries on the IO thread once it's initialized, 559 // so don't ask the cache. 560 ContentSetting setting = GetContentSetting(origin); 561 if (setting == CONTENT_SETTING_ASK) { 562 if (PermissionBubbleManager::Enabled()) { 563 PermissionBubbleManager* bubble_manager = 564 PermissionBubbleManager::FromWebContents(contents); 565 bubble_manager->AddRequest(new NotificationPermissionRequest(this, 566 origin, DisplayNameForOriginInProcessId(origin, process_id), 567 process_id, route_id, callback_context)); 568 return; 569 } 570 571 // Show an info bar requesting permission. 572 InfoBarService* infobar_service = 573 InfoBarService::FromWebContents(contents); 574 // |infobar_service| may be NULL, e.g., if this request originated in a 575 // browser action popup, extension background page, or any HTML that runs 576 // outside of a tab. 577 if (infobar_service) { 578 NotificationPermissionInfoBarDelegate::Create( 579 infobar_service, this, 580 origin, DisplayNameForOriginInProcessId(origin, process_id), 581 process_id, route_id, callback_context); 582 return; 583 } 584 } 585 586 // Notify renderer immediately. 587 RenderViewHost* host = RenderViewHost::FromID(process_id, route_id); 588 if (host) 589 host->DesktopNotificationPermissionRequestDone(callback_context); 590} 591 592#if !defined(OS_WIN) 593void DesktopNotificationService::ShowNotification( 594 const Notification& notification) { 595 GetUIManager()->Add(notification, profile_); 596} 597 598bool DesktopNotificationService::CancelDesktopNotification( 599 int process_id, int route_id, int notification_id) { 600 scoped_refptr<NotificationObjectProxy> proxy( 601 new NotificationObjectProxy(process_id, route_id, notification_id, 602 false)); 603 return GetUIManager()->CancelById(proxy->id()); 604} 605#endif // OS_WIN 606 607bool DesktopNotificationService::ShowDesktopNotification( 608 const content::ShowDesktopNotificationHostMsgParams& params, 609 int process_id, int route_id, DesktopNotificationSource source) { 610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 611 const GURL& origin = params.origin; 612 NotificationObjectProxy* proxy = 613 new NotificationObjectProxy(process_id, route_id, 614 params.notification_id, 615 source == WorkerNotification); 616 617 base::string16 display_source = 618 DisplayNameForOriginInProcessId(origin, process_id); 619 Notification notification(origin, params.icon_url, params.title, 620 params.body, params.direction, display_source, params.replace_id, 621 proxy); 622 623 // The webkit notification doesn't timeout. 624 notification.set_never_timeout(true); 625 626 ShowNotification(notification); 627 return true; 628} 629 630base::string16 DesktopNotificationService::DisplayNameForOriginInProcessId( 631 const GURL& origin, int process_id) { 632 // If the source is an extension, lookup the display name. 633 // Message center prefers to use extension name if the notification 634 // is allowed by an extension. 635 if (NotificationUIManager::DelegatesToMessageCenter() || 636 origin.SchemeIs(extensions::kExtensionScheme)) { 637 extensions::InfoMap* extension_info_map = 638 extensions::ExtensionSystem::Get(profile_)->info_map(); 639 if (extension_info_map) { 640 extensions::ExtensionSet extensions; 641 extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin( 642 origin, process_id, extensions::APIPermission::kNotification, 643 &extensions); 644 for (extensions::ExtensionSet::const_iterator iter = extensions.begin(); 645 iter != extensions.end(); ++iter) { 646 NotifierId notifier_id(NotifierId::APPLICATION, (*iter)->id()); 647 if (IsNotifierEnabled(notifier_id)) 648 return base::UTF8ToUTF16((*iter)->name()); 649 } 650 } 651 } 652 return base::UTF8ToUTF16(origin.host()); 653} 654 655void DesktopNotificationService::NotifySettingsChange() { 656 content::NotificationService::current()->Notify( 657 chrome::NOTIFICATION_DESKTOP_NOTIFICATION_SETTINGS_CHANGED, 658 content::Source<DesktopNotificationService>(this), 659 content::NotificationService::NoDetails()); 660} 661 662NotificationUIManager* DesktopNotificationService::GetUIManager() { 663 // We defer setting ui_manager_ to the global singleton until we need it 664 // in order to avoid UI dependent construction during startup. 665 if (!ui_manager_) 666 ui_manager_ = g_browser_process->notification_ui_manager(); 667 return ui_manager_; 668} 669 670bool DesktopNotificationService::IsNotifierEnabled( 671 const NotifierId& notifier_id) { 672 switch (notifier_id.type) { 673 case NotifierId::APPLICATION: 674 return disabled_extension_ids_.find(notifier_id.id) == 675 disabled_extension_ids_.end(); 676 case NotifierId::WEB_PAGE: 677 return GetContentSetting(notifier_id.url) == CONTENT_SETTING_ALLOW; 678 case NotifierId::SYSTEM_COMPONENT: 679#if defined(OS_CHROMEOS) 680 return disabled_system_component_ids_.find(notifier_id.id) == 681 disabled_system_component_ids_.end(); 682#else 683 // We do not disable system component notifications. 684 return true; 685#endif 686 case NotifierId::SYNCED_NOTIFICATION_SERVICE: 687 return enabled_sync_notifier_ids_.find(notifier_id.id) != 688 enabled_sync_notifier_ids_.end(); 689 } 690 691 NOTREACHED(); 692 return false; 693} 694 695void DesktopNotificationService::SetNotifierEnabled( 696 const NotifierId& notifier_id, 697 bool enabled) { 698 DCHECK_NE(NotifierId::WEB_PAGE, notifier_id.type); 699 700 bool add_new_item = false; 701 const char* pref_name = NULL; 702 scoped_ptr<base::StringValue> id; 703 switch (notifier_id.type) { 704 case NotifierId::APPLICATION: 705 pref_name = prefs::kMessageCenterDisabledExtensionIds; 706 add_new_item = !enabled; 707 id.reset(new base::StringValue(notifier_id.id)); 708 FirePermissionLevelChangedEvent(notifier_id, enabled); 709 break; 710 case NotifierId::SYSTEM_COMPONENT: 711#if defined(OS_CHROMEOS) 712 pref_name = prefs::kMessageCenterDisabledSystemComponentIds; 713 add_new_item = !enabled; 714 id.reset(new base::StringValue(notifier_id.id)); 715#else 716 return; 717#endif 718 break; 719 case NotifierId::SYNCED_NOTIFICATION_SERVICE: 720 pref_name = prefs::kMessageCenterEnabledSyncNotifierIds; 721 // Adding a new item if |enabled| == true, since synced notification 722 // services are opt-in. 723 add_new_item = enabled; 724 id.reset(new base::StringValue(notifier_id.id)); 725 break; 726 default: 727 NOTREACHED(); 728 } 729 DCHECK(pref_name != NULL); 730 731 ListPrefUpdate update(profile_->GetPrefs(), pref_name); 732 base::ListValue* const list = update.Get(); 733 if (add_new_item) { 734 // AppendIfNotPresent will delete |adding_value| when the same value 735 // already exists. 736 list->AppendIfNotPresent(id.release()); 737 } else { 738 list->Remove(*id, NULL); 739 } 740} 741 742void DesktopNotificationService::ShowWelcomeNotificationIfNecessary( 743 const Notification& notification) { 744 if (!chrome_now_welcome_notification_ && 745 message_center::IsRichNotificationEnabled()) { 746 chrome_now_welcome_notification_ = 747 ExtensionWelcomeNotification::Create(kChromeNowExtensionID, profile_); 748 } 749 750 if (chrome_now_welcome_notification_) { 751 chrome_now_welcome_notification_->ShowWelcomeNotificationIfNecessary( 752 notification); 753 } 754} 755 756void DesktopNotificationService::OnStringListPrefChanged( 757 const char* pref_name, std::set<std::string>* ids_field) { 758 ids_field->clear(); 759 // Separate GetPrefs()->GetList() to analyze the crash. See crbug.com/322320 760 const PrefService* pref_service = profile_->GetPrefs(); 761 CHECK(pref_service); 762 const base::ListValue* pref_list = pref_service->GetList(pref_name); 763 for (size_t i = 0; i < pref_list->GetSize(); ++i) { 764 std::string element; 765 if (pref_list->GetString(i, &element) && !element.empty()) 766 ids_field->insert(element); 767 else 768 LOG(WARNING) << i << "-th element is not a string for " << pref_name; 769 } 770} 771 772void DesktopNotificationService::Observe( 773 int type, 774 const content::NotificationSource& source, 775 const content::NotificationDetails& details) { 776 DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_UNINSTALLED, type); 777 778 extensions::Extension* extension = 779 content::Details<extensions::Extension>(details).ptr(); 780 NotifierId notifier_id(NotifierId::APPLICATION, extension->id()); 781 if (IsNotifierEnabled(notifier_id)) 782 return; 783 784 // The settings for ephemeral apps will be persisted across cache evictions. 785 if (extension->is_ephemeral()) 786 return; 787 788 SetNotifierEnabled(notifier_id, true); 789} 790 791void DesktopNotificationService::FirePermissionLevelChangedEvent( 792 const NotifierId& notifier_id, bool enabled) { 793 DCHECK_EQ(NotifierId::APPLICATION, notifier_id.type); 794 extensions::api::notifications::PermissionLevel permission = 795 enabled ? extensions::api::notifications::PERMISSION_LEVEL_GRANTED 796 : extensions::api::notifications::PERMISSION_LEVEL_DENIED; 797 scoped_ptr<base::ListValue> args(new base::ListValue()); 798 args->Append(new base::StringValue( 799 extensions::api::notifications::ToString(permission))); 800 scoped_ptr<extensions::Event> event(new extensions::Event( 801 extensions::api::notifications::OnPermissionLevelChanged::kEventName, 802 args.Pass())); 803 extensions::ExtensionSystem::Get(profile_)->event_router()-> 804 DispatchEventToExtension(notifier_id.id, event.Pass()); 805 806 // Tell the IO thread that this extension's permission for notifications 807 // has changed. 808 extensions::InfoMap* extension_info_map = 809 extensions::ExtensionSystem::Get(profile_)->info_map(); 810 BrowserThread::PostTask( 811 BrowserThread::IO, FROM_HERE, 812 base::Bind(&extensions::InfoMap::SetNotificationsDisabled, 813 extension_info_map, notifier_id.id, !enabled)); 814 815} 816