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/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