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