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