Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / notifications / platform_notification_service_impl.cc
blob95bc0e64f1daacdb621ef02a9416ffbf1bbdce49
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"
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,
91 int action_index) const {
92 DCHECK_CURRENTLY_ON(BrowserThread::UI);
93 content::NotificationEventDispatcher::GetInstance()
94 ->DispatchNotificationClickEvent(
95 browser_context,
96 persistent_notification_id,
97 origin,
98 action_index,
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(
112 BrowserThread::IO,
113 FROM_HERE,
114 base::Bind(&PlatformNotificationContext::DeleteNotificationData,
115 context,
116 persistent_notification_id,
117 origin,
118 base::Bind(&OnPersistentNotificationDataDeleted)));
121 blink::WebNotificationPermission
122 PlatformNotificationServiceImpl::CheckPermissionOnUIThread(
123 BrowserContext* browser_context,
124 const GURL& origin,
125 int render_process_id) {
126 DCHECK_CURRENTLY_ON(BrowserThread::UI);
128 Profile* profile = Profile::FromBrowserContext(browser_context);
129 DCHECK(profile);
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);
145 if (extension &&
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;
158 #endif
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,
174 const GURL& origin,
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());
191 if (extension &&
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;
199 #endif
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(
205 origin,
206 origin,
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,
220 const GURL& origin,
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);
228 DCHECK(profile);
230 NotificationObjectProxy* proxy = new NotificationObjectProxy(delegate.Pass());
231 Notification notification = CreateNotificationFromData(
232 profile, origin, icon, notification_data, proxy);
234 GetNotificationUIManager()->Add(notification, profile);
235 if (cancel_callback)
236 *cancel_callback =
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,
248 const GURL& origin,
249 const SkBitmap& icon,
250 const content::PlatformNotificationData& notification_data) {
251 DCHECK_CURRENTLY_ON(BrowserThread::UI);
253 Profile* profile = Profile::FromBrowserContext(browser_context);
254 DCHECK(profile);
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);
278 DCHECK(profile);
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));
288 #else
289 auto iter = persistent_notifications_.find(persistent_notification_id);
290 if (iter == persistent_notifications_.end())
291 return;
293 GetNotificationUIManager()->CancelById(
294 iter->second, NotificationUIManager::GetProfileID(profile));
296 persistent_notifications_.erase(iter);
297 #endif
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));
314 return true;
315 #else
316 // Android cannot reliably return the notifications that are currently being
317 // displayed on the platform, see the comment in NotificationUIManagerAndroid.
318 return false;
319 #endif // !defined(OS_ANDROID)
322 Notification PlatformNotificationServiceImpl::CreateNotificationFromData(
323 Profile* profile,
324 const GURL& origin,
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);
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::DisplayNameForContextMessage(
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 return base::string16();