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/prefs/pref_service.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/notifications/desktop_notification_profile_util.h"
11 #include "chrome/browser/notifications/notification_object_proxy.h"
12 #include "chrome/browser/notifications/notification_ui_manager.h"
13 #include "chrome/browser/notifications/persistent_notification_delegate.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/profiles/profile_io_data.h"
16 #include "chrome/common/pref_names.h"
17 #include "components/content_settings/core/browser/host_content_settings_map.h"
18 #include "components/content_settings/core/common/content_settings.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/desktop_notification_delegate.h"
21 #include "content/public/browser/notification_event_dispatcher.h"
22 #include "content/public/common/platform_notification_data.h"
23 #include "net/base/net_util.h"
24 #include "ui/message_center/notifier_settings.h"
25 #include "url/url_constants.h"
27 #if defined(ENABLE_EXTENSIONS)
28 #include "chrome/browser/notifications/desktop_notification_service.h"
29 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
30 #include "extensions/browser/extension_registry.h"
31 #include "extensions/browser/extension_system.h"
32 #include "extensions/browser/info_map.h"
33 #include "extensions/common/constants.h"
34 #include "extensions/common/extension_set.h"
35 #include "extensions/common/permissions/api_permission.h"
36 #include "extensions/common/permissions/permissions_data.h"
39 using content::BrowserThread
;
40 using message_center::NotifierId
;
44 void CancelNotification(const std::string
& id
, ProfileID profile_id
) {
45 PlatformNotificationServiceImpl::GetInstance()
46 ->GetNotificationUIManager()->CancelById(id
, profile_id
);
52 PlatformNotificationServiceImpl
*
53 PlatformNotificationServiceImpl::GetInstance() {
54 return Singleton
<PlatformNotificationServiceImpl
>::get();
57 PlatformNotificationServiceImpl::PlatformNotificationServiceImpl()
58 : notification_ui_manager_for_tests_(nullptr) {}
60 PlatformNotificationServiceImpl::~PlatformNotificationServiceImpl() {}
62 void PlatformNotificationServiceImpl::OnPersistentNotificationClick(
63 content::BrowserContext
* browser_context
,
64 int64 service_worker_registration_id
,
65 const std::string
& notification_id
,
67 const content::PlatformNotificationData
& notification_data
,
68 const base::Callback
<void(content::PersistentNotificationStatus
)>&
70 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
71 content::NotificationEventDispatcher::GetInstance()
72 ->DispatchNotificationClickEvent(
75 service_worker_registration_id
,
81 blink::WebNotificationPermission
82 PlatformNotificationServiceImpl::CheckPermissionOnUIThread(
83 content::BrowserContext
* browser_context
,
85 int render_process_id
) {
86 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
88 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
91 #if defined(ENABLE_EXTENSIONS)
92 // Extensions support an API permission named "notification". This will grant
93 // not only grant permission for using the Chrome App extension API, but also
94 // for the Web Notification API.
95 extensions::ExtensionRegistry
* registry
=
96 extensions::ExtensionRegistry::Get(browser_context
);
97 extensions::ProcessMap
* process_map
=
98 extensions::ProcessMap::Get(browser_context
);
99 extensions::ExtensionSet extensions
;
101 DesktopNotificationService
* desktop_notification_service
=
102 DesktopNotificationServiceFactory::GetForProfile(profile
);
103 DCHECK(desktop_notification_service
);
105 // If |origin| is an enabled extension, only select that one. Otherwise select
106 // all extensions whose web content matches |origin|.
107 if (origin
.SchemeIs(extensions::kExtensionScheme
)) {
108 const extensions::Extension
* extension
= registry
->GetExtensionById(
109 origin
.host(), extensions::ExtensionRegistry::ENABLED
);
111 extensions
.Insert(extension
);
113 for (const auto& extension
: registry
->enabled_extensions()) {
114 if (extension
->web_extent().MatchesSecurityOrigin(origin
))
115 extensions
.Insert(extension
);
119 // Check if any of the selected extensions have the "notification" API
120 // permission, are active in |render_process_id| and has not been manually
121 // disabled by the user. If all of that is true, grant permission.
122 for (const auto& extension
: extensions
) {
123 if (!extension
->permissions_data()->HasAPIPermission(
124 extensions::APIPermission::kNotifications
))
127 if (!process_map
->Contains(extension
->id(), render_process_id
))
130 NotifierId
notifier_id(NotifierId::APPLICATION
, extension
->id());
131 if (!desktop_notification_service
->IsNotifierEnabled(notifier_id
))
134 return blink::WebNotificationPermissionAllowed
;
138 ContentSetting setting
=
139 DesktopNotificationProfileUtil::GetContentSetting(profile
, origin
);
141 if (setting
== CONTENT_SETTING_ALLOW
)
142 return blink::WebNotificationPermissionAllowed
;
143 if (setting
== CONTENT_SETTING_BLOCK
)
144 return blink::WebNotificationPermissionDenied
;
146 return blink::WebNotificationPermissionDefault
;
149 blink::WebNotificationPermission
150 PlatformNotificationServiceImpl::CheckPermissionOnIOThread(
151 content::ResourceContext
* resource_context
,
153 int render_process_id
) {
154 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
156 ProfileIOData
* io_data
= ProfileIOData::FromResourceContext(resource_context
);
157 #if defined(ENABLE_EXTENSIONS)
158 extensions::InfoMap
* extension_info_map
= io_data
->GetExtensionInfoMap();
160 // We want to see if there is an extension that hasn't been manually disabled
161 // that has the notifications permission and applies to this security origin.
162 // First, get the list of extensions with permission for the origin.
163 extensions::ExtensionSet extensions
;
164 extension_info_map
->GetExtensionsWithAPIPermissionForSecurityOrigin(
167 extensions::APIPermission::kNotifications
,
169 for (const auto& extension
: extensions
) {
170 if (!extension_info_map
->AreNotificationsDisabled(extension
->id()))
171 return blink::WebNotificationPermissionAllowed
;
175 // No enabled extensions exist, so check the normal host content settings.
176 HostContentSettingsMap
* host_content_settings_map
=
177 io_data
->GetHostContentSettingsMap();
178 ContentSetting setting
= host_content_settings_map
->GetContentSetting(
181 CONTENT_SETTINGS_TYPE_NOTIFICATIONS
,
182 NO_RESOURCE_IDENTIFIER
);
184 if (setting
== CONTENT_SETTING_ALLOW
)
185 return blink::WebNotificationPermissionAllowed
;
186 if (setting
== CONTENT_SETTING_BLOCK
)
187 return blink::WebNotificationPermissionDenied
;
189 return blink::WebNotificationPermissionDefault
;
192 void PlatformNotificationServiceImpl::DisplayNotification(
193 content::BrowserContext
* browser_context
,
195 const SkBitmap
& icon
,
196 const content::PlatformNotificationData
& notification_data
,
197 scoped_ptr
<content::DesktopNotificationDelegate
> delegate
,
198 base::Closure
* cancel_callback
) {
199 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
201 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
204 NotificationObjectProxy
* proxy
= new NotificationObjectProxy(delegate
.Pass());
205 Notification notification
= CreateNotificationFromData(
206 profile
, origin
, icon
, notification_data
, proxy
);
208 GetNotificationUIManager()->Add(notification
, profile
);
211 base::Bind(&CancelNotification
,
212 notification
.delegate_id(),
213 NotificationUIManager::GetProfileID(profile
));
215 profile
->GetHostContentSettingsMap()->UpdateLastUsage(
216 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
219 void PlatformNotificationServiceImpl::DisplayPersistentNotification(
220 content::BrowserContext
* browser_context
,
221 int64 service_worker_registration_id
,
223 const SkBitmap
& icon
,
224 const content::PlatformNotificationData
& notification_data
) {
225 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
227 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
230 PersistentNotificationDelegate
* delegate
= new PersistentNotificationDelegate(
232 service_worker_registration_id
,
236 Notification notification
= CreateNotificationFromData(
237 profile
, origin
, icon
, notification_data
, delegate
);
239 GetNotificationUIManager()->Add(notification
, profile
);
241 profile
->GetHostContentSettingsMap()->UpdateLastUsage(
242 origin
, origin
, CONTENT_SETTINGS_TYPE_NOTIFICATIONS
);
245 void PlatformNotificationServiceImpl::ClosePersistentNotification(
246 content::BrowserContext
* browser_context
,
247 const std::string
& persistent_notification_id
) {
248 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
250 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
253 GetNotificationUIManager()->CancelById(
254 persistent_notification_id
, NotificationUIManager::GetProfileID(profile
));
257 Notification
PlatformNotificationServiceImpl::CreateNotificationFromData(
260 const SkBitmap
& icon
,
261 const content::PlatformNotificationData
& notification_data
,
262 NotificationDelegate
* delegate
) const {
263 base::string16 display_source
= DisplayNameForOrigin(profile
, origin
);
265 // TODO(peter): Icons for Web Notifications are currently always requested for
266 // 1x scale, whereas the displays on which they can be displayed can have a
267 // different pixel density. Be smarter about this when the API gets updated
268 // with a way for developers to specify images of different resolutions.
269 Notification
notification(origin
, notification_data
.title
,
270 notification_data
.body
, gfx::Image::CreateFrom1xBitmap(icon
),
271 display_source
, notification_data
.tag
, delegate
);
273 notification
.set_context_message(display_source
);
274 notification
.set_silent(notification_data
.silent
);
276 // Web Notifications do not timeout.
277 notification
.set_never_timeout(true);
282 NotificationUIManager
*
283 PlatformNotificationServiceImpl::GetNotificationUIManager() const {
284 if (notification_ui_manager_for_tests_
)
285 return notification_ui_manager_for_tests_
;
287 return g_browser_process
->notification_ui_manager();
290 void PlatformNotificationServiceImpl::SetNotificationUIManagerForTesting(
291 NotificationUIManager
* manager
) {
292 notification_ui_manager_for_tests_
= manager
;
295 base::string16
PlatformNotificationServiceImpl::DisplayNameForOrigin(
297 const GURL
& origin
) const {
298 #if defined(ENABLE_EXTENSIONS)
299 // If the source is an extension, lookup the display name.
300 if (origin
.SchemeIs(extensions::kExtensionScheme
)) {
301 const extensions::Extension
* extension
=
302 extensions::ExtensionRegistry::Get(profile
)->GetExtensionById(
303 origin
.host(), extensions::ExtensionRegistry::EVERYTHING
);
306 return base::UTF8ToUTF16(extension
->name());
310 std::string languages
=
311 profile
->GetPrefs()->GetString(prefs::kAcceptLanguages
);
313 return WebOriginDisplayName(origin
, languages
);
317 base::string16
PlatformNotificationServiceImpl::WebOriginDisplayName(
319 const std::string
& languages
) {
320 if (origin
.SchemeIsHTTPOrHTTPS()) {
321 base::string16 formatted_origin
;
322 if (origin
.SchemeIs(url::kHttpScheme
)) {
323 const url::Parsed
& parsed
= origin
.parsed_for_possibly_invalid_spec();
324 const std::string
& spec
= origin
.possibly_invalid_spec();
325 formatted_origin
.append(
328 parsed
.CountCharactersBefore(url::Parsed::USERNAME
, true));
330 formatted_origin
.append(net::IDNToUnicode(origin
.host(), languages
));
331 if (origin
.has_port()) {
332 formatted_origin
.push_back(':');
333 formatted_origin
.append(base::UTF8ToUTF16(origin
.port()));
335 return formatted_origin
;
338 // TODO(dewittj): Once file:// URLs are passed in to the origin
339 // GURL here, begin returning the path as the display name.
340 return net::FormatUrl(origin
, languages
);