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