desktop_notification_service.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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/content_settings/content_settings_details.h"
12#include "chrome/browser/content_settings/content_settings_provider.h"
13#include "chrome/browser/content_settings/host_content_settings_map.h"
14#include "chrome/browser/extensions/extension_info_map.h"
15#include "chrome/browser/extensions/extension_service.h"
16#include "chrome/browser/extensions/extension_system.h"
17#include "chrome/browser/infobars/confirm_infobar_delegate.h"
18#include "chrome/browser/infobars/infobar_service.h"
19#include "chrome/browser/notifications/desktop_notification_service_factory.h"
20#include "chrome/browser/notifications/notification.h"
21#include "chrome/browser/notifications/notification_object_proxy.h"
22#include "chrome/browser/notifications/notification_ui_manager.h"
23#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
24#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
25#include "chrome/browser/prefs/scoped_user_pref_update.h"
26#include "chrome/browser/profiles/profile.h"
27#include "chrome/browser/ui/browser.h"
28#include "chrome/common/chrome_notification_types.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
60const ContentSetting kDefaultSetting = CONTENT_SETTING_ASK;
61
62// NotificationPermissionInfoBarDelegate --------------------------------------
63
64// The delegate for the infobar shown when an origin requests notification
65// permissions.
66class NotificationPermissionInfoBarDelegate : public ConfirmInfoBarDelegate {
67 public:
68  // Creates a notification permission delegate and adds it to
69  // |infobar_service|.
70  static void Create(InfoBarService* infobar_service,
71                     DesktopNotificationService* notification_service,
72                     const GURL& origin,
73                     const string16& display_name,
74                     int process_id,
75                     int route_id,
76                     int callback_context);
77
78 private:
79  NotificationPermissionInfoBarDelegate(
80      InfoBarService* infobar_service,
81      DesktopNotificationService* notification_service,
82      const GURL& origin,
83      const string16& display_name,
84      int process_id,
85      int route_id,
86      int callback_context);
87  virtual ~NotificationPermissionInfoBarDelegate();
88
89  // ConfirmInfoBarDelegate:
90  virtual int GetIconID() const OVERRIDE;
91  virtual Type GetInfoBarType() const OVERRIDE;
92  virtual string16 GetMessageText() const OVERRIDE;
93  virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
94  virtual bool Accept() OVERRIDE;
95  virtual bool Cancel() OVERRIDE;
96
97  // The origin we are asking for permissions on.
98  GURL origin_;
99
100  // The display name for the origin to be displayed.  Will be different from
101  // origin_ for extensions.
102  string16 display_name_;
103
104  // The notification service to be used.
105  DesktopNotificationService* notification_service_;
106
107  // The callback information that tells us how to respond to javascript via
108  // the correct RenderView.
109  int process_id_;
110  int route_id_;
111  int callback_context_;
112
113  // Whether the user clicked one of the buttons.
114  bool action_taken_;
115
116  DISALLOW_COPY_AND_ASSIGN(NotificationPermissionInfoBarDelegate);
117};
118
119// static
120void NotificationPermissionInfoBarDelegate::Create(
121    InfoBarService* infobar_service,
122    DesktopNotificationService* notification_service,
123    const GURL& origin,
124    const string16& display_name,
125    int process_id,
126    int route_id,
127    int callback_context) {
128  infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>(
129      new NotificationPermissionInfoBarDelegate(
130          infobar_service, notification_service, origin, display_name,
131          process_id, route_id, callback_context)));
132}
133
134NotificationPermissionInfoBarDelegate::NotificationPermissionInfoBarDelegate(
135    InfoBarService* infobar_service,
136    DesktopNotificationService* notification_service,
137    const GURL& origin,
138    const string16& display_name,
139    int process_id,
140    int route_id,
141    int callback_context)
142    : ConfirmInfoBarDelegate(infobar_service),
143      origin_(origin),
144      display_name_(display_name),
145      notification_service_(notification_service),
146      process_id_(process_id),
147      route_id_(route_id),
148      callback_context_(callback_context),
149      action_taken_(false) {
150}
151
152NotificationPermissionInfoBarDelegate::
153    ~NotificationPermissionInfoBarDelegate() {
154  if (!action_taken_)
155    UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Ignored", 1);
156
157  RenderViewHost* host = RenderViewHost::FromID(process_id_, route_id_);
158  if (host)
159    host->DesktopNotificationPermissionRequestDone(callback_context_);
160}
161
162int NotificationPermissionInfoBarDelegate::GetIconID() const {
163  return IDR_INFOBAR_DESKTOP_NOTIFICATIONS;
164}
165
166InfoBarDelegate::Type
167    NotificationPermissionInfoBarDelegate::GetInfoBarType() const {
168  return PAGE_ACTION_TYPE;
169}
170
171string16 NotificationPermissionInfoBarDelegate::GetMessageText() const {
172  return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS,
173                                    display_name_);
174}
175
176string16 NotificationPermissionInfoBarDelegate::GetButtonLabel(
177    InfoBarButton button) const {
178  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
179      IDS_NOTIFICATION_PERMISSION_YES : IDS_NOTIFICATION_PERMISSION_NO);
180}
181
182bool NotificationPermissionInfoBarDelegate::Accept() {
183  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1);
184  notification_service_->GrantPermission(origin_);
185  action_taken_ = true;
186  return true;
187}
188
189bool NotificationPermissionInfoBarDelegate::Cancel() {
190  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1);
191  notification_service_->DenyPermission(origin_);
192  action_taken_ = true;
193  return true;
194}
195
196
197// DesktopNotificationService -------------------------------------------------
198
199// static
200void DesktopNotificationService::RegisterUserPrefs(
201    user_prefs::PrefRegistrySyncable* registry) {
202  registry->RegisterListPref(prefs::kMessageCenterDisabledExtensionIds,
203                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
204  registry->RegisterListPref(prefs::kMessageCenterDisabledSystemComponentIds,
205                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
206  registry->RegisterListPref(prefs::kMessageCenterEnabledSyncNotifierIds,
207                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
208}
209
210// static
211string16 DesktopNotificationService::CreateDataUrl(
212    const GURL& icon_url, const string16& title, const string16& body,
213    WebTextDirection dir) {
214  int resource;
215  std::vector<std::string> subst;
216  if (icon_url.is_valid()) {
217    resource = IDR_NOTIFICATION_ICON_HTML;
218    subst.push_back(icon_url.spec());
219    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(title)));
220    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(body)));
221    // icon float position
222    subst.push_back(dir == WebKit::WebTextDirectionRightToLeft ?
223                    "right" : "left");
224  } else if (title.empty() || body.empty()) {
225    resource = IDR_NOTIFICATION_1LINE_HTML;
226    string16 line = title.empty() ? body : title;
227    // Strings are div names in the template file.
228    string16 line_name = title.empty() ? ASCIIToUTF16("description")
229                                       : ASCIIToUTF16("title");
230    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(line_name)));
231    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(line)));
232  } else {
233    resource = IDR_NOTIFICATION_2LINE_HTML;
234    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(title)));
235    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(body)));
236  }
237  // body text direction
238  subst.push_back(dir == WebKit::WebTextDirectionRightToLeft ?
239                  "rtl" : "ltr");
240
241  return CreateDataUrl(resource, subst);
242}
243
244// static
245string16 DesktopNotificationService::CreateDataUrl(
246    int resource, const std::vector<std::string>& subst) {
247  const base::StringPiece template_html(
248      ResourceBundle::GetSharedInstance().GetRawDataResource(
249          resource));
250
251  if (template_html.empty()) {
252    NOTREACHED() << "unable to load template. ID: " << resource;
253    return string16();
254  }
255
256  std::string data = ReplaceStringPlaceholders(template_html, subst, NULL);
257  return UTF8ToUTF16("data:text/html;charset=utf-8," +
258                      net::EscapeQueryParamValue(data, false));
259}
260
261// static
262std::string DesktopNotificationService::AddNotification(
263    const GURL& origin_url,
264    const string16& title,
265    const string16& message,
266    const GURL& icon_url,
267    const string16& replace_id,
268    NotificationDelegate* delegate,
269    Profile* profile) {
270  if (message_center::IsRichNotificationEnabled()) {
271    // For message center create a non-HTML notification with |icon_url|.
272    Notification notification(origin_url, icon_url, title, message,
273                              WebKit::WebTextDirectionDefault,
274                              string16(), replace_id, delegate);
275    g_browser_process->notification_ui_manager()->Add(notification, profile);
276    return notification.notification_id();
277  }
278
279  // Generate a data URL embedding the icon URL, title, and message.
280  GURL content_url(CreateDataUrl(
281      icon_url, title, message, WebKit::WebTextDirectionDefault));
282  Notification notification(
283      GURL(), content_url, string16(), replace_id, delegate);
284  g_browser_process->notification_ui_manager()->Add(notification, profile);
285  return notification.notification_id();
286}
287
288// static
289std::string DesktopNotificationService::AddIconNotification(
290    const GURL& origin_url,
291    const string16& title,
292    const string16& message,
293    const gfx::Image& icon,
294    const string16& replace_id,
295    NotificationDelegate* delegate,
296    Profile* profile) {
297
298  if (message_center::IsRichNotificationEnabled()) {
299    // For message center create a non-HTML notification with |icon|.
300    Notification notification(origin_url, icon, title, message,
301                              WebKit::WebTextDirectionDefault,
302                              string16(), replace_id, delegate);
303    g_browser_process->notification_ui_manager()->Add(notification, profile);
304    return notification.notification_id();
305  }
306
307  GURL icon_url;
308  if (!icon.IsEmpty())
309    icon_url = GURL(webui::GetBitmapDataUrl(*icon.ToSkBitmap()));
310  return AddNotification(
311      origin_url, title, message, icon_url, replace_id, delegate, profile);
312}
313
314// static
315void DesktopNotificationService::RemoveNotification(
316    const std::string& notification_id) {
317    g_browser_process->notification_ui_manager()->CancelById(notification_id);
318}
319
320DesktopNotificationService::DesktopNotificationService(
321    Profile* profile,
322    NotificationUIManager* ui_manager)
323    : profile_(profile),
324      ui_manager_(ui_manager) {
325  OnStringListPrefChanged(
326      prefs::kMessageCenterDisabledExtensionIds, &disabled_extension_ids_);
327  OnStringListPrefChanged(
328      prefs::kMessageCenterDisabledSystemComponentIds,
329      &disabled_system_component_ids_);
330  OnStringListPrefChanged(
331      prefs::kMessageCenterEnabledSyncNotifierIds, &enabled_sync_notifier_ids_);
332  disabled_extension_id_pref_.Init(
333      prefs::kMessageCenterDisabledExtensionIds,
334      profile_->GetPrefs(),
335      base::Bind(
336          &DesktopNotificationService::OnStringListPrefChanged,
337          base::Unretained(this),
338          base::Unretained(prefs::kMessageCenterDisabledExtensionIds),
339          base::Unretained(&disabled_extension_ids_)));
340  disabled_system_component_id_pref_.Init(
341      prefs::kMessageCenterDisabledSystemComponentIds,
342      profile_->GetPrefs(),
343      base::Bind(
344          &DesktopNotificationService::OnStringListPrefChanged,
345          base::Unretained(this),
346          base::Unretained(prefs::kMessageCenterDisabledSystemComponentIds),
347          base::Unretained(&disabled_system_component_ids_)));
348  enabled_sync_notifier_id_pref_.Init(
349      prefs::kMessageCenterEnabledSyncNotifierIds,
350      profile_->GetPrefs(),
351      base::Bind(
352          &DesktopNotificationService::OnStringListPrefChanged,
353          base::Unretained(this),
354          base::Unretained(prefs::kMessageCenterEnabledSyncNotifierIds),
355          base::Unretained(&enabled_sync_notifier_ids_)));
356  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
357                 content::Source<Profile>(profile_));
358  // TODO(mukai, petewil): invoking notifier_service here directly may cause
359  // crashes on several tests, since notifier_service relies on
360  // NotificationUIManager in g_browser_process. To suppress the crashes,
361  // here checks if it really needs to ping notifier_service here.
362  if (!enabled_sync_notifier_ids_.empty()) {
363    notifier::ChromeNotifierService* notifier_service =
364        notifier::ChromeNotifierServiceFactory::GetInstance()->GetForProfile(
365            profile, Profile::EXPLICIT_ACCESS);
366    for (std::set<std::string>::const_iterator it =
367             enabled_sync_notifier_ids_.begin();
368         it != enabled_sync_notifier_ids_.end(); ++it) {
369      notifier_service->OnSyncedNotificationServiceEnabled(*it, true);
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,
469          DisplayNameForOriginInProcessId(origin, process_id),
470          process_id,
471          route_id,
472          callback_context);
473      return;
474    }
475  }
476
477  // Notify renderer immediately.
478  RenderViewHost* host = RenderViewHost::FromID(process_id, route_id);
479  if (host)
480    host->DesktopNotificationPermissionRequestDone(callback_context);
481}
482
483#if !defined(OS_WIN)
484void DesktopNotificationService::ShowNotification(
485    const Notification& notification) {
486  GetUIManager()->Add(notification, profile_);
487}
488
489bool DesktopNotificationService::CancelDesktopNotification(
490    int process_id, int route_id, int notification_id) {
491  scoped_refptr<NotificationObjectProxy> proxy(
492      new NotificationObjectProxy(process_id, route_id, notification_id,
493                                  false));
494  return GetUIManager()->CancelById(proxy->id());
495}
496#endif  // OS_WIN
497
498bool DesktopNotificationService::ShowDesktopNotification(
499    const content::ShowDesktopNotificationHostMsgParams& params,
500    int process_id, int route_id, DesktopNotificationSource source) {
501  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
502  const GURL& origin = params.origin;
503  NotificationObjectProxy* proxy =
504      new NotificationObjectProxy(process_id, route_id,
505                                  params.notification_id,
506                                  source == WorkerNotification);
507
508  string16 display_source = DisplayNameForOriginInProcessId(origin, process_id);
509  if (params.is_html) {
510    ShowNotification(Notification(origin, params.contents_url, display_source,
511        params.replace_id, proxy));
512  } else {
513    Notification notification(origin, params.icon_url, params.title,
514        params.body, params.direction, display_source, params.replace_id,
515        proxy);
516    // The webkit notification doesn't timeout.
517    notification.set_never_timeout(true);
518    ShowNotification(notification);
519  }
520  return true;
521}
522
523string16 DesktopNotificationService::DisplayNameForOriginInProcessId(
524    const GURL& origin, int process_id) {
525  // If the source is an extension, lookup the display name.
526  // Message center prefers to use extension name if the notification
527  // is allowed by an extension.
528  if (NotificationUIManager::DelegatesToMessageCenter() ||
529      origin.SchemeIs(extensions::kExtensionScheme)) {
530    ExtensionInfoMap* extension_info_map =
531        extensions::ExtensionSystem::Get(profile_)->info_map();
532    if (extension_info_map) {
533      ExtensionSet extensions;
534      extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin(
535          origin, process_id, extensions::APIPermission::kNotification,
536          &extensions);
537      for (ExtensionSet::const_iterator iter = extensions.begin();
538           iter != extensions.end(); ++iter) {
539        NotifierId notifier_id(NotifierId::APPLICATION, (*iter)->id());
540        if (IsNotifierEnabled(notifier_id))
541          return UTF8ToUTF16((*iter)->name());
542      }
543    }
544  }
545  return UTF8ToUTF16(origin.host());
546}
547
548void DesktopNotificationService::NotifySettingsChange() {
549  content::NotificationService::current()->Notify(
550      chrome::NOTIFICATION_DESKTOP_NOTIFICATION_SETTINGS_CHANGED,
551      content::Source<DesktopNotificationService>(this),
552      content::NotificationService::NoDetails());
553}
554
555NotificationUIManager* DesktopNotificationService::GetUIManager() {
556  // We defer setting ui_manager_ to the global singleton until we need it
557  // in order to avoid UI dependent construction during startup.
558  if (!ui_manager_)
559    ui_manager_ = g_browser_process->notification_ui_manager();
560  return ui_manager_;
561}
562
563bool DesktopNotificationService::IsNotifierEnabled(
564    const NotifierId& notifier_id) {
565  switch (notifier_id.type) {
566    case NotifierId::APPLICATION:
567      return disabled_extension_ids_.find(notifier_id.id) ==
568          disabled_extension_ids_.end();
569    case NotifierId::WEB_PAGE:
570      return GetContentSetting(notifier_id.url) == CONTENT_SETTING_ALLOW;
571    case NotifierId::SYSTEM_COMPONENT:
572      return disabled_system_component_ids_.find(
573          message_center::ToString(notifier_id.system_component_type)) ==
574          disabled_system_component_ids_.end();
575    case NotifierId::SYNCED_NOTIFICATION_SERVICE:
576      return enabled_sync_notifier_ids_.find(notifier_id.id) !=
577          enabled_sync_notifier_ids_.end();
578  }
579
580  NOTREACHED();
581  return false;
582}
583
584void DesktopNotificationService::SetNotifierEnabled(
585    const NotifierId& notifier_id,
586    bool enabled) {
587  DCHECK_NE(NotifierId::WEB_PAGE, notifier_id.type);
588
589  bool add_new_item = false;
590  const char* pref_name = NULL;
591  scoped_ptr<base::StringValue> id;
592  switch (notifier_id.type) {
593    case NotifierId::APPLICATION:
594      pref_name = prefs::kMessageCenterDisabledExtensionIds;
595      add_new_item = !enabled;
596      id.reset(new base::StringValue(notifier_id.id));
597      break;
598    case NotifierId::SYSTEM_COMPONENT:
599      pref_name = prefs::kMessageCenterDisabledSystemComponentIds;
600      add_new_item = !enabled;
601      id.reset(new base::StringValue(
602          message_center::ToString(notifier_id.system_component_type)));
603      break;
604    case NotifierId::SYNCED_NOTIFICATION_SERVICE:
605      pref_name = prefs::kMessageCenterEnabledSyncNotifierIds;
606      // Adding a new item if |enabled| == true, since synced notification
607      // services are opt-in.
608      add_new_item = enabled;
609      id.reset(new base::StringValue(notifier_id.id));
610      break;
611    default:
612      NOTREACHED();
613  }
614  DCHECK(pref_name != NULL);
615
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}
628
629bool DesktopNotificationService::IsExtensionEnabled(
630    const std::string& extension_id) {
631  return IsNotifierEnabled(NotifierId(NotifierId::APPLICATION, extension_id));
632}
633
634void DesktopNotificationService::OnStringListPrefChanged(
635    const char* pref_name, std::set<std::string>* ids_field) {
636  ids_field->clear();
637  const base::ListValue* pref_list = profile_->GetPrefs()->GetList(pref_name);
638  for (size_t i = 0; i < pref_list->GetSize(); ++i) {
639    std::string element;
640    if (pref_list->GetString(i, &element) && !element.empty())
641      ids_field->insert(element);
642    else
643      LOG(WARNING) << i << "-th element is not a string for " << pref_name;
644  }
645}
646
647WebKit::WebNotificationPresenter::Permission
648    DesktopNotificationService::HasPermission(const GURL& origin) {
649  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
650  HostContentSettingsMap* host_content_settings_map =
651      profile_->GetHostContentSettingsMap();
652  ContentSetting setting = host_content_settings_map->GetContentSetting(
653      origin,
654      origin,
655      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
656      NO_RESOURCE_IDENTIFIER);
657
658  if (setting == CONTENT_SETTING_ALLOW)
659    return WebKit::WebNotificationPresenter::PermissionAllowed;
660  if (setting == CONTENT_SETTING_BLOCK)
661    return WebKit::WebNotificationPresenter::PermissionDenied;
662  if (setting == CONTENT_SETTING_ASK)
663    return WebKit::WebNotificationPresenter::PermissionNotAllowed;
664  NOTREACHED() << "Invalid notifications settings value: " << setting;
665  return WebKit::WebNotificationPresenter::PermissionNotAllowed;
666}
667
668void DesktopNotificationService::Observe(
669    int type,
670    const content::NotificationSource& source,
671    const content::NotificationDetails& details) {
672  DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_UNINSTALLED, type);
673
674  extensions::Extension* extension =
675      content::Details<extensions::Extension>(details).ptr();
676  NotifierId notifier_id(NotifierId::APPLICATION, extension->id());
677  if (IsNotifierEnabled(notifier_id))
678    return;
679
680  SetNotifierEnabled(notifier_id, true);
681}
682