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