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