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 "content/child/notifications/notification_manager.h"
9 #include "base/lazy_instance.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/threading/thread_local.h"
14 #include "content/child/notifications/notification_data_conversions.h"
15 #include "content/child/notifications/notification_dispatcher.h"
16 #include "content/child/service_worker/web_service_worker_registration_impl.h"
17 #include "content/child/thread_safe_sender.h"
18 #include "content/child/worker_task_runner.h"
19 #include "content/public/common/platform_notification_data.h"
20 #include "third_party/WebKit/public/platform/WebSerializedOrigin.h"
21 #include "third_party/WebKit/public/platform/modules/notifications/WebNotificationDelegate.h"
22 #include "third_party/skia/include/core/SkBitmap.h"
24 using blink::WebNotificationPermission
;
29 int CurrentWorkerId() {
30 return WorkerTaskRunner::Instance()->CurrentWorkerId();
35 static base::LazyInstance
<base::ThreadLocalPointer
<NotificationManager
>>::Leaky
36 g_notification_manager_tls
= LAZY_INSTANCE_INITIALIZER
;
38 NotificationManager::NotificationManager(
39 ThreadSafeSender
* thread_safe_sender
,
40 base::SingleThreadTaskRunner
* main_thread_task_runner
,
41 NotificationDispatcher
* notification_dispatcher
)
42 : thread_safe_sender_(thread_safe_sender
),
43 notification_dispatcher_(notification_dispatcher
),
44 pending_notifications_(main_thread_task_runner
) {
45 g_notification_manager_tls
.Pointer()->Set(this);
48 NotificationManager::~NotificationManager() {
49 g_notification_manager_tls
.Pointer()->Set(nullptr);
52 NotificationManager
* NotificationManager::ThreadSpecificInstance(
53 ThreadSafeSender
* thread_safe_sender
,
54 base::SingleThreadTaskRunner
* main_thread_task_runner
,
55 NotificationDispatcher
* notification_dispatcher
) {
56 if (g_notification_manager_tls
.Pointer()->Get())
57 return g_notification_manager_tls
.Pointer()->Get();
59 NotificationManager
* manager
= new NotificationManager(
60 thread_safe_sender
, main_thread_task_runner
, notification_dispatcher
);
61 if (CurrentWorkerId())
62 WorkerTaskRunner::Instance()->AddStopObserver(manager
);
66 void NotificationManager::OnWorkerRunLoopStopped() {
70 void NotificationManager::show(
71 const blink::WebSerializedOrigin
& origin
,
72 const blink::WebNotificationData
& notification_data
,
73 blink::WebNotificationDelegate
* delegate
) {
74 if (notification_data
.icon
.isEmpty()) {
75 DisplayPageNotification(origin
, notification_data
, delegate
, SkBitmap());
79 pending_notifications_
.FetchPageNotificationResources(
82 base::Bind(&NotificationManager::DisplayPageNotification
,
83 base::Unretained(this), // this owns |pending_notifications_|
89 void NotificationManager::showPersistent(
90 const blink::WebSerializedOrigin
& origin
,
91 const blink::WebNotificationData
& notification_data
,
92 blink::WebServiceWorkerRegistration
* service_worker_registration
,
93 blink::WebNotificationShowCallbacks
* callbacks
) {
94 DCHECK(service_worker_registration
);
95 int64_t service_worker_registration_id
=
96 static_cast<WebServiceWorkerRegistrationImpl
*>(
97 service_worker_registration
)->registration_id();
99 scoped_ptr
<blink::WebNotificationShowCallbacks
> owned_callbacks(callbacks
);
101 // Verify that the author-provided payload size does not exceed our limit.
102 // This is an implementation-defined limit to prevent abuse of notification
103 // data as a storage mechanism. A UMA histogram records the requested sizes,
104 // which enables us to track how much data authors are attempting to store.
106 // If the size exceeds this limit, reject the showNotification() promise. This
107 // is outside of the boundaries set by the specification, but it gives authors
108 // an indication that something has gone wrong.
109 size_t author_data_size
= notification_data
.data
.size();
110 UMA_HISTOGRAM_MEMORY_KB("Notifications.AuthorDataSizeKB",
111 static_cast<int>(ceil(author_data_size
/ 1024.0)));
113 if (author_data_size
> PlatformNotificationData::kMaximumDeveloperDataSize
) {
114 owned_callbacks
->onError();
118 if (notification_data
.icon
.isEmpty()) {
119 DisplayPersistentNotification(origin
,
121 service_worker_registration_id
,
122 owned_callbacks
.Pass(),
127 pending_notifications_
.FetchPersistentNotificationResources(
129 base::Bind(&NotificationManager::DisplayPersistentNotification
,
130 base::Unretained(this), // this owns |pending_notifications_|
133 service_worker_registration_id
,
134 base::Passed(&owned_callbacks
)));
137 void NotificationManager::getNotifications(
138 const blink::WebString
& filter_tag
,
139 blink::WebServiceWorkerRegistration
* service_worker_registration
,
140 blink::WebNotificationGetCallbacks
* callbacks
) {
141 DCHECK(service_worker_registration
);
144 WebServiceWorkerRegistrationImpl
* service_worker_registration_impl
=
145 static_cast<WebServiceWorkerRegistrationImpl
*>(
146 service_worker_registration
);
148 GURL origin
= GURL(service_worker_registration_impl
->scope()).GetOrigin();
149 int64_t service_worker_registration_id
=
150 service_worker_registration_impl
->registration_id();
152 // TODO(peter): GenerateNotificationId is more of a request id. Consider
153 // renaming the method in the NotificationDispatcher if this makes sense.
155 notification_dispatcher_
->GenerateNotificationId(CurrentWorkerId());
157 pending_get_notification_requests_
.AddWithID(callbacks
, request_id
);
159 thread_safe_sender_
->Send(
160 new PlatformNotificationHostMsg_GetNotifications(
162 service_worker_registration_id
,
164 base::UTF16ToUTF8(filter_tag
)));
167 void NotificationManager::close(blink::WebNotificationDelegate
* delegate
) {
168 if (pending_notifications_
.CancelPageNotificationFetches(delegate
))
171 for (auto& iter
: active_page_notifications_
) {
172 if (iter
.second
!= delegate
)
175 thread_safe_sender_
->Send(
176 new PlatformNotificationHostMsg_Close(iter
.first
));
177 active_page_notifications_
.erase(iter
.first
);
181 // It should not be possible for Blink to call close() on a Notification which
182 // does not exist in either the pending or active notification lists.
186 void NotificationManager::closePersistent(
187 const blink::WebSerializedOrigin
& origin
,
188 int64_t persistent_notification_id
) {
189 thread_safe_sender_
->Send(new PlatformNotificationHostMsg_ClosePersistent(
190 GURL(origin
.string()),
191 persistent_notification_id
));
194 void NotificationManager::notifyDelegateDestroyed(
195 blink::WebNotificationDelegate
* delegate
) {
196 if (pending_notifications_
.CancelPageNotificationFetches(delegate
))
199 for (auto& iter
: active_page_notifications_
) {
200 if (iter
.second
!= delegate
)
203 active_page_notifications_
.erase(iter
.first
);
208 WebNotificationPermission
NotificationManager::checkPermission(
209 const blink::WebSerializedOrigin
& origin
) {
210 WebNotificationPermission permission
=
211 blink::WebNotificationPermissionAllowed
;
212 thread_safe_sender_
->Send(new PlatformNotificationHostMsg_CheckPermission(
213 GURL(origin
.string()), &permission
));
218 bool NotificationManager::OnMessageReceived(const IPC::Message
& message
) {
220 IPC_BEGIN_MESSAGE_MAP(NotificationManager
, message
)
221 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidShow
, OnDidShow
);
222 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidShowPersistent
,
224 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClose
, OnDidClose
);
225 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClick
, OnDidClick
);
226 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidGetNotifications
,
227 OnDidGetNotifications
)
228 IPC_MESSAGE_UNHANDLED(handled
= false)
229 IPC_END_MESSAGE_MAP()
234 void NotificationManager::OnDidShow(int notification_id
) {
235 const auto& iter
= active_page_notifications_
.find(notification_id
);
236 if (iter
== active_page_notifications_
.end())
239 iter
->second
->dispatchShowEvent();
242 void NotificationManager::OnDidShowPersistent(int request_id
, bool success
) {
243 blink::WebNotificationShowCallbacks
* callbacks
=
244 pending_show_notification_requests_
.Lookup(request_id
);
251 callbacks
->onSuccess();
253 callbacks
->onError();
255 pending_show_notification_requests_
.Remove(request_id
);
258 void NotificationManager::OnDidClose(int notification_id
) {
259 const auto& iter
= active_page_notifications_
.find(notification_id
);
260 if (iter
== active_page_notifications_
.end())
263 iter
->second
->dispatchCloseEvent();
264 active_page_notifications_
.erase(iter
);
267 void NotificationManager::OnDidClick(int notification_id
) {
268 const auto& iter
= active_page_notifications_
.find(notification_id
);
269 if (iter
== active_page_notifications_
.end())
272 iter
->second
->dispatchClickEvent();
275 void NotificationManager::OnDidGetNotifications(
277 const std::vector
<PersistentNotificationInfo
>& notification_infos
) {
278 blink::WebNotificationGetCallbacks
* callbacks
=
279 pending_get_notification_requests_
.Lookup(request_id
);
284 scoped_ptr
<blink::WebVector
<blink::WebPersistentNotificationInfo
>>
285 notifications(new blink::WebVector
<blink::WebPersistentNotificationInfo
>(
286 notification_infos
.size()));
288 for (size_t i
= 0; i
< notification_infos
.size(); ++i
) {
289 blink::WebPersistentNotificationInfo web_notification_info
;
290 web_notification_info
.persistentId
= notification_infos
[i
].first
;
291 web_notification_info
.data
=
292 ToWebNotificationData(notification_infos
[i
].second
);
294 (*notifications
)[i
] = web_notification_info
;
297 callbacks
->onSuccess(notifications
.release());
299 pending_get_notification_requests_
.Remove(request_id
);
302 void NotificationManager::DisplayPageNotification(
303 const blink::WebSerializedOrigin
& origin
,
304 const blink::WebNotificationData
& notification_data
,
305 blink::WebNotificationDelegate
* delegate
,
306 const SkBitmap
& icon
) {
307 int notification_id
=
308 notification_dispatcher_
->GenerateNotificationId(CurrentWorkerId());
310 active_page_notifications_
[notification_id
] = delegate
;
311 thread_safe_sender_
->Send(
312 new PlatformNotificationHostMsg_Show(
314 GURL(origin
.string()),
316 ToPlatformNotificationData(notification_data
)));
319 void NotificationManager::DisplayPersistentNotification(
320 const blink::WebSerializedOrigin
& origin
,
321 const blink::WebNotificationData
& notification_data
,
322 int64_t service_worker_registration_id
,
323 scoped_ptr
<blink::WebNotificationShowCallbacks
> callbacks
,
324 const SkBitmap
& icon
) {
325 // TODO(peter): GenerateNotificationId is more of a request id. Consider
326 // renaming the method in the NotificationDispatcher if this makes sense.
328 notification_dispatcher_
->GenerateNotificationId(CurrentWorkerId());
330 pending_show_notification_requests_
.AddWithID(callbacks
.release(),
333 thread_safe_sender_
->Send(
334 new PlatformNotificationHostMsg_ShowPersistent(
336 service_worker_registration_id
,
337 GURL(origin
.string()),
339 ToPlatformNotificationData(notification_data
)));
342 } // namespace content