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