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