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