desktop_notification_service.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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/browser_child_process_host.h"
11#include "chrome/browser/browser_thread.h"
12#include "chrome/browser/extensions/extension_service.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_pref_update.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/renderer_host/render_process_host.h"
21#include "chrome/browser/renderer_host/render_view_host.h"
22#include "chrome/browser/renderer_host/site_instance.h"
23#include "chrome/browser/tab_contents/infobar_delegate.h"
24#include "chrome/browser/tab_contents/tab_contents.h"
25#include "chrome/browser/worker_host/worker_process_host.h"
26#include "chrome/common/notification_service.h"
27#include "chrome/common/notification_type.h"
28#include "chrome/common/pref_names.h"
29#include "chrome/common/render_messages.h"
30#include "chrome/common/render_messages_params.h"
31#include "chrome/common/url_constants.h"
32#include "grit/browser_resources.h"
33#include "grit/chromium_strings.h"
34#include "grit/generated_resources.h"
35#include "grit/theme_resources.h"
36#include "net/base/escape.h"
37#include "third_party/WebKit/Source/WebKit/chromium/public/WebNotificationPresenter.h"
38#include "ui/base/l10n/l10n_util.h"
39#include "ui/base/resource/resource_bundle.h"
40
41using WebKit::WebNotificationPresenter;
42using WebKit::WebTextDirection;
43
44const ContentSetting kDefaultSetting = CONTENT_SETTING_ASK;
45
46// NotificationPermissionCallbackTask -----------------------------------------
47
48// A task object which calls the renderer to inform the web page that the
49// permission request has completed.
50class NotificationPermissionCallbackTask : public Task {
51 public:
52  NotificationPermissionCallbackTask(int process_id,
53                                     int route_id,
54                                     int request_id);
55  virtual ~NotificationPermissionCallbackTask();
56
57 private:
58  virtual void Run();
59
60  int process_id_;
61  int route_id_;
62  int request_id_;
63};
64
65NotificationPermissionCallbackTask::NotificationPermissionCallbackTask(
66    int process_id,
67    int route_id,
68    int request_id)
69    : process_id_(process_id),
70      route_id_(route_id),
71      request_id_(request_id) {
72}
73
74NotificationPermissionCallbackTask::~NotificationPermissionCallbackTask() {
75}
76
77void NotificationPermissionCallbackTask::Run() {
78  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
79  RenderViewHost* host = RenderViewHost::FromID(process_id_, route_id_);
80  if (host)
81    host->Send(new ViewMsg_PermissionRequestDone(route_id_, request_id_));
82}
83
84
85// NotificationPermissionInfoBarDelegate --------------------------------------
86
87// The delegate for the infobar shown when an origin requests notification
88// permissions.
89class NotificationPermissionInfoBarDelegate : public ConfirmInfoBarDelegate {
90 public:
91  NotificationPermissionInfoBarDelegate(TabContents* contents,
92                                        const GURL& origin,
93                                        const string16& display_name,
94                                        int process_id,
95                                        int route_id,
96                                        int callback_context);
97
98 private:
99  virtual ~NotificationPermissionInfoBarDelegate();
100
101  // ConfirmInfoBarDelegate:
102  virtual void InfoBarClosed();
103  virtual SkBitmap* GetIcon() const;
104  virtual Type GetInfoBarType() const;
105  virtual string16 GetMessageText() const;
106  virtual string16 GetButtonLabel(InfoBarButton button) const;
107  virtual bool Accept();
108  virtual bool Cancel();
109
110  // The origin we are asking for permissions on.
111  GURL origin_;
112
113  // The display name for the origin to be displayed.  Will be different from
114  // origin_ for extensions.
115  string16 display_name_;
116
117  // The Profile that we restore sessions from.
118  Profile* profile_;
119
120  // The callback information that tells us how to respond to javascript via
121  // the correct RenderView.
122  int process_id_;
123  int route_id_;
124  int callback_context_;
125
126  // Whether the user clicked one of the buttons.
127  bool action_taken_;
128
129  DISALLOW_COPY_AND_ASSIGN(NotificationPermissionInfoBarDelegate);
130};
131
132NotificationPermissionInfoBarDelegate::NotificationPermissionInfoBarDelegate(
133    TabContents* contents,
134    const GURL& origin,
135    const string16& display_name,
136    int process_id,
137    int route_id,
138    int callback_context)
139    : ConfirmInfoBarDelegate(contents),
140      origin_(origin),
141      display_name_(display_name),
142      profile_(contents->profile()),
143      process_id_(process_id),
144      route_id_(route_id),
145      callback_context_(callback_context),
146      action_taken_(false) {
147}
148
149NotificationPermissionInfoBarDelegate::
150    ~NotificationPermissionInfoBarDelegate() {
151}
152
153void NotificationPermissionInfoBarDelegate::InfoBarClosed() {
154  if (!action_taken_)
155    UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Ignored", 1);
156
157  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
158      new NotificationPermissionCallbackTask(process_id_, route_id_,
159                                             callback_context_));
160
161  delete this;
162}
163
164SkBitmap* NotificationPermissionInfoBarDelegate::GetIcon() const {
165  return ResourceBundle::GetSharedInstance().GetBitmapNamed(
166     IDR_PRODUCT_ICON_32);
167}
168
169InfoBarDelegate::Type
170    NotificationPermissionInfoBarDelegate::GetInfoBarType() const {
171  return PAGE_ACTION_TYPE;
172}
173
174string16 NotificationPermissionInfoBarDelegate::GetMessageText() const {
175  return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS,
176                                    display_name_);
177}
178
179string16 NotificationPermissionInfoBarDelegate::GetButtonLabel(
180    InfoBarButton button) const {
181  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
182      IDS_NOTIFICATION_PERMISSION_YES : IDS_NOTIFICATION_PERMISSION_NO);
183}
184
185bool NotificationPermissionInfoBarDelegate::Accept() {
186  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1);
187  profile_->GetDesktopNotificationService()->GrantPermission(origin_);
188  action_taken_ = true;
189  return true;
190}
191
192bool NotificationPermissionInfoBarDelegate::Cancel() {
193  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1);
194  profile_->GetDesktopNotificationService()->DenyPermission(origin_);
195  action_taken_ = true;
196  return true;
197}
198
199
200// DesktopNotificationService -------------------------------------------------
201
202// static
203string16 DesktopNotificationService::CreateDataUrl(
204    const GURL& icon_url, const string16& title, const string16& body,
205    WebTextDirection dir) {
206  int resource;
207  std::vector<std::string> subst;
208  if (icon_url.is_valid()) {
209    resource = IDR_NOTIFICATION_ICON_HTML;
210    subst.push_back(icon_url.spec());
211    subst.push_back(EscapeForHTML(UTF16ToUTF8(title)));
212    subst.push_back(EscapeForHTML(UTF16ToUTF8(body)));
213    // icon float position
214    subst.push_back(dir == WebKit::WebTextDirectionRightToLeft ?
215                    "right" : "left");
216  } else if (title.empty() || body.empty()) {
217    resource = IDR_NOTIFICATION_1LINE_HTML;
218    string16 line = title.empty() ? body : title;
219    // Strings are div names in the template file.
220    string16 line_name = title.empty() ? ASCIIToUTF16("description")
221                                       : ASCIIToUTF16("title");
222    subst.push_back(EscapeForHTML(UTF16ToUTF8(line_name)));
223    subst.push_back(EscapeForHTML(UTF16ToUTF8(line)));
224  } else {
225    resource = IDR_NOTIFICATION_2LINE_HTML;
226    subst.push_back(EscapeForHTML(UTF16ToUTF8(title)));
227    subst.push_back(EscapeForHTML(UTF16ToUTF8(body)));
228  }
229  // body text direction
230  subst.push_back(dir == WebKit::WebTextDirectionRightToLeft ?
231                  "rtl" : "ltr");
232
233  return CreateDataUrl(resource, subst);
234}
235
236// static
237string16 DesktopNotificationService::CreateDataUrl(
238    int resource, const std::vector<std::string>& subst) {
239  const base::StringPiece template_html(
240      ResourceBundle::GetSharedInstance().GetRawDataResource(
241          resource));
242
243  if (template_html.empty()) {
244    NOTREACHED() << "unable to load template. ID: " << resource;
245    return string16();
246  }
247
248  std::string data = ReplaceStringPlaceholders(template_html, subst, NULL);
249  return UTF8ToUTF16("data:text/html;charset=utf-8," +
250                      EscapeQueryParamValue(data, false));
251}
252
253DesktopNotificationService::DesktopNotificationService(Profile* profile,
254    NotificationUIManager* ui_manager)
255    : profile_(profile),
256      ui_manager_(ui_manager) {
257  prefs_registrar_.Init(profile_->GetPrefs());
258  InitPrefs();
259  StartObserving();
260}
261
262DesktopNotificationService::~DesktopNotificationService() {
263  StopObserving();
264}
265
266void DesktopNotificationService::RegisterUserPrefs(PrefService* user_prefs) {
267  if (!user_prefs->FindPreference(
268      prefs::kDesktopNotificationDefaultContentSetting)) {
269    user_prefs->RegisterIntegerPref(
270        prefs::kDesktopNotificationDefaultContentSetting, kDefaultSetting);
271  }
272  if (!user_prefs->FindPreference(prefs::kDesktopNotificationAllowedOrigins))
273    user_prefs->RegisterListPref(prefs::kDesktopNotificationAllowedOrigins);
274  if (!user_prefs->FindPreference(prefs::kDesktopNotificationDeniedOrigins))
275    user_prefs->RegisterListPref(prefs::kDesktopNotificationDeniedOrigins);
276}
277
278// Initialize the cache with the allowed and denied origins, or
279// create the preferences if they don't exist yet.
280void DesktopNotificationService::InitPrefs() {
281  PrefService* prefs = profile_->GetPrefs();
282  std::vector<GURL> allowed_origins;
283  std::vector<GURL> denied_origins;
284  ContentSetting default_content_setting = CONTENT_SETTING_DEFAULT;
285
286  if (!profile_->IsOffTheRecord()) {
287    default_content_setting = IntToContentSetting(
288        prefs->GetInteger(prefs::kDesktopNotificationDefaultContentSetting));
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
300void DesktopNotificationService::StartObserving() {
301  if (!profile_->IsOffTheRecord()) {
302    prefs_registrar_.Add(prefs::kDesktopNotificationDefaultContentSetting,
303                         this);
304    prefs_registrar_.Add(prefs::kDesktopNotificationAllowedOrigins, this);
305    prefs_registrar_.Add(prefs::kDesktopNotificationDeniedOrigins, this);
306
307    notification_registrar_.Add(this, NotificationType::EXTENSION_UNLOADED,
308                                NotificationService::AllSources());
309  }
310
311  notification_registrar_.Add(this, NotificationType::PROFILE_DESTROYED,
312                              Source<Profile>(profile_));
313}
314
315void DesktopNotificationService::StopObserving() {
316  if (!profile_->IsOffTheRecord()) {
317    prefs_registrar_.RemoveAll();
318  }
319  notification_registrar_.RemoveAll();
320}
321
322void DesktopNotificationService::GrantPermission(const GURL& origin) {
323  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324  PersistPermissionChange(origin, true);
325
326  // Schedule a cache update on the IO thread.
327  BrowserThread::PostTask(
328      BrowserThread::IO, FROM_HERE,
329      NewRunnableMethod(
330          prefs_cache_.get(), &NotificationsPrefsCache::CacheAllowedOrigin,
331          origin));
332
333  NotifySettingsChange();
334}
335
336void DesktopNotificationService::DenyPermission(const GURL& origin) {
337  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338  PersistPermissionChange(origin, false);
339
340  // Schedule a cache update on the IO thread.
341  BrowserThread::PostTask(
342      BrowserThread::IO, FROM_HERE,
343      NewRunnableMethod(
344          prefs_cache_.get(), &NotificationsPrefsCache::CacheDeniedOrigin,
345          origin));
346
347  NotifySettingsChange();
348}
349
350void DesktopNotificationService::Observe(NotificationType type,
351                                         const NotificationSource& source,
352                                         const NotificationDetails& details) {
353  if (NotificationType::PREF_CHANGED == type) {
354    const std::string& name = *Details<std::string>(details).ptr();
355    OnPrefsChanged(name);
356  } else if (NotificationType::EXTENSION_UNLOADED == type) {
357    // Remove all notifications currently shown or queued by the extension
358    // which was unloaded.
359    const Extension* extension =
360        Details<UnloadedExtensionInfo>(details)->extension;
361    if (extension)
362      ui_manager_->CancelAllBySourceOrigin(extension->url());
363  } else if (NotificationType::PROFILE_DESTROYED == type) {
364    StopObserving();
365  }
366}
367
368void DesktopNotificationService::OnPrefsChanged(const std::string& pref_name) {
369  PrefService* prefs = profile_->GetPrefs();
370
371  if (pref_name == prefs::kDesktopNotificationAllowedOrigins) {
372    NotifySettingsChange();
373
374    std::vector<GURL> allowed_origins(GetAllowedOrigins());
375    // Schedule a cache update on the IO thread.
376    BrowserThread::PostTask(
377        BrowserThread::IO, FROM_HERE,
378        NewRunnableMethod(
379            prefs_cache_.get(),
380            &NotificationsPrefsCache::SetCacheAllowedOrigins,
381            allowed_origins));
382  } else if (pref_name == prefs::kDesktopNotificationDeniedOrigins) {
383    NotifySettingsChange();
384
385    std::vector<GURL> denied_origins(GetBlockedOrigins());
386    // Schedule a cache update on the IO thread.
387    BrowserThread::PostTask(
388        BrowserThread::IO, FROM_HERE,
389        NewRunnableMethod(
390            prefs_cache_.get(),
391            &NotificationsPrefsCache::SetCacheDeniedOrigins,
392            denied_origins));
393  } else if (pref_name == prefs::kDesktopNotificationDefaultContentSetting) {
394    NotificationService::current()->Notify(
395        NotificationType::DESKTOP_NOTIFICATION_DEFAULT_CHANGED,
396        Source<DesktopNotificationService>(this),
397        NotificationService::NoDetails());
398
399    const ContentSetting default_content_setting = IntToContentSetting(
400        prefs->GetInteger(prefs::kDesktopNotificationDefaultContentSetting));
401
402    // Schedule a cache update on the IO thread.
403    BrowserThread::PostTask(
404        BrowserThread::IO, FROM_HERE,
405        NewRunnableMethod(
406            prefs_cache_.get(),
407            &NotificationsPrefsCache::SetCacheDefaultContentSetting,
408            default_content_setting));
409  }
410}
411
412void DesktopNotificationService::PersistPermissionChange(
413    const GURL& origin, bool is_allowed) {
414  // Don't persist changes when off the record.
415  if (profile_->IsOffTheRecord())
416    return;
417
418  PrefService* prefs = profile_->GetPrefs();
419
420  // |Observe()| updates the whole permission set in the cache, but only a
421  // single origin has changed. Hence, callers of this method manually
422  // schedule a task to update the prefs cache, and the prefs observer is
423  // disabled while the update runs.
424  StopObserving();
425
426  bool allowed_changed = false;
427  bool denied_changed = false;
428
429  ListValue* allowed_sites =
430      prefs->GetMutableList(prefs::kDesktopNotificationAllowedOrigins);
431  ListValue* denied_sites =
432      prefs->GetMutableList(prefs::kDesktopNotificationDeniedOrigins);
433  {
434    // value is passed to the preferences list, or deleted.
435    StringValue* value = new StringValue(origin.spec());
436
437    // Remove from one list and add to the other.
438    if (is_allowed) {
439      // Remove from the denied list.
440      if (denied_sites->Remove(*value) != -1)
441        denied_changed = true;
442
443      // Add to the allowed list.
444      if (allowed_sites->AppendIfNotPresent(value))
445        allowed_changed = true;
446      else
447        delete value;
448    } else {
449      // Remove from the allowed list.
450      if (allowed_sites->Remove(*value) != -1)
451        allowed_changed = true;
452
453      // Add to the denied list.
454      if (denied_sites->AppendIfNotPresent(value))
455        denied_changed = true;
456      else
457        delete value;
458    }
459  }
460
461  // Persist the pref if anthing changed, but only send updates for the
462  // list that changed.
463  if (allowed_changed || denied_changed) {
464    if (allowed_changed) {
465      ScopedPrefUpdate update_allowed(
466          prefs, prefs::kDesktopNotificationAllowedOrigins);
467    }
468    if (denied_changed) {
469      ScopedPrefUpdate updateDenied(
470          prefs, prefs::kDesktopNotificationDeniedOrigins);
471    }
472    prefs->ScheduleSavePersistentPrefs();
473  }
474  StartObserving();
475}
476
477ContentSetting DesktopNotificationService::GetDefaultContentSetting() {
478  PrefService* prefs = profile_->GetPrefs();
479  ContentSetting setting = IntToContentSetting(
480      prefs->GetInteger(prefs::kDesktopNotificationDefaultContentSetting));
481  if (setting == CONTENT_SETTING_DEFAULT)
482    setting = kDefaultSetting;
483  return setting;
484}
485
486void DesktopNotificationService::SetDefaultContentSetting(
487    ContentSetting setting) {
488  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
489  profile_->GetPrefs()->SetInteger(
490      prefs::kDesktopNotificationDefaultContentSetting,
491      setting == CONTENT_SETTING_DEFAULT ?  kDefaultSetting : setting);
492  // The cache is updated through the notification observer.
493}
494
495bool DesktopNotificationService::IsDefaultContentSettingManaged() const {
496  return profile_->GetPrefs()->IsManagedPreference(
497      prefs::kDesktopNotificationDefaultContentSetting);
498}
499
500void DesktopNotificationService::ResetToDefaultContentSetting() {
501  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
502
503  PrefService* prefs = profile_->GetPrefs();
504  prefs->ClearPref(prefs::kDesktopNotificationDefaultContentSetting);
505}
506
507std::vector<GURL> DesktopNotificationService::GetAllowedOrigins() {
508  std::vector<GURL> allowed_origins;
509  PrefService* prefs = profile_->GetPrefs();
510  const ListValue* allowed_sites =
511      prefs->GetList(prefs::kDesktopNotificationAllowedOrigins);
512  if (allowed_sites) {
513    NotificationsPrefsCache::ListValueToGurlVector(*allowed_sites,
514                                                   &allowed_origins);
515  }
516  return allowed_origins;
517}
518
519std::vector<GURL> DesktopNotificationService::GetBlockedOrigins() {
520  std::vector<GURL> denied_origins;
521  PrefService* prefs = profile_->GetPrefs();
522  const ListValue* denied_sites =
523      prefs->GetList(prefs::kDesktopNotificationDeniedOrigins);
524  if (denied_sites) {
525    NotificationsPrefsCache::ListValueToGurlVector(*denied_sites,
526                                                   &denied_origins);
527  }
528  return denied_origins;
529}
530
531void DesktopNotificationService::ResetAllowedOrigin(const GURL& origin) {
532  if (profile_->IsOffTheRecord())
533    return;
534
535  // Since this isn't called often, let the normal observer behavior update the
536  // cache in this case.
537  PrefService* prefs = profile_->GetPrefs();
538  ListValue* allowed_sites =
539      prefs->GetMutableList(prefs::kDesktopNotificationAllowedOrigins);
540  {
541    StringValue value(origin.spec());
542    int removed_index = allowed_sites->Remove(value);
543    DCHECK_NE(-1, removed_index) << origin << " was not allowed";
544    ScopedPrefUpdate update_allowed(
545        prefs, prefs::kDesktopNotificationAllowedOrigins);
546  }
547  prefs->ScheduleSavePersistentPrefs();
548}
549
550void DesktopNotificationService::ResetBlockedOrigin(const GURL& origin) {
551  if (profile_->IsOffTheRecord())
552    return;
553
554  // Since this isn't called often, let the normal observer behavior update the
555  // cache in this case.
556  PrefService* prefs = profile_->GetPrefs();
557  ListValue* denied_sites =
558      prefs->GetMutableList(prefs::kDesktopNotificationDeniedOrigins);
559  {
560    StringValue value(origin.spec());
561    int removed_index = denied_sites->Remove(value);
562    DCHECK_NE(-1, removed_index) << origin << " was not blocked";
563    ScopedPrefUpdate update_allowed(
564        prefs, prefs::kDesktopNotificationDeniedOrigins);
565  }
566  prefs->ScheduleSavePersistentPrefs();
567}
568
569void DesktopNotificationService::ResetAllOrigins() {
570  PrefService* prefs = profile_->GetPrefs();
571  prefs->ClearPref(prefs::kDesktopNotificationAllowedOrigins);
572  prefs->ClearPref(prefs::kDesktopNotificationDeniedOrigins);
573}
574
575ContentSetting DesktopNotificationService::GetContentSetting(
576    const GURL& origin) {
577  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
578  if (profile_->IsOffTheRecord())
579    return kDefaultSetting;
580
581  std::vector<GURL> allowed_origins(GetAllowedOrigins());
582  if (std::find(allowed_origins.begin(), allowed_origins.end(), origin) !=
583      allowed_origins.end())
584    return CONTENT_SETTING_ALLOW;
585
586  std::vector<GURL> denied_origins(GetBlockedOrigins());
587  if (std::find(denied_origins.begin(), denied_origins.end(), origin) !=
588      denied_origins.end())
589    return CONTENT_SETTING_BLOCK;
590
591  return GetDefaultContentSetting();
592}
593
594void DesktopNotificationService::RequestPermission(
595    const GURL& origin, int process_id, int route_id, int callback_context,
596    TabContents* tab) {
597  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
598  if (!tab)
599    return;
600
601  // If |origin| hasn't been seen before and the default content setting for
602  // notifications is "ask", show an infobar.
603  // The cache can only answer queries on the IO thread once it's initialized,
604  // so don't ask the cache.
605  ContentSetting setting = GetContentSetting(origin);
606  if (setting == CONTENT_SETTING_ASK) {
607    // Show an info bar requesting permission.
608    tab->AddInfoBar(new NotificationPermissionInfoBarDelegate(
609                        tab, origin, DisplayNameForOrigin(origin), process_id,
610                        route_id, callback_context));
611  } else {
612    // Notify renderer immediately.
613    BrowserThread::PostTask(
614      BrowserThread::IO, FROM_HERE,
615      new NotificationPermissionCallbackTask(
616          process_id, route_id, callback_context));
617  }
618}
619
620void DesktopNotificationService::ShowNotification(
621    const Notification& notification) {
622  ui_manager_->Add(notification, profile_);
623}
624
625bool DesktopNotificationService::CancelDesktopNotification(
626    int process_id, int route_id, int notification_id) {
627  scoped_refptr<NotificationObjectProxy> proxy(
628      new NotificationObjectProxy(process_id, route_id, notification_id,
629                                  false));
630  return ui_manager_->CancelById(proxy->id());
631}
632
633
634bool DesktopNotificationService::ShowDesktopNotification(
635    const ViewHostMsg_ShowNotification_Params& params,
636    int process_id, int route_id, DesktopNotificationSource source) {
637  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
638  const GURL& origin = params.origin;
639  NotificationObjectProxy* proxy =
640      new NotificationObjectProxy(process_id, route_id,
641                                  params.notification_id,
642                                  source == WorkerNotification);
643  GURL contents;
644  if (params.is_html) {
645    contents = params.contents_url;
646  } else {
647    // "upconvert" the string parameters to a data: URL.
648    contents = GURL(
649        CreateDataUrl(params.icon_url, params.title, params.body,
650                      params.direction));
651  }
652  Notification notification(
653      origin, contents, DisplayNameForOrigin(origin),
654      params.replace_id, proxy);
655  ShowNotification(notification);
656  return true;
657}
658
659string16 DesktopNotificationService::DisplayNameForOrigin(
660    const GURL& origin) {
661  // If the source is an extension, lookup the display name.
662  if (origin.SchemeIs(chrome::kExtensionScheme)) {
663    ExtensionService* ext_service = profile_->GetExtensionService();
664    if (ext_service) {
665      const Extension* extension = ext_service->GetExtensionByURL(origin);
666      if (extension)
667        return UTF8ToUTF16(extension->name());
668    }
669  }
670  return UTF8ToUTF16(origin.host());
671}
672
673void DesktopNotificationService::NotifySettingsChange() {
674  NotificationService::current()->Notify(
675      NotificationType::DESKTOP_NOTIFICATION_SETTINGS_CHANGED,
676      Source<DesktopNotificationService>(this),
677      NotificationService::NoDetails());
678}
679