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