desktop_notification_service.cc revision ca12bfac764ba476d6cd062bf1dde12cc64c3f40
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    for (std::set<std::string>::const_iterator it =
363             enabled_sync_notifier_ids_.begin();
364         it != enabled_sync_notifier_ids_.end(); ++it) {
365      notifier_service->OnSyncedNotificationServiceEnabled(*it, true);
366    }
367  }
368}
369
370DesktopNotificationService::~DesktopNotificationService() {
371}
372
373void DesktopNotificationService::GrantPermission(const GURL& origin) {
374  ContentSettingsPattern primary_pattern =
375      ContentSettingsPattern::FromURLNoWildcard(origin);
376  profile_->GetHostContentSettingsMap()->SetContentSetting(
377      primary_pattern,
378      ContentSettingsPattern::Wildcard(),
379      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
380      NO_RESOURCE_IDENTIFIER,
381      CONTENT_SETTING_ALLOW);
382}
383
384void DesktopNotificationService::DenyPermission(const GURL& origin) {
385  ContentSettingsPattern primary_pattern =
386      ContentSettingsPattern::FromURLNoWildcard(origin);
387  profile_->GetHostContentSettingsMap()->SetContentSetting(
388      primary_pattern,
389      ContentSettingsPattern::Wildcard(),
390      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
391      NO_RESOURCE_IDENTIFIER,
392      CONTENT_SETTING_BLOCK);
393}
394
395ContentSetting DesktopNotificationService::GetDefaultContentSetting(
396    std::string* provider_id) {
397  return profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
398      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, provider_id);
399}
400
401void DesktopNotificationService::SetDefaultContentSetting(
402    ContentSetting setting) {
403  profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
404      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, setting);
405}
406
407void DesktopNotificationService::ResetToDefaultContentSetting() {
408  profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
409      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_DEFAULT);
410}
411
412void DesktopNotificationService::GetNotificationsSettings(
413    ContentSettingsForOneType* settings) {
414  profile_->GetHostContentSettingsMap()->GetSettingsForOneType(
415      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
416      NO_RESOURCE_IDENTIFIER,
417      settings);
418}
419
420void DesktopNotificationService::ClearSetting(
421    const ContentSettingsPattern& pattern) {
422  profile_->GetHostContentSettingsMap()->SetContentSetting(
423      pattern,
424      ContentSettingsPattern::Wildcard(),
425      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
426      NO_RESOURCE_IDENTIFIER,
427      CONTENT_SETTING_DEFAULT);
428}
429
430void DesktopNotificationService::ResetAllOrigins() {
431  profile_->GetHostContentSettingsMap()->ClearSettingsForOneType(
432      CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
433}
434
435ContentSetting DesktopNotificationService::GetContentSetting(
436    const GURL& origin) {
437  return profile_->GetHostContentSettingsMap()->GetContentSetting(
438      origin,
439      origin,
440      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
441      NO_RESOURCE_IDENTIFIER);
442}
443
444void DesktopNotificationService::RequestPermission(
445    const GURL& origin, int process_id, int route_id, int callback_context,
446    WebContents* contents) {
447  // If |origin| hasn't been seen before and the default content setting for
448  // notifications is "ask", show an infobar.
449  // The cache can only answer queries on the IO thread once it's initialized,
450  // so don't ask the cache.
451  ContentSetting setting = GetContentSetting(origin);
452  if (setting == CONTENT_SETTING_ASK) {
453    // Show an info bar requesting permission.
454    InfoBarService* infobar_service =
455        InfoBarService::FromWebContents(contents);
456    // |infobar_service| may be NULL, e.g., if this request originated in a
457    // browser action popup, extension background page, or any HTML that runs
458    // outside of a tab.
459    if (infobar_service) {
460      NotificationPermissionInfoBarDelegate::Create(
461          infobar_service,
462          DesktopNotificationServiceFactory::GetForProfile(
463              Profile::FromBrowserContext(contents->GetBrowserContext())),
464          origin, DisplayNameForOriginInProcessId(origin, process_id),
465          process_id, route_id, callback_context);
466      return;
467    }
468  }
469
470  // Notify renderer immediately.
471  RenderViewHost* host = RenderViewHost::FromID(process_id, route_id);
472  if (host)
473    host->DesktopNotificationPermissionRequestDone(callback_context);
474}
475
476#if !defined(OS_WIN)
477void DesktopNotificationService::ShowNotification(
478    const Notification& notification) {
479  GetUIManager()->Add(notification, profile_);
480}
481
482bool DesktopNotificationService::CancelDesktopNotification(
483    int process_id, int route_id, int notification_id) {
484  scoped_refptr<NotificationObjectProxy> proxy(
485      new NotificationObjectProxy(process_id, route_id, notification_id,
486                                  false));
487  return GetUIManager()->CancelById(proxy->id());
488}
489#endif  // OS_WIN
490
491bool DesktopNotificationService::ShowDesktopNotification(
492    const content::ShowDesktopNotificationHostMsgParams& params,
493    int process_id, int route_id, DesktopNotificationSource source) {
494  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
495  const GURL& origin = params.origin;
496  NotificationObjectProxy* proxy =
497      new NotificationObjectProxy(process_id, route_id,
498                                  params.notification_id,
499                                  source == WorkerNotification);
500
501  string16 display_source = DisplayNameForOriginInProcessId(origin, process_id);
502  if (params.is_html) {
503    ShowNotification(Notification(origin, params.contents_url, display_source,
504        params.replace_id, proxy));
505  } else {
506    Notification notification(origin, params.icon_url, params.title,
507        params.body, params.direction, display_source, params.replace_id,
508        proxy);
509    // The webkit notification doesn't timeout.
510    notification.set_never_timeout(true);
511    ShowNotification(notification);
512  }
513  return true;
514}
515
516string16 DesktopNotificationService::DisplayNameForOriginInProcessId(
517    const GURL& origin, int process_id) {
518  // If the source is an extension, lookup the display name.
519  // Message center prefers to use extension name if the notification
520  // is allowed by an extension.
521  if (NotificationUIManager::DelegatesToMessageCenter() ||
522      origin.SchemeIs(extensions::kExtensionScheme)) {
523    ExtensionInfoMap* extension_info_map =
524        extensions::ExtensionSystem::Get(profile_)->info_map();
525    if (extension_info_map) {
526      ExtensionSet extensions;
527      extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin(
528          origin, process_id, extensions::APIPermission::kNotification,
529          &extensions);
530      for (ExtensionSet::const_iterator iter = extensions.begin();
531           iter != extensions.end(); ++iter) {
532        NotifierId notifier_id(NotifierId::APPLICATION, (*iter)->id());
533        if (IsNotifierEnabled(notifier_id))
534          return UTF8ToUTF16((*iter)->name());
535      }
536    }
537  }
538  return UTF8ToUTF16(origin.host());
539}
540
541void DesktopNotificationService::NotifySettingsChange() {
542  content::NotificationService::current()->Notify(
543      chrome::NOTIFICATION_DESKTOP_NOTIFICATION_SETTINGS_CHANGED,
544      content::Source<DesktopNotificationService>(this),
545      content::NotificationService::NoDetails());
546}
547
548NotificationUIManager* DesktopNotificationService::GetUIManager() {
549  // We defer setting ui_manager_ to the global singleton until we need it
550  // in order to avoid UI dependent construction during startup.
551  if (!ui_manager_)
552    ui_manager_ = g_browser_process->notification_ui_manager();
553  return ui_manager_;
554}
555
556bool DesktopNotificationService::IsNotifierEnabled(
557    const NotifierId& notifier_id) {
558  switch (notifier_id.type) {
559    case NotifierId::APPLICATION:
560      return disabled_extension_ids_.find(notifier_id.id) ==
561          disabled_extension_ids_.end();
562    case NotifierId::WEB_PAGE:
563      return GetContentSetting(notifier_id.url) == CONTENT_SETTING_ALLOW;
564    case NotifierId::SYSTEM_COMPONENT:
565      return disabled_system_component_ids_.find(
566          message_center::ToString(notifier_id.system_component_type)) ==
567          disabled_system_component_ids_.end();
568    case NotifierId::SYNCED_NOTIFICATION_SERVICE:
569      return enabled_sync_notifier_ids_.find(notifier_id.id) !=
570          enabled_sync_notifier_ids_.end();
571  }
572
573  NOTREACHED();
574  return false;
575}
576
577void DesktopNotificationService::SetNotifierEnabled(
578    const NotifierId& notifier_id,
579    bool enabled) {
580  DCHECK_NE(NotifierId::WEB_PAGE, notifier_id.type);
581
582  bool add_new_item = false;
583  const char* pref_name = NULL;
584  scoped_ptr<base::StringValue> id;
585  switch (notifier_id.type) {
586    case NotifierId::APPLICATION:
587      pref_name = prefs::kMessageCenterDisabledExtensionIds;
588      add_new_item = !enabled;
589      id.reset(new base::StringValue(notifier_id.id));
590      break;
591    case NotifierId::SYSTEM_COMPONENT:
592      pref_name = prefs::kMessageCenterDisabledSystemComponentIds;
593      add_new_item = !enabled;
594      id.reset(new base::StringValue(
595          message_center::ToString(notifier_id.system_component_type)));
596      break;
597    case NotifierId::SYNCED_NOTIFICATION_SERVICE:
598      pref_name = prefs::kMessageCenterEnabledSyncNotifierIds;
599      // Adding a new item if |enabled| == true, since synced notification
600      // services are opt-in.
601      add_new_item = enabled;
602      id.reset(new base::StringValue(notifier_id.id));
603      break;
604    default:
605      NOTREACHED();
606  }
607  DCHECK(pref_name != NULL);
608
609  ListPrefUpdate update(profile_->GetPrefs(), pref_name);
610  base::ListValue* const list = update.Get();
611  if (add_new_item) {
612    // AppendIfNotPresent will delete |adding_value| when the same value
613    // already exists.
614    list->AppendIfNotPresent(id.release());
615  } else {
616    list->Remove(*id, NULL);
617  }
618}
619
620bool DesktopNotificationService::IsExtensionEnabled(
621    const std::string& extension_id) {
622  return IsNotifierEnabled(NotifierId(NotifierId::APPLICATION, extension_id));
623}
624
625void DesktopNotificationService::OnStringListPrefChanged(
626    const char* pref_name, std::set<std::string>* ids_field) {
627  ids_field->clear();
628  const base::ListValue* pref_list = profile_->GetPrefs()->GetList(pref_name);
629  for (size_t i = 0; i < pref_list->GetSize(); ++i) {
630    std::string element;
631    if (pref_list->GetString(i, &element) && !element.empty())
632      ids_field->insert(element);
633    else
634      LOG(WARNING) << i << "-th element is not a string for " << pref_name;
635  }
636}
637
638WebKit::WebNotificationPresenter::Permission
639    DesktopNotificationService::HasPermission(const GURL& origin) {
640  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
641  HostContentSettingsMap* host_content_settings_map =
642      profile_->GetHostContentSettingsMap();
643  ContentSetting setting = host_content_settings_map->GetContentSetting(
644      origin,
645      origin,
646      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
647      NO_RESOURCE_IDENTIFIER);
648
649  if (setting == CONTENT_SETTING_ALLOW)
650    return WebKit::WebNotificationPresenter::PermissionAllowed;
651  if (setting == CONTENT_SETTING_BLOCK)
652    return WebKit::WebNotificationPresenter::PermissionDenied;
653  if (setting == CONTENT_SETTING_ASK)
654    return WebKit::WebNotificationPresenter::PermissionNotAllowed;
655  NOTREACHED() << "Invalid notifications settings value: " << setting;
656  return WebKit::WebNotificationPresenter::PermissionNotAllowed;
657}
658
659void DesktopNotificationService::Observe(
660    int type,
661    const content::NotificationSource& source,
662    const content::NotificationDetails& details) {
663  DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_UNINSTALLED, type);
664
665  extensions::Extension* extension =
666      content::Details<extensions::Extension>(details).ptr();
667  NotifierId notifier_id(NotifierId::APPLICATION, extension->id());
668  if (IsNotifierEnabled(notifier_id))
669    return;
670
671  SetNotifierEnabled(notifier_id, true);
672}
673