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