1 // Copyright 2014 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.
5 #include "chrome/browser/notifications/platform_notification_service_impl.h"
7 #include "base/command_line.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/notifications/desktop_notification_profile_util.h"
13 #include "chrome/browser/notifications/notification_object_proxy.h"
14 #include "chrome/browser/notifications/notification_ui_manager.h"
15 #include "chrome/browser/notifications/persistent_notification_delegate.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/profiles/profile_io_data.h"
18 #include "chrome/common/pref_names.h"
19 #include "components/content_settings/core/browser/host_content_settings_map.h"
20 #include "components/content_settings/core/common/content_settings.h"
21 #include "components/url_formatter/url_formatter.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/desktop_notification_delegate.h"
24 #include "content/public/browser/notification_event_dispatcher.h"
25 #include "content/public/browser/platform_notification_context.h"
26 #include "content/public/browser/storage_partition.h"
27 #include "content/public/common/content_switches.h"
28 #include "content/public/common/platform_notification_data.h"
29 #include "ui/message_center/notifier_settings.h"
30 #include "url/url_constants.h"
32 #if defined(ENABLE_EXTENSIONS)
33 #include "chrome/browser/notifications/notifier_state_tracker.h"
34 #include "chrome/browser/notifications/notifier_state_tracker_factory.h"
35 #include "extensions/browser/extension_registry.h"
36 #include "extensions/browser/info_map.h"
37 #include "extensions/common/constants.h"
38 #include "extensions/common/permissions/api_permission.h"
39 #include "extensions/common/permissions/permissions_data.h"
42 #if defined(OS_ANDROID)
43 #include "base/strings/string_number_conversions.h"
46 using content::BrowserContext
;
47 using content::BrowserThread
;
48 using content::PlatformNotificationContext
;
49 using message_center::NotifierId
;
53 // Callback to provide when deleting the data associated with persistent Web
54 // Notifications from the notification database.
55 void OnPersistentNotificationDataDeleted(bool success
) {
56 UMA_HISTOGRAM_BOOLEAN("Notifications.PersistentNotificationDataDeleted",
60 // Persistent notifications fired through the delegate do not care about the
61 // lifetime of the Service Worker responsible for executing the event.
62 void OnEventDispatchComplete(content::PersistentNotificationStatus status
) {
63 UMA_HISTOGRAM_ENUMERATION(
64 "Notifications.PersistentWebNotificationClickResult", status
,
65 content::PersistentNotificationStatus::
66 PERSISTENT_NOTIFICATION_STATUS_MAX
);
69 void CancelNotification(const std::string
& id
, ProfileID profile_id
) {
70 PlatformNotificationServiceImpl::GetInstance()
71 ->GetNotificationUIManager()->CancelById(id
, profile_id
);
77 PlatformNotificationServiceImpl
*
78 PlatformNotificationServiceImpl::GetInstance() {
79 return base::Singleton
<PlatformNotificationServiceImpl
>::get();
82 PlatformNotificationServiceImpl::PlatformNotificationServiceImpl()
83 : notification_ui_manager_for_tests_(nullptr) {}
85 PlatformNotificationServiceImpl::~PlatformNotificationServiceImpl() {}
87 void PlatformNotificationServiceImpl::OnPersistentNotificationClick(
88 BrowserContext
* browser_context
,
89 int64_t persistent_notification_id
,
91 int action_index
) const {
92 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
93 content::NotificationEventDispatcher::GetInstance()
94 ->DispatchNotificationClickEvent(
96 persistent_notification_id
,
99 base::Bind(&OnEventDispatchComplete
));
102 void PlatformNotificationServiceImpl::OnPersistentNotificationClose(
103 BrowserContext
* browser_context
,
104 int64_t persistent_notification_id
,
105 const GURL
& origin
) const {
106 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
107 PlatformNotificationContext
* context
=
108 BrowserContext::GetStoragePartitionForSite(browser_context
, origin
)
109 ->GetPlatformNotificationContext();
111 BrowserThread::PostTask(
114 base::Bind(&PlatformNotificationContext::DeleteNotificationData
,
116 persistent_notification_id
,
118 base::Bind(&OnPersistentNotificationDataDeleted
)));
121 blink::WebNotificationPermission
122 PlatformNotificationServiceImpl::CheckPermissionOnUIThread(
123 BrowserContext
* browser_context
,
125 int render_process_id
) {
126 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
128 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
131 #if defined(ENABLE_EXTENSIONS)
132 // Extensions support an API permission named "notification". This will grant
133 // not only grant permission for using the Chrome App extension API, but also
134 // for the Web Notification API.
135 if (origin
.SchemeIs(extensions::kExtensionScheme
)) {
136 extensions::ExtensionRegistry
* registry
=
137 extensions::ExtensionRegistry::Get(browser_context
);
138 extensions::ProcessMap
* process_map
=
139 extensions::ProcessMap::Get(browser_context
);
141 const extensions::Extension
* extension
=
142 registry
->GetExtensionById(origin
.host(),
143 extensions::ExtensionRegistry::ENABLED
);
146 extension
->permissions_data()->HasAPIPermission(
147 extensions::APIPermission::kNotifications
) &&
148 process_map
->Contains(extension
->id(), render_process_id
)) {
149 NotifierStateTracker
* notifier_state_tracker
=
150 NotifierStateTrackerFactory::GetForProfile(profile
);
151 DCHECK(notifier_state_tracker
);
153 NotifierId
notifier_id(NotifierId::APPLICATION
, extension
->id());
154 if (notifier_state_tracker
->IsNotifierEnabled(notifier_id
))
155 return blink::WebNotificationPermissionAllowed
;
160 ContentSetting setting
=
161 DesktopNotificationProfileUtil::GetContentSetting(profile
, origin
);
163 if (setting
== CONTENT_SETTING_ALLOW
)
164 return blink::WebNotificationPermissionAllowed
;
165 if (setting
== CONTENT_SETTING_BLOCK
)
166 return blink::WebNotificationPermissionDenied
;
168 return blink::WebNotificationPermissionDefault
;
171 blink::WebNotificationPermission
172 PlatformNotificationServiceImpl::CheckPermissionOnIOThread(
173 content::ResourceContext
* resource_context
,
175 int render_process_id
) {
176 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
178 ProfileIOData
* io_data
= ProfileIOData::FromResourceContext(resource_context
);
179 #if defined(ENABLE_EXTENSIONS)
180 // Extensions support an API permission named "notification". This will grant
181 // not only grant permission for using the Chrome App extension API, but also
182 // for the Web Notification API.
183 if (origin
.SchemeIs(extensions::kExtensionScheme
)) {
184 extensions::InfoMap
* extension_info_map
= io_data
->GetExtensionInfoMap();
185 const extensions::ProcessMap
& process_map
=
186 extension_info_map
->process_map();
188 const extensions::Extension
* extension
=
189 extension_info_map
->extensions().GetByID(origin
.host());
192 extension
->permissions_data()->HasAPIPermission(
193 extensions::APIPermission::kNotifications
) &&
194 process_map
.Contains(extension
->id(), render_process_id
)) {
195 if (!extension_info_map
->AreNotificationsDisabled(extension
->id()))
196 return blink::WebNotificationPermissionAllowed
;
201 // No enabled extensions exist, so check the normal host content settings.
202 HostContentSettingsMap
* host_content_settings_map
=
203 io_data
->GetHostContentSettingsMap();
204 ContentSetting setting
= host_content_settings_map
->GetContentSetting(
207 CONTENT_SETTINGS_TYPE_NOTIFICATIONS
,
208 content_settings::ResourceIdentifier());
210 if (setting
== CONTENT_SETTING_ALLOW
)
211 return blink::WebNotificationPermissionAllowed
;
212 if (setting
== CONTENT_SETTING_BLOCK
)
213 return blink::WebNotificationPermissionDenied
;
215 return blink::WebNotificationPermissionDefault
;
218 void PlatformNotificationServiceImpl::DisplayNotification(
219 BrowserContext
* browser_context
,
221 const SkBitmap
& icon
,
222 const content::PlatformNotificationData
& notification_data
,
223 scoped_ptr
<content::DesktopNotificationDelegate
> delegate
,
224 base::Closure
* cancel_callback
) {
225 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
227 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
230 NotificationObjectProxy
* proxy
= new NotificationObjectProxy(delegate
.Pass());
231 Notification notification
= CreateNotificationFromData(
232 profile
, origin
, icon
, notification_data
, proxy
);
234 GetNotificationUIManager()->Add(notification
, profile
);
237 base::Bind(&CancelNotification
,
238 notification
.delegate_id(),
239 NotificationUIManager::GetProfileID(profile
));
241 profile
->GetHostContentSettingsMap()->UpdateLastUsage(
242 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
245 void PlatformNotificationServiceImpl::DisplayPersistentNotification(
246 BrowserContext
* browser_context
,
247 int64_t persistent_notification_id
,
249 const SkBitmap
& icon
,
250 const content::PlatformNotificationData
& notification_data
) {
251 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
253 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
256 PersistentNotificationDelegate
* delegate
= new PersistentNotificationDelegate(
257 browser_context
, persistent_notification_id
, origin
);
259 Notification notification
= CreateNotificationFromData(
260 profile
, origin
, icon
, notification_data
, delegate
);
262 // TODO(peter): Remove this mapping when we have reliable id generation for
263 // the message_center::Notification objects.
264 persistent_notifications_
[persistent_notification_id
] = notification
.id();
266 GetNotificationUIManager()->Add(notification
, profile
);
268 profile
->GetHostContentSettingsMap()->UpdateLastUsage(
269 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
272 void PlatformNotificationServiceImpl::ClosePersistentNotification(
273 BrowserContext
* browser_context
,
274 int64_t persistent_notification_id
) {
275 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
277 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
280 #if defined(OS_ANDROID)
281 // TODO(peter): Remove this conversion when the notification ids are being
282 // generated by the caller of this method.
283 std::string textual_persistent_notification_id
=
284 base::Int64ToString(persistent_notification_id
);
285 GetNotificationUIManager()->CancelById(
286 textual_persistent_notification_id
,
287 NotificationUIManager::GetProfileID(profile
));
289 auto iter
= persistent_notifications_
.find(persistent_notification_id
);
290 if (iter
== persistent_notifications_
.end())
293 GetNotificationUIManager()->CancelById(
294 iter
->second
, NotificationUIManager::GetProfileID(profile
));
296 persistent_notifications_
.erase(iter
);
300 bool PlatformNotificationServiceImpl::GetDisplayedPersistentNotifications(
301 BrowserContext
* browser_context
,
302 std::set
<std::string
>* displayed_notifications
) {
303 DCHECK(displayed_notifications
);
305 #if !defined(OS_ANDROID)
306 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
307 if (!profile
|| profile
->AsTestingProfile())
308 return false; // Tests will not have a message center.
310 // TODO(peter): Filter for persistent notifications only.
311 *displayed_notifications
= GetNotificationUIManager()->GetAllIdsByProfile(
312 NotificationUIManager::GetProfileID(profile
));
316 // Android cannot reliably return the notifications that are currently being
317 // displayed on the platform, see the comment in NotificationUIManagerAndroid.
319 #endif // !defined(OS_ANDROID)
322 Notification
PlatformNotificationServiceImpl::CreateNotificationFromData(
325 const SkBitmap
& icon
,
326 const content::PlatformNotificationData
& notification_data
,
327 NotificationDelegate
* delegate
) const {
328 // TODO(peter): Icons for Web Notifications are currently always requested for
329 // 1x scale, whereas the displays on which they can be displayed can have a
330 // different pixel density. Be smarter about this when the API gets updated
331 // with a way for developers to specify images of different resolutions.
332 Notification
notification(
333 origin
, notification_data
.title
, notification_data
.body
,
334 gfx::Image::CreateFrom1xBitmap(icon
), base::UTF8ToUTF16(origin
.host()),
335 notification_data
.tag
, delegate
);
337 notification
.set_context_message(
338 DisplayNameForContextMessage(profile
, origin
));
339 notification
.set_vibration_pattern(notification_data
.vibration_pattern
);
340 notification
.set_silent(notification_data
.silent
);
342 std::vector
<message_center::ButtonInfo
> buttons
;
343 for (const auto& action
: notification_data
.actions
)
344 buttons
.push_back(message_center::ButtonInfo(action
.title
));
346 notification
.set_buttons(buttons
);
348 // On desktop, notifications with require_interaction==true stay on-screen
349 // rather than minimizing to the notification center after a timeout.
350 // On mobile, this is ignored (notifications are minimized at all times).
351 if (notification_data
.require_interaction
||
352 !base::CommandLine::ForCurrentProcess()->HasSwitch(
353 switches::kEnableExperimentalWebPlatformFeatures
)) {
354 notification
.set_never_timeout(true);
360 NotificationUIManager
*
361 PlatformNotificationServiceImpl::GetNotificationUIManager() const {
362 if (notification_ui_manager_for_tests_
)
363 return notification_ui_manager_for_tests_
;
365 return g_browser_process
->notification_ui_manager();
368 void PlatformNotificationServiceImpl::SetNotificationUIManagerForTesting(
369 NotificationUIManager
* manager
) {
370 notification_ui_manager_for_tests_
= manager
;
373 base::string16
PlatformNotificationServiceImpl::DisplayNameForContextMessage(
375 const GURL
& origin
) const {
376 #if defined(ENABLE_EXTENSIONS)
377 // If the source is an extension, lookup the display name.
378 if (origin
.SchemeIs(extensions::kExtensionScheme
)) {
379 const extensions::Extension
* extension
=
380 extensions::ExtensionRegistry::Get(profile
)->GetExtensionById(
381 origin
.host(), extensions::ExtensionRegistry::EVERYTHING
);
384 return base::UTF8ToUTF16(extension
->name());
388 return base::string16();