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