Home | History | Annotate | Download | only in notifications
      1 // Copyright (c) 2011 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/threading/thread.h"
      9 #include "base/utf_string_conversions.h"
     10 #include "chrome/browser/content_settings/content_settings_provider.h"
     11 #include "chrome/browser/extensions/extension_service.h"
     12 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
     13 #include "chrome/browser/notifications/notification.h"
     14 #include "chrome/browser/notifications/notification_object_proxy.h"
     15 #include "chrome/browser/notifications/notification_ui_manager.h"
     16 #include "chrome/browser/notifications/notifications_prefs_cache.h"
     17 #include "chrome/browser/prefs/pref_service.h"
     18 #include "chrome/browser/prefs/scoped_user_pref_update.h"
     19 #include "chrome/browser/profiles/profile.h"
     20 #include "chrome/browser/tab_contents/confirm_infobar_delegate.h"
     21 #include "chrome/browser/ui/browser_list.h"
     22 #include "chrome/common/pref_names.h"
     23 #include "chrome/common/url_constants.h"
     24 #include "content/browser/browser_child_process_host.h"
     25 #include "content/browser/browser_thread.h"
     26 #include "content/browser/renderer_host/render_process_host.h"
     27 #include "content/browser/renderer_host/render_view_host.h"
     28 #include "content/browser/site_instance.h"
     29 #include "content/browser/tab_contents/tab_contents.h"
     30 #include "content/browser/worker_host/worker_process_host.h"
     31 #include "content/common/desktop_notification_messages.h"
     32 #include "content/common/notification_service.h"
     33 #include "content/common/notification_type.h"
     34 #include "grit/browser_resources.h"
     35 #include "grit/chromium_strings.h"
     36 #include "grit/generated_resources.h"
     37 #include "grit/theme_resources.h"
     38 #include "net/base/escape.h"
     39 #include "third_party/WebKit/Source/WebKit/chromium/public/WebNotificationPresenter.h"
     40 #include "ui/base/l10n/l10n_util.h"
     41 #include "ui/base/resource/resource_bundle.h"
     42 
     43 using WebKit::WebNotificationPresenter;
     44 using WebKit::WebTextDirection;
     45 
     46 const ContentSetting kDefaultSetting = CONTENT_SETTING_ASK;
     47 
     48 namespace {
     49 
     50 typedef content_settings::ProviderInterface::Rules Rules;
     51 
     52 void GetOriginsWithSettingFromContentSettingsRules(
     53     const Rules& content_setting_rules,
     54     ContentSetting setting,
     55     std::vector<GURL>* origins) {
     56   origins->clear();
     57 
     58   for (Rules::const_iterator rule = content_setting_rules.begin();
     59        rule != content_setting_rules.end();
     60        ++rule) {
     61     if (setting == rule->content_setting) {
     62       std::string url_str = rule->requesting_url_pattern.AsString();
     63       if (!rule->requesting_url_pattern.IsValid()) {
     64         // TODO(markusheintz): This will be removed in one of the next
     65         // refactoring steps as this entire function will disapear.
     66         LOG(DFATAL) << "Ignoring invalid content settings pattern: "
     67                     << url_str;
     68       } else if (url_str.find(ContentSettingsPattern::kDomainWildcard) == 0) {
     69         // TODO(markusheintz): This must be changed once the UI code is
     70         // refactored and content_settings patterns are fully supported for
     71         // notifications settings.
     72         LOG(DFATAL) << "Ignoring unsupported content settings pattern: "
     73                     << url_str << ". Content settings patterns other than "
     74                     << "hostnames (e.g. wildcard patterns) are not supported "
     75                     << "for notification content settings yet.";
     76       } else {
     77         origins->push_back(
     78             content_settings::NotificationProvider::ToGURL(
     79                 rule->requesting_url_pattern));
     80       }
     81     }
     82   }
     83 }
     84 
     85 }  // namespace
     86 
     87 // NotificationPermissionInfoBarDelegate --------------------------------------
     88 
     89 // The delegate for the infobar shown when an origin requests notification
     90 // permissions.
     91 class NotificationPermissionInfoBarDelegate : public ConfirmInfoBarDelegate {
     92  public:
     93   NotificationPermissionInfoBarDelegate(TabContents* contents,
     94                                         const GURL& origin,
     95                                         const string16& display_name,
     96                                         int process_id,
     97                                         int route_id,
     98                                         int callback_context);
     99 
    100  private:
    101   virtual ~NotificationPermissionInfoBarDelegate();
    102 
    103   // ConfirmInfoBarDelegate:
    104   virtual void InfoBarClosed();
    105   virtual SkBitmap* GetIcon() const;
    106   virtual Type GetInfoBarType() const;
    107   virtual string16 GetMessageText() const;
    108   virtual string16 GetButtonLabel(InfoBarButton button) const;
    109   virtual bool Accept();
    110   virtual bool Cancel();
    111 
    112   // The origin we are asking for permissions on.
    113   GURL origin_;
    114 
    115   // The display name for the origin to be displayed.  Will be different from
    116   // origin_ for extensions.
    117   string16 display_name_;
    118 
    119   // The Profile that we restore sessions from.
    120   Profile* profile_;
    121 
    122   // The callback information that tells us how to respond to javascript via
    123   // the correct RenderView.
    124   int process_id_;
    125   int route_id_;
    126   int callback_context_;
    127 
    128   // Whether the user clicked one of the buttons.
    129   bool action_taken_;
    130 
    131   DISALLOW_COPY_AND_ASSIGN(NotificationPermissionInfoBarDelegate);
    132 };
    133 
    134 NotificationPermissionInfoBarDelegate::NotificationPermissionInfoBarDelegate(
    135     TabContents* contents,
    136     const GURL& origin,
    137     const string16& display_name,
    138     int process_id,
    139     int route_id,
    140     int callback_context)
    141     : ConfirmInfoBarDelegate(contents),
    142       origin_(origin),
    143       display_name_(display_name),
    144       profile_(contents->profile()),
    145       process_id_(process_id),
    146       route_id_(route_id),
    147       callback_context_(callback_context),
    148       action_taken_(false) {
    149 }
    150 
    151 NotificationPermissionInfoBarDelegate::
    152     ~NotificationPermissionInfoBarDelegate() {
    153 }
    154 
    155 void NotificationPermissionInfoBarDelegate::InfoBarClosed() {
    156   if (!action_taken_)
    157     UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Ignored", 1);
    158 
    159   RenderViewHost* host = RenderViewHost::FromID(process_id_, route_id_);
    160   if (host) {
    161     host->Send(new DesktopNotificationMsg_PermissionRequestDone(
    162         route_id_, callback_context_));
    163   }
    164 
    165   delete this;
    166 }
    167 
    168 SkBitmap* NotificationPermissionInfoBarDelegate::GetIcon() const {
    169   return ResourceBundle::GetSharedInstance().GetBitmapNamed(
    170      IDR_PRODUCT_ICON_32);
    171 }
    172 
    173 InfoBarDelegate::Type
    174     NotificationPermissionInfoBarDelegate::GetInfoBarType() const {
    175   return PAGE_ACTION_TYPE;
    176 }
    177 
    178 string16 NotificationPermissionInfoBarDelegate::GetMessageText() const {
    179   return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS,
    180                                     display_name_);
    181 }
    182 
    183 string16 NotificationPermissionInfoBarDelegate::GetButtonLabel(
    184     InfoBarButton button) const {
    185   return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
    186       IDS_NOTIFICATION_PERMISSION_YES : IDS_NOTIFICATION_PERMISSION_NO);
    187 }
    188 
    189 bool NotificationPermissionInfoBarDelegate::Accept() {
    190   UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1);
    191   DesktopNotificationServiceFactory::GetForProfile(profile_)->
    192       GrantPermission(origin_);
    193   action_taken_ = true;
    194   return true;
    195 }
    196 
    197 bool NotificationPermissionInfoBarDelegate::Cancel() {
    198   UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1);
    199   DesktopNotificationServiceFactory::GetForProfile(profile_)->
    200       DenyPermission(origin_);
    201   action_taken_ = true;
    202   return true;
    203 }
    204 
    205 
    206 // DesktopNotificationService -------------------------------------------------
    207 
    208 // static
    209 string16 DesktopNotificationService::CreateDataUrl(
    210     const GURL& icon_url, const string16& title, const string16& body,
    211     WebTextDirection dir) {
    212   int resource;
    213   std::vector<std::string> subst;
    214   if (icon_url.is_valid()) {
    215     resource = IDR_NOTIFICATION_ICON_HTML;
    216     subst.push_back(icon_url.spec());
    217     subst.push_back(EscapeForHTML(UTF16ToUTF8(title)));
    218     subst.push_back(EscapeForHTML(UTF16ToUTF8(body)));
    219     // icon float position
    220     subst.push_back(dir == WebKit::WebTextDirectionRightToLeft ?
    221                     "right" : "left");
    222   } else if (title.empty() || body.empty()) {
    223     resource = IDR_NOTIFICATION_1LINE_HTML;
    224     string16 line = title.empty() ? body : title;
    225     // Strings are div names in the template file.
    226     string16 line_name = title.empty() ? ASCIIToUTF16("description")
    227                                        : ASCIIToUTF16("title");
    228     subst.push_back(EscapeForHTML(UTF16ToUTF8(line_name)));
    229     subst.push_back(EscapeForHTML(UTF16ToUTF8(line)));
    230   } else {
    231     resource = IDR_NOTIFICATION_2LINE_HTML;
    232     subst.push_back(EscapeForHTML(UTF16ToUTF8(title)));
    233     subst.push_back(EscapeForHTML(UTF16ToUTF8(body)));
    234   }
    235   // body text direction
    236   subst.push_back(dir == WebKit::WebTextDirectionRightToLeft ?
    237                   "rtl" : "ltr");
    238 
    239   return CreateDataUrl(resource, subst);
    240 }
    241 
    242 // static
    243 string16 DesktopNotificationService::CreateDataUrl(
    244     int resource, const std::vector<std::string>& subst) {
    245   const base::StringPiece template_html(
    246       ResourceBundle::GetSharedInstance().GetRawDataResource(
    247           resource));
    248 
    249   if (template_html.empty()) {
    250     NOTREACHED() << "unable to load template. ID: " << resource;
    251     return string16();
    252   }
    253 
    254   std::string data = ReplaceStringPlaceholders(template_html, subst, NULL);
    255   return UTF8ToUTF16("data:text/html;charset=utf-8," +
    256                       EscapeQueryParamValue(data, false));
    257 }
    258 
    259 DesktopNotificationService::DesktopNotificationService(Profile* profile,
    260     NotificationUIManager* ui_manager)
    261     : profile_(profile),
    262       ui_manager_(ui_manager) {
    263   prefs_registrar_.Init(profile_->GetPrefs());
    264   InitPrefs();
    265   StartObserving();
    266 }
    267 
    268 DesktopNotificationService::~DesktopNotificationService() {
    269   StopObserving();
    270 }
    271 
    272 void DesktopNotificationService::RegisterUserPrefs(PrefService* user_prefs) {
    273   content_settings::NotificationProvider::RegisterUserPrefs(user_prefs);
    274 }
    275 
    276 // Initialize the cache with the allowed and denied origins, or
    277 // create the preferences if they don't exist yet.
    278 void DesktopNotificationService::InitPrefs() {
    279   provider_.reset(new content_settings::NotificationProvider(profile_));
    280 
    281   std::vector<GURL> allowed_origins;
    282   std::vector<GURL> denied_origins;
    283   ContentSetting default_content_setting = CONTENT_SETTING_DEFAULT;
    284 
    285   if (!profile_->IsOffTheRecord()) {
    286     default_content_setting =
    287         profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
    288             CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
    289     allowed_origins = GetAllowedOrigins();
    290     denied_origins = GetBlockedOrigins();
    291   }
    292 
    293   prefs_cache_ = new NotificationsPrefsCache();
    294   prefs_cache_->SetCacheDefaultContentSetting(default_content_setting);
    295   prefs_cache_->SetCacheAllowedOrigins(allowed_origins);
    296   prefs_cache_->SetCacheDeniedOrigins(denied_origins);
    297   prefs_cache_->set_is_initialized(true);
    298 }
    299 
    300 void DesktopNotificationService::StartObserving() {
    301   if (!profile_->IsOffTheRecord()) {
    302     prefs_registrar_.Add(prefs::kDesktopNotificationAllowedOrigins, this);
    303     prefs_registrar_.Add(prefs::kDesktopNotificationDeniedOrigins, this);
    304     notification_registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
    305                                 NotificationService::AllSources());
    306     notification_registrar_.Add(
    307         this,
    308         NotificationType::CONTENT_SETTINGS_CHANGED,
    309         // TODO(markusheintz): Remember to change to HostContentSettingsMap.
    310         NotificationService::AllSources());
    311   }
    312   notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
    313                               Source<Profile>(profile_));
    314 }
    315 
    316 void DesktopNotificationService::StopObserving() {
    317   if (!profile_->IsOffTheRecord()) {
    318     prefs_registrar_.RemoveAll();
    319   }
    320   notification_registrar_.RemoveAll();
    321 }
    322 
    323 void DesktopNotificationService::GrantPermission(const GURL& origin) {
    324   ContentSettingsPattern pattern =
    325       content_settings::NotificationProvider::ToContentSettingsPattern(origin);
    326   provider_->SetContentSetting(
    327       pattern,
    328       pattern,
    329       CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
    330       NO_RESOURCE_IDENTIFIER,
    331       CONTENT_SETTING_ALLOW);
    332 
    333   // Schedule a cache update on the IO thread.
    334   BrowserThread::PostTask(
    335       BrowserThread::IO, FROM_HERE,
    336       NewRunnableMethod(
    337           prefs_cache_.get(),
    338           &NotificationsPrefsCache::CacheAllowedOrigin,
    339           origin));
    340 }
    341 
    342 void DesktopNotificationService::DenyPermission(const GURL& origin) {
    343   // Update content settings
    344   ContentSettingsPattern pattern =
    345       content_settings::NotificationProvider::ToContentSettingsPattern(origin);
    346   provider_->SetContentSetting(
    347       pattern,
    348       pattern,
    349       CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
    350       NO_RESOURCE_IDENTIFIER,
    351       CONTENT_SETTING_BLOCK);
    352 
    353   // Schedule a cache update on the IO thread.
    354   BrowserThread::PostTask(
    355       BrowserThread::IO, FROM_HERE,
    356       NewRunnableMethod(
    357           prefs_cache_.get(),
    358           &NotificationsPrefsCache::CacheDeniedOrigin,
    359           origin));
    360 }
    361 
    362 void DesktopNotificationService::Observe(NotificationType type,
    363                                          const NotificationSource& source,
    364                                          const NotificationDetails& details) {
    365   if (NotificationType::PREF_CHANGED == type) {
    366     const std::string& name = *Details<std::string>(details).ptr();
    367     OnPrefsChanged(name);
    368   } else if (NotificationType::CONTENT_SETTINGS_CHANGED == type) {
    369     // TODO(markusheintz): Check if content settings type default was changed;
    370     const ContentSetting default_content_setting =
    371         profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
    372             CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
    373     // Schedule a cache update on the IO thread.
    374     BrowserThread::PostTask(
    375         BrowserThread::IO, FROM_HERE,
    376         NewRunnableMethod(
    377             prefs_cache_.get(),
    378             &NotificationsPrefsCache::SetCacheDefaultContentSetting,
    379             default_content_setting));
    380   } else if (NotificationType::EXTENSION_UNLOADED == type) {
    381     // Remove all notifications currently shown or queued by the extension
    382     // which was unloaded.
    383     const Extension* extension =
    384         Details<UnloadedExtensionInfo>(details)->extension;
    385     if (extension)
    386       ui_manager_->CancelAllBySourceOrigin(extension->url());
    387   } else if (NotificationType::PROFILE_DESTROYED == type) {
    388     StopObserving();
    389   }
    390 }
    391 
    392 void DesktopNotificationService::OnPrefsChanged(const std::string& pref_name) {
    393   if (pref_name == prefs::kDesktopNotificationAllowedOrigins) {
    394     // Schedule a cache update on the IO thread.
    395     std::vector<GURL> allowed_origins(GetAllowedOrigins());
    396     BrowserThread::PostTask(
    397         BrowserThread::IO, FROM_HERE,
    398         NewRunnableMethod(
    399             prefs_cache_.get(),
    400             &NotificationsPrefsCache::SetCacheAllowedOrigins,
    401             allowed_origins));
    402   } else if (pref_name == prefs::kDesktopNotificationDeniedOrigins) {
    403     // Schedule a cache update on the IO thread.
    404     std::vector<GURL> denied_origins(GetBlockedOrigins());
    405     BrowserThread::PostTask(
    406         BrowserThread::IO, FROM_HERE,
    407         NewRunnableMethod(
    408             prefs_cache_.get(),
    409             &NotificationsPrefsCache::SetCacheDeniedOrigins,
    410             denied_origins));
    411   } else {
    412     NOTREACHED();
    413   }
    414 }
    415 
    416 ContentSetting DesktopNotificationService::GetDefaultContentSetting() {
    417   return profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
    418       CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
    419 }
    420 
    421 void DesktopNotificationService::SetDefaultContentSetting(
    422     ContentSetting setting) {
    423   profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
    424       CONTENT_SETTINGS_TYPE_NOTIFICATIONS, setting);
    425 }
    426 
    427 bool DesktopNotificationService::IsDefaultContentSettingManaged() const {
    428   return profile_->GetHostContentSettingsMap()->IsDefaultContentSettingManaged(
    429       CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
    430 }
    431 
    432 void DesktopNotificationService::ResetToDefaultContentSetting() {
    433   profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
    434       CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_DEFAULT);
    435 }
    436 
    437 std::vector<GURL> DesktopNotificationService::GetAllowedOrigins() {
    438   content_settings::ProviderInterface::Rules content_setting_rules;
    439   provider_->GetAllContentSettingsRules(
    440       CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
    441       NO_RESOURCE_IDENTIFIER,
    442       &content_setting_rules);
    443   std::vector<GURL> allowed_origins;
    444 
    445   GetOriginsWithSettingFromContentSettingsRules(
    446       content_setting_rules, CONTENT_SETTING_ALLOW, &allowed_origins);
    447 
    448   return allowed_origins;
    449 }
    450 
    451 std::vector<GURL> DesktopNotificationService::GetBlockedOrigins() {
    452   content_settings::ProviderInterface::Rules content_settings_rules;
    453   provider_->GetAllContentSettingsRules(
    454       CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
    455       NO_RESOURCE_IDENTIFIER,
    456       &content_settings_rules);
    457   std::vector<GURL> denied_origins;
    458 
    459   GetOriginsWithSettingFromContentSettingsRules(
    460       content_settings_rules, CONTENT_SETTING_BLOCK, &denied_origins);
    461 
    462   return denied_origins;
    463 }
    464 
    465 void DesktopNotificationService::ResetAllowedOrigin(const GURL& origin) {
    466   ContentSettingsPattern pattern =
    467       ContentSettingsPattern::FromURLNoWildcard(origin);
    468   provider_->SetContentSetting(
    469       pattern,
    470       pattern,
    471       CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
    472       NO_RESOURCE_IDENTIFIER,
    473       CONTENT_SETTING_DEFAULT);
    474 }
    475 
    476 void DesktopNotificationService::ResetBlockedOrigin(const GURL& origin) {
    477   ContentSettingsPattern pattern =
    478       ContentSettingsPattern::FromURLNoWildcard(origin);
    479   provider_->SetContentSetting(
    480       pattern,
    481       pattern,
    482       CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
    483       NO_RESOURCE_IDENTIFIER,
    484       CONTENT_SETTING_DEFAULT);
    485 }
    486 
    487 void DesktopNotificationService::ResetAllOrigins() {
    488   provider_->ClearAllContentSettingsRules(CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
    489 }
    490 
    491 ContentSetting DesktopNotificationService::GetContentSetting(
    492     const GURL& origin) {
    493   ContentSetting provided_setting = provider_->GetContentSetting(
    494       origin,
    495       origin,
    496       CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
    497       NO_RESOURCE_IDENTIFIER);
    498   if (CONTENT_SETTING_DEFAULT == provided_setting)
    499     return GetDefaultContentSetting();
    500   return provided_setting;
    501 }
    502 
    503 void DesktopNotificationService::RequestPermission(
    504     const GURL& origin, int process_id, int route_id, int callback_context,
    505     TabContents* tab) {
    506   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    507   if (!tab) {
    508     Browser* browser = BrowserList::GetLastActive();
    509     if (browser)
    510       tab = browser->GetSelectedTabContents();
    511   }
    512 
    513   if (!tab)
    514     return;
    515 
    516   // If |origin| hasn't been seen before and the default content setting for
    517   // notifications is "ask", show an infobar.
    518   // The cache can only answer queries on the IO thread once it's initialized,
    519   // so don't ask the cache.
    520   ContentSetting setting = GetContentSetting(origin);
    521   if (setting == CONTENT_SETTING_ASK) {
    522     // Show an info bar requesting permission.
    523     tab->AddInfoBar(new NotificationPermissionInfoBarDelegate(
    524                         tab, origin, DisplayNameForOrigin(origin), process_id,
    525                         route_id, callback_context));
    526   } else {
    527     // Notify renderer immediately.
    528     RenderViewHost* host = RenderViewHost::FromID(process_id, route_id);
    529     if (host) {
    530       host->Send(new DesktopNotificationMsg_PermissionRequestDone(
    531           route_id, callback_context));
    532     }
    533   }
    534 }
    535 
    536 void DesktopNotificationService::ShowNotification(
    537     const Notification& notification) {
    538   ui_manager_->Add(notification, profile_);
    539 }
    540 
    541 bool DesktopNotificationService::CancelDesktopNotification(
    542     int process_id, int route_id, int notification_id) {
    543   scoped_refptr<NotificationObjectProxy> proxy(
    544       new NotificationObjectProxy(process_id, route_id, notification_id,
    545                                   false));
    546   return ui_manager_->CancelById(proxy->id());
    547 }
    548 
    549 
    550 bool DesktopNotificationService::ShowDesktopNotification(
    551     const DesktopNotificationHostMsg_Show_Params& params,
    552     int process_id, int route_id, DesktopNotificationSource source) {
    553   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    554   const GURL& origin = params.origin;
    555   NotificationObjectProxy* proxy =
    556       new NotificationObjectProxy(process_id, route_id,
    557                                   params.notification_id,
    558                                   source == WorkerNotification);
    559   GURL contents;
    560   if (params.is_html) {
    561     contents = params.contents_url;
    562   } else {
    563     // "upconvert" the string parameters to a data: URL.
    564     contents = GURL(
    565         CreateDataUrl(params.icon_url, params.title, params.body,
    566                       params.direction));
    567   }
    568   Notification notification(
    569       origin, contents, DisplayNameForOrigin(origin),
    570       params.replace_id, proxy);
    571   ShowNotification(notification);
    572   return true;
    573 }
    574 
    575 string16 DesktopNotificationService::DisplayNameForOrigin(
    576     const GURL& origin) {
    577   // If the source is an extension, lookup the display name.
    578   if (origin.SchemeIs(chrome::kExtensionScheme)) {
    579     ExtensionService* ext_service = profile_->GetExtensionService();
    580     if (ext_service) {
    581       const Extension* extension = ext_service->GetExtensionByURL(origin);
    582       if (extension)
    583         return UTF8ToUTF16(extension->name());
    584     }
    585   }
    586   return UTF8ToUTF16(origin.host());
    587 }
    588 
    589 void DesktopNotificationService::NotifySettingsChange() {
    590   NotificationService::current()->Notify(
    591       NotificationType::DESKTOP_NOTIFICATION_SETTINGS_CHANGED,
    592       Source<DesktopNotificationService>(this),
    593       NotificationService::NoDetails());
    594 }
    595