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