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