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