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