desktop_notification_service.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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/bind.h"
8#include "base/metrics/histogram.h"
9#include "base/prefs/scoped_user_pref_update.h"
10#include "base/strings/utf_string_conversions.h"
11#include "base/threading/thread.h"
12#include "chrome/browser/browser_process.h"
13#include "chrome/browser/chrome_notification_types.h"
14#include "chrome/browser/content_settings/content_settings_details.h"
15#include "chrome/browser/content_settings/content_settings_provider.h"
16#include "chrome/browser/content_settings/host_content_settings_map.h"
17#include "chrome/browser/infobars/infobar_service.h"
18#include "chrome/browser/notifications/desktop_notification_service_factory.h"
19#include "chrome/browser/notifications/notification.h"
20#include "chrome/browser/notifications/notification_object_proxy.h"
21#include "chrome/browser/notifications/notification_ui_manager.h"
22#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
23#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
24#include "chrome/browser/profiles/profile.h"
25#include "chrome/browser/ui/browser.h"
26#include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
27#include "chrome/browser/ui/website_settings/permission_bubble_request.h"
28#include "chrome/common/content_settings.h"
29#include "chrome/common/content_settings_pattern.h"
30#include "chrome/common/pref_names.h"
31#include "chrome/common/url_constants.h"
32#include "components/infobars/core/confirm_infobar_delegate.h"
33#include "components/infobars/core/infobar.h"
34#include "components/pref_registry/pref_registry_syncable.h"
35#include "content/public/browser/browser_thread.h"
36#include "content/public/browser/notification_service.h"
37#include "content/public/browser/render_frame_host.h"
38#include "content/public/browser/render_process_host.h"
39#include "content/public/browser/render_view_host.h"
40#include "content/public/browser/web_contents.h"
41#include "content/public/common/show_desktop_notification_params.h"
42#include "grit/browser_resources.h"
43#include "grit/chromium_strings.h"
44#include "grit/generated_resources.h"
45#include "grit/theme_resources.h"
46#include "net/base/escape.h"
47#include "ui/base/l10n/l10n_util.h"
48#include "ui/base/resource/resource_bundle.h"
49#include "ui/base/webui/web_ui_util.h"
50#include "ui/message_center/notifier_settings.h"
51
52#if defined(ENABLE_EXTENSIONS)
53#include "chrome/browser/extensions/api/notifications/notifications_api.h"
54#include "chrome/browser/extensions/extension_service.h"
55#include "extensions/browser/event_router.h"
56#include "extensions/browser/extension_system.h"
57#include "extensions/browser/extension_util.h"
58#include "extensions/browser/info_map.h"
59#include "extensions/common/constants.h"
60#include "extensions/common/extension.h"
61#include "extensions/common/extension_set.h"
62#endif
63
64using blink::WebTextDirection;
65using content::BrowserThread;
66using content::RenderViewHost;
67using content::WebContents;
68using message_center::NotifierId;
69
70namespace {
71
72const char kChromeNowExtensionID[] = "pafkbggdmjlpgkdkcbjmhmfcdpncadgh";
73
74// NotificationPermissionRequest ---------------------------------------
75
76class NotificationPermissionRequest : public PermissionBubbleRequest {
77 public:
78  NotificationPermissionRequest(
79      DesktopNotificationService* notification_service,
80      const GURL& origin,
81      base::string16 display_name,
82      const base::Closure& callback);
83  virtual ~NotificationPermissionRequest();
84
85  // PermissionBubbleDelegate:
86  virtual int GetIconID() const OVERRIDE;
87  virtual base::string16 GetMessageText() const OVERRIDE;
88  virtual base::string16 GetMessageTextFragment() const OVERRIDE;
89  virtual bool HasUserGesture() const OVERRIDE;
90  virtual GURL GetRequestingHostname() const OVERRIDE;
91  virtual void PermissionGranted() OVERRIDE;
92  virtual void PermissionDenied() OVERRIDE;
93  virtual void Cancelled() OVERRIDE;
94  virtual void RequestFinished() OVERRIDE;
95
96 private:
97  // The notification service to be used.
98  DesktopNotificationService* notification_service_;
99
100  // The origin we are asking for permissions on.
101  GURL origin_;
102
103  // The display name for the origin to be displayed.  Will be different from
104  // origin_ for extensions.
105  base::string16 display_name_;
106
107  // The callback information that tells us how to respond to javascript.
108  base::Closure callback_;
109
110  // Whether the user clicked one of the buttons.
111  bool action_taken_;
112
113  DISALLOW_COPY_AND_ASSIGN(NotificationPermissionRequest);
114};
115
116NotificationPermissionRequest::NotificationPermissionRequest(
117    DesktopNotificationService* notification_service,
118    const GURL& origin,
119    base::string16 display_name,
120    const base::Closure& callback)
121    : notification_service_(notification_service),
122      origin_(origin),
123      display_name_(display_name),
124      callback_(callback),
125      action_taken_(false) {}
126
127NotificationPermissionRequest::~NotificationPermissionRequest() {}
128
129int NotificationPermissionRequest::GetIconID() const {
130  return IDR_INFOBAR_DESKTOP_NOTIFICATIONS;
131}
132
133base::string16 NotificationPermissionRequest::GetMessageText() const {
134  return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS,
135                                    display_name_);
136}
137
138base::string16
139NotificationPermissionRequest::GetMessageTextFragment() const {
140  return l10n_util::GetStringUTF16(IDS_NOTIFICATION_PERMISSIONS_FRAGMENT);
141}
142
143bool NotificationPermissionRequest::HasUserGesture() const {
144  // Currently notification permission requests are only issued on
145  // user gesture.
146  return true;
147}
148
149GURL NotificationPermissionRequest::GetRequestingHostname() const {
150  return origin_;
151}
152
153void NotificationPermissionRequest::PermissionGranted() {
154  action_taken_ = true;
155  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1);
156  notification_service_->GrantPermission(origin_);
157}
158
159void NotificationPermissionRequest::PermissionDenied() {
160  action_taken_ = true;
161  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1);
162  notification_service_->DenyPermission(origin_);
163}
164
165void NotificationPermissionRequest::Cancelled() {
166}
167
168void NotificationPermissionRequest::RequestFinished() {
169  if (!action_taken_)
170    UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Ignored", 1);
171
172  callback_.Run();
173
174  delete this;
175}
176
177
178// NotificationPermissionInfoBarDelegate --------------------------------------
179
180// The delegate for the infobar shown when an origin requests notification
181// permissions.
182class NotificationPermissionInfoBarDelegate : public ConfirmInfoBarDelegate {
183 public:
184  // Creates a notification permission infobar and delegate and adds the infobar
185  // to |infobar_service|.
186  static void Create(InfoBarService* infobar_service,
187                     DesktopNotificationService* notification_service,
188                     const GURL& origin,
189                     const base::string16& display_name,
190                     const base::Closure& callback);
191
192 private:
193  NotificationPermissionInfoBarDelegate(
194      DesktopNotificationService* notification_service,
195      const GURL& origin,
196      const base::string16& display_name,
197      const base::Closure& callback);
198  virtual ~NotificationPermissionInfoBarDelegate();
199
200  // ConfirmInfoBarDelegate:
201  virtual int GetIconID() const OVERRIDE;
202  virtual Type GetInfoBarType() const OVERRIDE;
203  virtual base::string16 GetMessageText() const OVERRIDE;
204  virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
205  virtual bool Accept() OVERRIDE;
206  virtual bool Cancel() OVERRIDE;
207
208  // The origin we are asking for permissions on.
209  GURL origin_;
210
211  // The display name for the origin to be displayed.  Will be different from
212  // origin_ for extensions.
213  base::string16 display_name_;
214
215  // The notification service to be used.
216  DesktopNotificationService* notification_service_;
217
218  // The callback information that tells us how to respond to javascript.
219  base::Closure callback_;
220
221  // Whether the user clicked one of the buttons.
222  bool action_taken_;
223
224  DISALLOW_COPY_AND_ASSIGN(NotificationPermissionInfoBarDelegate);
225};
226
227// static
228void NotificationPermissionInfoBarDelegate::Create(
229    InfoBarService* infobar_service,
230    DesktopNotificationService* notification_service,
231    const GURL& origin,
232    const base::string16& display_name,
233    const base::Closure& callback) {
234  infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
235      scoped_ptr<ConfirmInfoBarDelegate>(
236          new NotificationPermissionInfoBarDelegate(
237              notification_service, origin, display_name, callback))));
238}
239
240NotificationPermissionInfoBarDelegate::NotificationPermissionInfoBarDelegate(
241    DesktopNotificationService* notification_service,
242    const GURL& origin,
243    const base::string16& display_name,
244    const base::Closure& callback)
245    : ConfirmInfoBarDelegate(),
246      origin_(origin),
247      display_name_(display_name),
248      notification_service_(notification_service),
249      callback_(callback),
250      action_taken_(false) {
251}
252
253NotificationPermissionInfoBarDelegate::
254    ~NotificationPermissionInfoBarDelegate() {
255  if (!action_taken_)
256    UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Ignored", 1);
257
258  callback_.Run();
259}
260
261int NotificationPermissionInfoBarDelegate::GetIconID() const {
262  return IDR_INFOBAR_DESKTOP_NOTIFICATIONS;
263}
264
265infobars::InfoBarDelegate::Type
266NotificationPermissionInfoBarDelegate::GetInfoBarType() const {
267  return PAGE_ACTION_TYPE;
268}
269
270base::string16 NotificationPermissionInfoBarDelegate::GetMessageText() const {
271  return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS,
272                                    display_name_);
273}
274
275base::string16 NotificationPermissionInfoBarDelegate::GetButtonLabel(
276    InfoBarButton button) const {
277  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
278      IDS_NOTIFICATION_PERMISSION_YES : IDS_NOTIFICATION_PERMISSION_NO);
279}
280
281bool NotificationPermissionInfoBarDelegate::Accept() {
282  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1);
283  notification_service_->GrantPermission(origin_);
284  action_taken_ = true;
285  return true;
286}
287
288bool NotificationPermissionInfoBarDelegate::Cancel() {
289  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1);
290  notification_service_->DenyPermission(origin_);
291  action_taken_ = true;
292  return true;
293}
294
295void CancelNotification(const std::string& id) {
296  g_browser_process->notification_ui_manager()->CancelById(id);
297}
298
299}  // namespace
300
301
302// DesktopNotificationService -------------------------------------------------
303
304// static
305void DesktopNotificationService::RegisterProfilePrefs(
306    user_prefs::PrefRegistrySyncable* registry) {
307  registry->RegisterListPref(
308      prefs::kMessageCenterDisabledExtensionIds,
309      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
310  registry->RegisterListPref(
311      prefs::kMessageCenterDisabledSystemComponentIds,
312      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
313  registry->RegisterListPref(
314      prefs::kMessageCenterEnabledSyncNotifierIds,
315      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
316  ExtensionWelcomeNotification::RegisterProfilePrefs(registry);
317}
318
319// static
320base::string16 DesktopNotificationService::CreateDataUrl(
321    const GURL& icon_url,
322    const base::string16& title,
323    const base::string16& body,
324    WebTextDirection dir) {
325  int resource;
326  std::vector<std::string> subst;
327  if (icon_url.is_valid()) {
328    resource = IDR_NOTIFICATION_ICON_HTML;
329    subst.push_back(icon_url.spec());
330    subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(title)));
331    subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(body)));
332    // icon float position
333    subst.push_back(dir == blink::WebTextDirectionRightToLeft ?
334                    "right" : "left");
335  } else if (title.empty() || body.empty()) {
336    resource = IDR_NOTIFICATION_1LINE_HTML;
337    base::string16 line = title.empty() ? body : title;
338    // Strings are div names in the template file.
339    base::string16 line_name =
340        title.empty() ? base::ASCIIToUTF16("description")
341                      : base::ASCIIToUTF16("title");
342    subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(line_name)));
343    subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(line)));
344  } else {
345    resource = IDR_NOTIFICATION_2LINE_HTML;
346    subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(title)));
347    subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(body)));
348  }
349  // body text direction
350  subst.push_back(dir == blink::WebTextDirectionRightToLeft ?
351                  "rtl" : "ltr");
352
353  return CreateDataUrl(resource, subst);
354}
355
356// static
357base::string16 DesktopNotificationService::CreateDataUrl(
358    int resource, const std::vector<std::string>& subst) {
359  const base::StringPiece template_html(
360      ResourceBundle::GetSharedInstance().GetRawDataResource(
361          resource));
362
363  if (template_html.empty()) {
364    NOTREACHED() << "unable to load template. ID: " << resource;
365    return base::string16();
366  }
367
368  std::string data = ReplaceStringPlaceholders(template_html, subst, NULL);
369  return base::UTF8ToUTF16("data:text/html;charset=utf-8," +
370                               net::EscapeQueryParamValue(data, false));
371}
372
373// static
374std::string DesktopNotificationService::AddIconNotification(
375    const GURL& origin_url,
376    const base::string16& title,
377    const base::string16& message,
378    const gfx::Image& icon,
379    const base::string16& replace_id,
380    NotificationDelegate* delegate,
381    Profile* profile) {
382  Notification notification(origin_url, icon, title, message,
383                            blink::WebTextDirectionDefault,
384                            base::string16(), replace_id, delegate);
385  g_browser_process->notification_ui_manager()->Add(notification, profile);
386  return notification.delegate_id();
387}
388
389DesktopNotificationService::DesktopNotificationService(
390    Profile* profile,
391    NotificationUIManager* ui_manager)
392    : profile_(profile),
393      ui_manager_(ui_manager) {
394  OnStringListPrefChanged(
395      prefs::kMessageCenterDisabledExtensionIds, &disabled_extension_ids_);
396  OnStringListPrefChanged(
397      prefs::kMessageCenterDisabledSystemComponentIds,
398      &disabled_system_component_ids_);
399  OnStringListPrefChanged(
400      prefs::kMessageCenterEnabledSyncNotifierIds, &enabled_sync_notifier_ids_);
401  disabled_extension_id_pref_.Init(
402      prefs::kMessageCenterDisabledExtensionIds,
403      profile_->GetPrefs(),
404      base::Bind(
405          &DesktopNotificationService::OnStringListPrefChanged,
406          base::Unretained(this),
407          base::Unretained(prefs::kMessageCenterDisabledExtensionIds),
408          base::Unretained(&disabled_extension_ids_)));
409  disabled_system_component_id_pref_.Init(
410      prefs::kMessageCenterDisabledSystemComponentIds,
411      profile_->GetPrefs(),
412      base::Bind(
413          &DesktopNotificationService::OnStringListPrefChanged,
414          base::Unretained(this),
415          base::Unretained(prefs::kMessageCenterDisabledSystemComponentIds),
416          base::Unretained(&disabled_system_component_ids_)));
417  enabled_sync_notifier_id_pref_.Init(
418      prefs::kMessageCenterEnabledSyncNotifierIds,
419      profile_->GetPrefs(),
420      base::Bind(
421          &DesktopNotificationService::OnStringListPrefChanged,
422          base::Unretained(this),
423          base::Unretained(prefs::kMessageCenterEnabledSyncNotifierIds),
424          base::Unretained(&enabled_sync_notifier_ids_)));
425  registrar_.Add(this,
426                 chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED,
427                 content::Source<Profile>(profile_));
428}
429
430DesktopNotificationService::~DesktopNotificationService() {
431}
432
433void DesktopNotificationService::GrantPermission(const GURL& origin) {
434  ContentSettingsPattern primary_pattern =
435      ContentSettingsPattern::FromURLNoWildcard(origin);
436  profile_->GetHostContentSettingsMap()->SetContentSetting(
437      primary_pattern,
438      ContentSettingsPattern::Wildcard(),
439      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
440      NO_RESOURCE_IDENTIFIER,
441      CONTENT_SETTING_ALLOW);
442}
443
444void DesktopNotificationService::DenyPermission(const GURL& origin) {
445  ContentSettingsPattern primary_pattern =
446      ContentSettingsPattern::FromURLNoWildcard(origin);
447  profile_->GetHostContentSettingsMap()->SetContentSetting(
448      primary_pattern,
449      ContentSettingsPattern::Wildcard(),
450      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
451      NO_RESOURCE_IDENTIFIER,
452      CONTENT_SETTING_BLOCK);
453}
454
455ContentSetting DesktopNotificationService::GetDefaultContentSetting(
456    std::string* provider_id) {
457  return profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
458      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, provider_id);
459}
460
461void DesktopNotificationService::SetDefaultContentSetting(
462    ContentSetting setting) {
463  profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
464      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, setting);
465}
466
467void DesktopNotificationService::ResetToDefaultContentSetting() {
468  profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
469      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_DEFAULT);
470}
471
472void DesktopNotificationService::GetNotificationsSettings(
473    ContentSettingsForOneType* settings) {
474  profile_->GetHostContentSettingsMap()->GetSettingsForOneType(
475      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
476      NO_RESOURCE_IDENTIFIER,
477      settings);
478}
479
480void DesktopNotificationService::ClearSetting(
481    const ContentSettingsPattern& pattern) {
482  profile_->GetHostContentSettingsMap()->SetContentSetting(
483      pattern,
484      ContentSettingsPattern::Wildcard(),
485      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
486      NO_RESOURCE_IDENTIFIER,
487      CONTENT_SETTING_DEFAULT);
488}
489
490void DesktopNotificationService::ResetAllOrigins() {
491  profile_->GetHostContentSettingsMap()->ClearSettingsForOneType(
492      CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
493}
494
495ContentSetting DesktopNotificationService::GetContentSetting(
496    const GURL& origin) {
497  return profile_->GetHostContentSettingsMap()->GetContentSetting(
498      origin,
499      origin,
500      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
501      NO_RESOURCE_IDENTIFIER);
502}
503
504void DesktopNotificationService::RequestPermission(
505    const GURL& origin,
506    content::RenderFrameHost* render_frame_host,
507    const base::Closure& callback) {
508  // If |origin| hasn't been seen before and the default content setting for
509  // notifications is "ask", show an infobar.
510  // The cache can only answer queries on the IO thread once it's initialized,
511  // so don't ask the cache.
512  WebContents* web_contents = WebContents::FromRenderFrameHost(
513      render_frame_host);
514  ContentSetting setting = GetContentSetting(origin);
515  if (setting == CONTENT_SETTING_ASK) {
516    if (PermissionBubbleManager::Enabled()) {
517      PermissionBubbleManager* bubble_manager =
518          PermissionBubbleManager::FromWebContents(web_contents);
519      if (bubble_manager) {
520        bubble_manager->AddRequest(new NotificationPermissionRequest(
521            this,
522            origin,
523            DisplayNameForOriginInProcessId(
524                origin, render_frame_host->GetProcess()->GetID()),
525            callback));
526      }
527      return;
528    }
529
530    // Show an info bar requesting permission.
531    InfoBarService* infobar_service =
532        InfoBarService::FromWebContents(web_contents);
533    // |infobar_service| may be NULL, e.g., if this request originated in a
534    // browser action popup, extension background page, or any HTML that runs
535    // outside of a tab.
536    if (infobar_service) {
537      NotificationPermissionInfoBarDelegate::Create(
538          infobar_service, this, origin,
539          DisplayNameForOriginInProcessId(
540              origin, render_frame_host->GetProcess()->GetID()),
541          callback);
542      return;
543    }
544  }
545
546  // Notify renderer immediately.
547  callback.Run();
548}
549
550void DesktopNotificationService::ShowDesktopNotification(
551    const content::ShowDesktopNotificationHostMsgParams& params,
552    content::RenderFrameHost* render_frame_host,
553    content::DesktopNotificationDelegate* delegate,
554    base::Closure* cancel_callback) {
555  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
556  const GURL& origin = params.origin;
557  NotificationObjectProxy* proxy =
558      new NotificationObjectProxy(render_frame_host, delegate);
559
560  base::string16 display_source = DisplayNameForOriginInProcessId(
561      origin, render_frame_host->GetProcess()->GetID());
562  Notification notification(origin, params.icon_url, params.title,
563      params.body, params.direction, display_source, params.replace_id,
564      proxy);
565
566  // The webkit notification doesn't timeout.
567  notification.set_never_timeout(true);
568
569  GetUIManager()->Add(notification, profile_);
570  if (cancel_callback)
571    *cancel_callback = base::Bind(&CancelNotification, proxy->id());
572}
573
574base::string16 DesktopNotificationService::DisplayNameForOriginInProcessId(
575    const GURL& origin, int process_id) {
576#if defined(ENABLE_EXTENSIONS)
577  // If the source is an extension, lookup the display name.
578  if (origin.SchemeIs(extensions::kExtensionScheme)) {
579    extensions::InfoMap* extension_info_map =
580        extensions::ExtensionSystem::Get(profile_)->info_map();
581    if (extension_info_map) {
582      extensions::ExtensionSet extensions;
583      extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin(
584          origin, process_id, extensions::APIPermission::kNotification,
585          &extensions);
586      for (extensions::ExtensionSet::const_iterator iter = extensions.begin();
587           iter != extensions.end(); ++iter) {
588        NotifierId notifier_id(NotifierId::APPLICATION, (*iter)->id());
589        if (IsNotifierEnabled(notifier_id))
590          return base::UTF8ToUTF16((*iter)->name());
591      }
592    }
593  }
594#endif
595
596  return base::UTF8ToUTF16(origin.host());
597}
598
599void DesktopNotificationService::NotifySettingsChange() {
600  content::NotificationService::current()->Notify(
601      chrome::NOTIFICATION_DESKTOP_NOTIFICATION_SETTINGS_CHANGED,
602      content::Source<DesktopNotificationService>(this),
603      content::NotificationService::NoDetails());
604}
605
606NotificationUIManager* DesktopNotificationService::GetUIManager() {
607  // We defer setting ui_manager_ to the global singleton until we need it
608  // in order to avoid UI dependent construction during startup.
609  if (!ui_manager_)
610    ui_manager_ = g_browser_process->notification_ui_manager();
611  return ui_manager_;
612}
613
614bool DesktopNotificationService::IsNotifierEnabled(
615    const NotifierId& notifier_id) {
616  switch (notifier_id.type) {
617    case NotifierId::APPLICATION:
618      return disabled_extension_ids_.find(notifier_id.id) ==
619          disabled_extension_ids_.end();
620    case NotifierId::WEB_PAGE:
621      return GetContentSetting(notifier_id.url) == CONTENT_SETTING_ALLOW;
622    case NotifierId::SYSTEM_COMPONENT:
623#if defined(OS_CHROMEOS)
624      return disabled_system_component_ids_.find(notifier_id.id) ==
625          disabled_system_component_ids_.end();
626#else
627      // We do not disable system component notifications.
628      return true;
629#endif
630    case NotifierId::SYNCED_NOTIFICATION_SERVICE:
631      return enabled_sync_notifier_ids_.find(notifier_id.id) !=
632          enabled_sync_notifier_ids_.end();
633  }
634
635  NOTREACHED();
636  return false;
637}
638
639void DesktopNotificationService::SetNotifierEnabled(
640    const NotifierId& notifier_id,
641    bool enabled) {
642  DCHECK_NE(NotifierId::WEB_PAGE, notifier_id.type);
643
644  bool add_new_item = false;
645  const char* pref_name = NULL;
646  scoped_ptr<base::StringValue> id;
647  switch (notifier_id.type) {
648    case NotifierId::APPLICATION:
649      pref_name = prefs::kMessageCenterDisabledExtensionIds;
650      add_new_item = !enabled;
651      id.reset(new base::StringValue(notifier_id.id));
652      FirePermissionLevelChangedEvent(notifier_id, enabled);
653      break;
654    case NotifierId::SYSTEM_COMPONENT:
655#if defined(OS_CHROMEOS)
656      pref_name = prefs::kMessageCenterDisabledSystemComponentIds;
657      add_new_item = !enabled;
658      id.reset(new base::StringValue(notifier_id.id));
659#else
660      return;
661#endif
662      break;
663    case NotifierId::SYNCED_NOTIFICATION_SERVICE:
664      pref_name = prefs::kMessageCenterEnabledSyncNotifierIds;
665      // Adding a new item if |enabled| == true, since synced notification
666      // services are opt-in.
667      add_new_item = enabled;
668      id.reset(new base::StringValue(notifier_id.id));
669      break;
670    default:
671      NOTREACHED();
672  }
673  DCHECK(pref_name != NULL);
674
675  ListPrefUpdate update(profile_->GetPrefs(), pref_name);
676  base::ListValue* const list = update.Get();
677  if (add_new_item) {
678    // AppendIfNotPresent will delete |adding_value| when the same value
679    // already exists.
680    list->AppendIfNotPresent(id.release());
681  } else {
682    list->Remove(*id, NULL);
683  }
684}
685
686void DesktopNotificationService::ShowWelcomeNotificationIfNecessary(
687    const Notification& notification) {
688  if (!chrome_now_welcome_notification_) {
689    chrome_now_welcome_notification_ =
690        ExtensionWelcomeNotification::Create(kChromeNowExtensionID, profile_);
691  }
692
693  if (chrome_now_welcome_notification_) {
694    chrome_now_welcome_notification_->ShowWelcomeNotificationIfNecessary(
695        notification);
696  }
697}
698
699void DesktopNotificationService::OnStringListPrefChanged(
700    const char* pref_name, std::set<std::string>* ids_field) {
701  ids_field->clear();
702  // Separate GetPrefs()->GetList() to analyze the crash. See crbug.com/322320
703  const PrefService* pref_service = profile_->GetPrefs();
704  CHECK(pref_service);
705  const base::ListValue* pref_list = pref_service->GetList(pref_name);
706  for (size_t i = 0; i < pref_list->GetSize(); ++i) {
707    std::string element;
708    if (pref_list->GetString(i, &element) && !element.empty())
709      ids_field->insert(element);
710    else
711      LOG(WARNING) << i << "-th element is not a string for " << pref_name;
712  }
713}
714
715void DesktopNotificationService::Observe(
716    int type,
717    const content::NotificationSource& source,
718    const content::NotificationDetails& details) {
719#if defined(ENABLE_EXTENSIONS)
720  DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_UNINSTALLED_DEPRECATED, type);
721
722  extensions::Extension* extension =
723      content::Details<extensions::Extension>(details).ptr();
724  NotifierId notifier_id(NotifierId::APPLICATION, extension->id());
725  if (IsNotifierEnabled(notifier_id))
726    return;
727
728  // The settings for ephemeral apps will be persisted across cache evictions.
729  if (extensions::util::IsEphemeralApp(extension->id(), profile_))
730    return;
731
732  SetNotifierEnabled(notifier_id, true);
733#endif
734}
735
736void DesktopNotificationService::FirePermissionLevelChangedEvent(
737    const NotifierId& notifier_id, bool enabled) {
738#if defined(ENABLE_EXTENSIONS)
739  DCHECK_EQ(NotifierId::APPLICATION, notifier_id.type);
740  extensions::api::notifications::PermissionLevel permission =
741      enabled ? extensions::api::notifications::PERMISSION_LEVEL_GRANTED
742              : extensions::api::notifications::PERMISSION_LEVEL_DENIED;
743  scoped_ptr<base::ListValue> args(new base::ListValue());
744  args->Append(new base::StringValue(
745      extensions::api::notifications::ToString(permission)));
746  scoped_ptr<extensions::Event> event(new extensions::Event(
747      extensions::api::notifications::OnPermissionLevelChanged::kEventName,
748      args.Pass()));
749  extensions::EventRouter::Get(profile_)
750      ->DispatchEventToExtension(notifier_id.id, event.Pass());
751
752  // Tell the IO thread that this extension's permission for notifications
753  // has changed.
754  extensions::InfoMap* extension_info_map =
755      extensions::ExtensionSystem::Get(profile_)->info_map();
756  BrowserThread::PostTask(
757      BrowserThread::IO, FROM_HERE,
758      base::Bind(&extensions::InfoMap::SetNotificationsDisabled,
759                 extension_info_map, notifier_id.id, !enabled));
760#endif
761}
762