Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / notifications / desktop_notification_service.cc
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
63 using blink::WebTextDirection;
64 using content::BrowserThread;
65 using content::RenderViewHost;
66 using content::WebContents;
67 using message_center::NotifierId;
68
69 namespace {
70
71 const char kChromeNowExtensionID[] = "pafkbggdmjlpgkdkcbjmhmfcdpncadgh";
72
73 // NotificationPermissionRequest ---------------------------------------
74
75 class 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
115 NotificationPermissionRequest::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
126 NotificationPermissionRequest::~NotificationPermissionRequest() {}
127
128 int NotificationPermissionRequest::GetIconID() const {
129   return IDR_INFOBAR_DESKTOP_NOTIFICATIONS;
130 }
131
132 base::string16 NotificationPermissionRequest::GetMessageText() const {
133   return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS,
134                                     display_name_);
135 }
136
137 base::string16
138 NotificationPermissionRequest::GetMessageTextFragment() const {
139   return l10n_util::GetStringUTF16(IDS_NOTIFICATION_PERMISSIONS_FRAGMENT);
140 }
141
142 bool NotificationPermissionRequest::HasUserGesture() const {
143   // Currently notification permission requests are only issued on
144   // user gesture.
145   return true;
146 }
147
148 GURL NotificationPermissionRequest::GetRequestingHostname() const {
149   return origin_;
150 }
151
152 void NotificationPermissionRequest::PermissionGranted() {
153   action_taken_ = true;
154   UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1);
155   notification_service_->GrantPermission(origin_);
156 }
157
158 void NotificationPermissionRequest::PermissionDenied() {
159   action_taken_ = true;
160   UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1);
161   notification_service_->DenyPermission(origin_);
162 }
163
164 void NotificationPermissionRequest::Cancelled() {
165 }
166
167 void 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.
181 class 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
227 void 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
239 NotificationPermissionInfoBarDelegate::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
252 NotificationPermissionInfoBarDelegate::
253     ~NotificationPermissionInfoBarDelegate() {
254   if (!action_taken_)
255     UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Ignored", 1);
256
257   callback_.Run();
258 }
259
260 int NotificationPermissionInfoBarDelegate::GetIconID() const {
261   return IDR_INFOBAR_DESKTOP_NOTIFICATIONS;
262 }
263
264 infobars::InfoBarDelegate::Type
265 NotificationPermissionInfoBarDelegate::GetInfoBarType() const {
266   return PAGE_ACTION_TYPE;
267 }
268
269 base::string16 NotificationPermissionInfoBarDelegate::GetMessageText() const {
270   return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS,
271                                     display_name_);
272 }
273
274 base::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
280 bool NotificationPermissionInfoBarDelegate::Accept() {
281   UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1);
282   notification_service_->GrantPermission(origin_);
283   action_taken_ = true;
284   return true;
285 }
286
287 bool NotificationPermissionInfoBarDelegate::Cancel() {
288   UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1);
289   notification_service_->DenyPermission(origin_);
290   action_taken_ = true;
291   return true;
292 }
293
294 void 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
304 void 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
319 base::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
356 base::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
373 std::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
388 DesktopNotificationService::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
428 DesktopNotificationService::~DesktopNotificationService() {
429 }
430
431 void 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
442 void 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
453 ContentSetting DesktopNotificationService::GetDefaultContentSetting(
454     std::string* provider_id) {
455   return profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
456       CONTENT_SETTINGS_TYPE_NOTIFICATIONS, provider_id);
457 }
458
459 void DesktopNotificationService::SetDefaultContentSetting(
460     ContentSetting setting) {
461   profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
462       CONTENT_SETTINGS_TYPE_NOTIFICATIONS, setting);
463 }
464
465 void DesktopNotificationService::ResetToDefaultContentSetting() {
466   profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
467       CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_DEFAULT);
468 }
469
470 void DesktopNotificationService::GetNotificationsSettings(
471     ContentSettingsForOneType* settings) {
472   profile_->GetHostContentSettingsMap()->GetSettingsForOneType(
473       CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
474       NO_RESOURCE_IDENTIFIER,
475       settings);
476 }
477
478 void 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
488 void DesktopNotificationService::ResetAllOrigins() {
489   profile_->GetHostContentSettingsMap()->ClearSettingsForOneType(
490       CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
491 }
492
493 ContentSetting 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
502 void 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
546 void 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
570 base::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
595 void 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
602 NotificationUIManager* 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
610 bool 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
635 void 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
682 void 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
695 void 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
711 void 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
732 void 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 }