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