Prepare for VS2015 toolchain
[chromium-blink-merge.git] / chrome / browser / notifications / platform_notification_service_impl.cc
blobbe6db4c970444e312fc2fded2c7fd7479c78499d
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/metrics/histogram_macros.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/notifications/desktop_notification_profile_util.h"
12 #include "chrome/browser/notifications/notification_object_proxy.h"
13 #include "chrome/browser/notifications/notification_ui_manager.h"
14 #include "chrome/browser/notifications/persistent_notification_delegate.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/profiles/profile_io_data.h"
17 #include "chrome/common/pref_names.h"
18 #include "components/content_settings/core/browser/host_content_settings_map.h"
19 #include "components/content_settings/core/common/content_settings.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/desktop_notification_delegate.h"
22 #include "content/public/browser/notification_event_dispatcher.h"
23 #include "content/public/browser/platform_notification_context.h"
24 #include "content/public/browser/storage_partition.h"
25 #include "content/public/common/platform_notification_data.h"
26 #include "net/base/net_util.h"
27 #include "ui/message_center/notifier_settings.h"
28 #include "url/url_constants.h"
30 #if defined(ENABLE_EXTENSIONS)
31 #include "chrome/browser/notifications/desktop_notification_service.h"
32 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
33 #include "extensions/browser/extension_registry.h"
34 #include "extensions/browser/extension_system.h"
35 #include "extensions/browser/info_map.h"
36 #include "extensions/common/constants.h"
37 #include "extensions/common/extension_set.h"
38 #include "extensions/common/permissions/api_permission.h"
39 #include "extensions/common/permissions/permissions_data.h"
40 #endif
42 #if defined(OS_ANDROID)
43 #include "base/strings/string_number_conversions.h"
44 #endif
46 using content::BrowserContext;
47 using content::BrowserThread;
48 using content::PlatformNotificationContext;
49 using message_center::NotifierId;
51 namespace {
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",
57 success);
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);
74 } // namespace
76 // static
77 PlatformNotificationServiceImpl*
78 PlatformNotificationServiceImpl::GetInstance() {
79 return 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,
90 const GURL& origin) const {
91 DCHECK_CURRENTLY_ON(BrowserThread::UI);
92 content::NotificationEventDispatcher::GetInstance()
93 ->DispatchNotificationClickEvent(
94 browser_context,
95 persistent_notification_id,
96 origin,
97 base::Bind(&OnEventDispatchComplete));
100 void PlatformNotificationServiceImpl::OnPersistentNotificationClose(
101 BrowserContext* browser_context,
102 int64_t persistent_notification_id,
103 const GURL& origin) const {
104 DCHECK_CURRENTLY_ON(BrowserThread::UI);
105 PlatformNotificationContext* context =
106 BrowserContext::GetStoragePartitionForSite(browser_context, origin)
107 ->GetPlatformNotificationContext();
109 BrowserThread::PostTask(
110 BrowserThread::IO,
111 FROM_HERE,
112 base::Bind(&PlatformNotificationContext::DeleteNotificationData,
113 context,
114 persistent_notification_id,
115 origin,
116 base::Bind(&OnPersistentNotificationDataDeleted)));
119 blink::WebNotificationPermission
120 PlatformNotificationServiceImpl::CheckPermissionOnUIThread(
121 BrowserContext* browser_context,
122 const GURL& origin,
123 int render_process_id) {
124 DCHECK_CURRENTLY_ON(BrowserThread::UI);
126 Profile* profile = Profile::FromBrowserContext(browser_context);
127 DCHECK(profile);
129 #if defined(ENABLE_EXTENSIONS)
130 // Extensions support an API permission named "notification". This will grant
131 // not only grant permission for using the Chrome App extension API, but also
132 // for the Web Notification API.
133 extensions::ExtensionRegistry* registry =
134 extensions::ExtensionRegistry::Get(browser_context);
135 extensions::ProcessMap* process_map =
136 extensions::ProcessMap::Get(browser_context);
137 extensions::ExtensionSet extensions;
139 DesktopNotificationService* desktop_notification_service =
140 DesktopNotificationServiceFactory::GetForProfile(profile);
141 DCHECK(desktop_notification_service);
143 // If |origin| is an enabled extension, only select that one. Otherwise select
144 // all extensions whose web content matches |origin|.
145 if (origin.SchemeIs(extensions::kExtensionScheme)) {
146 const extensions::Extension* extension = registry->GetExtensionById(
147 origin.host(), extensions::ExtensionRegistry::ENABLED);
148 if (extension)
149 extensions.Insert(extension);
150 } else {
151 for (const auto& extension : registry->enabled_extensions()) {
152 if (extension->web_extent().MatchesSecurityOrigin(origin))
153 extensions.Insert(extension);
157 // Check if any of the selected extensions have the "notification" API
158 // permission, are active in |render_process_id| and has not been manually
159 // disabled by the user. If all of that is true, grant permission.
160 for (const auto& extension : extensions) {
161 if (!extension->permissions_data()->HasAPIPermission(
162 extensions::APIPermission::kNotifications))
163 continue;
165 if (!process_map->Contains(extension->id(), render_process_id))
166 continue;
168 NotifierId notifier_id(NotifierId::APPLICATION, extension->id());
169 if (!desktop_notification_service->IsNotifierEnabled(notifier_id))
170 continue;
172 return blink::WebNotificationPermissionAllowed;
174 #endif
176 ContentSetting setting =
177 DesktopNotificationProfileUtil::GetContentSetting(profile, origin);
179 if (setting == CONTENT_SETTING_ALLOW)
180 return blink::WebNotificationPermissionAllowed;
181 if (setting == CONTENT_SETTING_BLOCK)
182 return blink::WebNotificationPermissionDenied;
184 return blink::WebNotificationPermissionDefault;
187 blink::WebNotificationPermission
188 PlatformNotificationServiceImpl::CheckPermissionOnIOThread(
189 content::ResourceContext* resource_context,
190 const GURL& origin,
191 int render_process_id) {
192 DCHECK_CURRENTLY_ON(BrowserThread::IO);
194 ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
195 #if defined(ENABLE_EXTENSIONS)
196 extensions::InfoMap* extension_info_map = io_data->GetExtensionInfoMap();
198 // We want to see if there is an extension that hasn't been manually disabled
199 // that has the notifications permission and applies to this security origin.
200 // First, get the list of extensions with permission for the origin.
201 extensions::ExtensionSet extensions;
202 extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin(
203 origin,
204 render_process_id,
205 extensions::APIPermission::kNotifications,
206 &extensions);
207 for (const auto& extension : extensions) {
208 if (!extension_info_map->AreNotificationsDisabled(extension->id()))
209 return blink::WebNotificationPermissionAllowed;
211 #endif
213 // No enabled extensions exist, so check the normal host content settings.
214 HostContentSettingsMap* host_content_settings_map =
215 io_data->GetHostContentSettingsMap();
216 ContentSetting setting = host_content_settings_map->GetContentSetting(
217 origin,
218 origin,
219 CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
220 content_settings::ResourceIdentifier());
222 if (setting == CONTENT_SETTING_ALLOW)
223 return blink::WebNotificationPermissionAllowed;
224 if (setting == CONTENT_SETTING_BLOCK)
225 return blink::WebNotificationPermissionDenied;
227 return blink::WebNotificationPermissionDefault;
230 void PlatformNotificationServiceImpl::DisplayNotification(
231 BrowserContext* browser_context,
232 const GURL& origin,
233 const SkBitmap& icon,
234 const content::PlatformNotificationData& notification_data,
235 scoped_ptr<content::DesktopNotificationDelegate> delegate,
236 base::Closure* cancel_callback) {
237 DCHECK_CURRENTLY_ON(BrowserThread::UI);
239 Profile* profile = Profile::FromBrowserContext(browser_context);
240 DCHECK(profile);
242 NotificationObjectProxy* proxy = new NotificationObjectProxy(delegate.Pass());
243 Notification notification = CreateNotificationFromData(
244 profile, origin, icon, notification_data, proxy);
246 GetNotificationUIManager()->Add(notification, profile);
247 if (cancel_callback)
248 *cancel_callback =
249 base::Bind(&CancelNotification,
250 notification.delegate_id(),
251 NotificationUIManager::GetProfileID(profile));
253 profile->GetHostContentSettingsMap()->UpdateLastUsage(
254 origin, origin, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
257 void PlatformNotificationServiceImpl::DisplayPersistentNotification(
258 BrowserContext* browser_context,
259 int64_t persistent_notification_id,
260 const GURL& origin,
261 const SkBitmap& icon,
262 const content::PlatformNotificationData& notification_data) {
263 DCHECK_CURRENTLY_ON(BrowserThread::UI);
265 Profile* profile = Profile::FromBrowserContext(browser_context);
266 DCHECK(profile);
268 PersistentNotificationDelegate* delegate = new PersistentNotificationDelegate(
269 browser_context, persistent_notification_id, origin);
271 Notification notification = CreateNotificationFromData(
272 profile, origin, icon, notification_data, delegate);
274 // TODO(peter): Remove this mapping when we have reliable id generation for
275 // the message_center::Notification objects.
276 persistent_notifications_[persistent_notification_id] = notification.id();
278 GetNotificationUIManager()->Add(notification, profile);
280 profile->GetHostContentSettingsMap()->UpdateLastUsage(
281 origin, origin, CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
284 void PlatformNotificationServiceImpl::ClosePersistentNotification(
285 BrowserContext* browser_context,
286 int64_t persistent_notification_id) {
287 DCHECK_CURRENTLY_ON(BrowserThread::UI);
289 Profile* profile = Profile::FromBrowserContext(browser_context);
290 DCHECK(profile);
292 #if defined(OS_ANDROID)
293 // TODO(peter): Remove this conversion when the notification ids are being
294 // generated by the caller of this method.
295 std::string textual_persistent_notification_id =
296 base::Int64ToString(persistent_notification_id);
297 GetNotificationUIManager()->CancelById(
298 textual_persistent_notification_id,
299 NotificationUIManager::GetProfileID(profile));
300 #else
301 auto iter = persistent_notifications_.find(persistent_notification_id);
302 if (iter == persistent_notifications_.end())
303 return;
305 GetNotificationUIManager()->CancelById(
306 iter->second, NotificationUIManager::GetProfileID(profile));
308 persistent_notifications_.erase(iter);
309 #endif
312 bool PlatformNotificationServiceImpl::GetDisplayedPersistentNotifications(
313 BrowserContext* browser_context,
314 std::set<std::string>* displayed_notifications) {
315 DCHECK(displayed_notifications);
317 #if !defined(OS_ANDROID)
318 Profile* profile = Profile::FromBrowserContext(browser_context);
319 if (!profile || profile->AsTestingProfile())
320 return false; // Tests will not have a message center.
322 // TODO(peter): Filter for persistent notifications only.
323 *displayed_notifications =
324 GetNotificationUIManager()->GetAllIdsByProfile(profile);
326 return true;
327 #else
328 // Android cannot reliably return the notifications that are currently being
329 // displayed on the platform, see the comment in NotificationUIManagerAndroid.
330 return false;
331 #endif // !defined(OS_ANDROID)
334 Notification PlatformNotificationServiceImpl::CreateNotificationFromData(
335 Profile* profile,
336 const GURL& origin,
337 const SkBitmap& icon,
338 const content::PlatformNotificationData& notification_data,
339 NotificationDelegate* delegate) const {
340 base::string16 display_source = DisplayNameForOrigin(profile, origin);
342 // TODO(peter): Icons for Web Notifications are currently always requested for
343 // 1x scale, whereas the displays on which they can be displayed can have a
344 // different pixel density. Be smarter about this when the API gets updated
345 // with a way for developers to specify images of different resolutions.
346 Notification notification(origin, notification_data.title,
347 notification_data.body, gfx::Image::CreateFrom1xBitmap(icon),
348 display_source, notification_data.tag, delegate);
350 notification.set_context_message(display_source);
351 notification.set_vibration_pattern(notification_data.vibration_pattern);
352 notification.set_silent(notification_data.silent);
354 // Web Notifications do not timeout.
355 notification.set_never_timeout(true);
357 return notification;
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::DisplayNameForOrigin(
374 Profile* profile,
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);
382 DCHECK(extension);
384 return base::UTF8ToUTF16(extension->name());
386 #endif
388 std::string languages =
389 profile->GetPrefs()->GetString(prefs::kAcceptLanguages);
391 return WebOriginDisplayName(origin, languages);
394 // static
395 base::string16 PlatformNotificationServiceImpl::WebOriginDisplayName(
396 const GURL& origin,
397 const std::string& languages) {
398 if (origin.SchemeIsHTTPOrHTTPS()) {
399 base::string16 formatted_origin;
400 if (origin.SchemeIs(url::kHttpScheme)) {
401 const url::Parsed& parsed = origin.parsed_for_possibly_invalid_spec();
402 const std::string& spec = origin.possibly_invalid_spec();
403 formatted_origin.append(
404 spec.begin(),
405 spec.begin() +
406 parsed.CountCharactersBefore(url::Parsed::USERNAME, true));
408 formatted_origin.append(net::IDNToUnicode(origin.host(), languages));
409 if (origin.has_port()) {
410 formatted_origin.push_back(':');
411 formatted_origin.append(base::UTF8ToUTF16(origin.port()));
413 return formatted_origin;
416 // TODO(dewittj): Once file:// URLs are passed in to the origin
417 // GURL here, begin returning the path as the display name.
418 return net::FormatUrl(origin, languages);