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