Home | History | Annotate | Download | only in notifications
      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