Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / child / notifications / notification_manager.cc
blob920cba7ed5429a2835c05d39432d47576afa1001
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"
7 #include <cmath>
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/common/notification_constants.h"
19 #include "content/public/common/platform_notification_data.h"
20 #include "third_party/WebKit/public/platform/WebSecurityOrigin.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;
26 namespace content {
27 namespace {
29 int CurrentWorkerId() {
30 return WorkerThread::GetCurrentId();
33 } // namespace
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 WorkerThread::AddObserver(manager);
63 return manager;
66 void NotificationManager::WillStopCurrentWorkerThread() {
67 delete this;
70 void NotificationManager::show(
71 const blink::WebSecurityOrigin& origin,
72 const blink::WebNotificationData& notification_data,
73 blink::WebNotificationDelegate* delegate) {
74 if (notification_data.icon.isEmpty()) {
75 DisplayPageNotification(origin, notification_data, delegate, SkBitmap());
76 return;
79 pending_notifications_.FetchPageNotificationResources(
80 notification_data,
81 delegate,
82 base::Bind(&NotificationManager::DisplayPageNotification,
83 base::Unretained(this), // this owns |pending_notifications_|
84 origin,
85 notification_data,
86 delegate));
89 void NotificationManager::showPersistent(
90 const blink::WebSecurityOrigin& 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();
115 return;
118 if (notification_data.icon.isEmpty()) {
119 DisplayPersistentNotification(origin,
120 notification_data,
121 service_worker_registration_id,
122 owned_callbacks.Pass(),
123 SkBitmap());
124 return;
127 pending_notifications_.FetchPersistentNotificationResources(
128 notification_data,
129 base::Bind(&NotificationManager::DisplayPersistentNotification,
130 base::Unretained(this), // this owns |pending_notifications_|
131 origin,
132 notification_data,
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);
142 DCHECK(callbacks);
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.
154 int request_id =
155 notification_dispatcher_->GenerateNotificationId(CurrentWorkerId());
157 pending_get_notification_requests_.AddWithID(callbacks, request_id);
159 thread_safe_sender_->Send(
160 new PlatformNotificationHostMsg_GetNotifications(
161 request_id,
162 service_worker_registration_id,
163 origin,
164 base::UTF16ToUTF8(base::StringPiece16(filter_tag))));
167 void NotificationManager::close(blink::WebNotificationDelegate* delegate) {
168 if (pending_notifications_.CancelPageNotificationFetches(delegate))
169 return;
171 for (auto& iter : active_page_notifications_) {
172 if (iter.second != delegate)
173 continue;
175 thread_safe_sender_->Send(
176 new PlatformNotificationHostMsg_Close(iter.first));
177 active_page_notifications_.erase(iter.first);
178 return;
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.
183 NOTREACHED();
186 void NotificationManager::closePersistent(
187 const blink::WebSecurityOrigin& origin,
188 int64_t persistent_notification_id) {
189 thread_safe_sender_->Send(new PlatformNotificationHostMsg_ClosePersistent(
190 // TODO(mkwst): This is potentially doing the wrong thing with unique
191 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See
192 // https://crbug.com/490074 for detail.
193 GURL(origin.toString()), persistent_notification_id));
196 void NotificationManager::notifyDelegateDestroyed(
197 blink::WebNotificationDelegate* delegate) {
198 if (pending_notifications_.CancelPageNotificationFetches(delegate))
199 return;
201 for (auto& iter : active_page_notifications_) {
202 if (iter.second != delegate)
203 continue;
205 active_page_notifications_.erase(iter.first);
206 return;
210 WebNotificationPermission NotificationManager::checkPermission(
211 const blink::WebSecurityOrigin& origin) {
212 WebNotificationPermission permission =
213 blink::WebNotificationPermissionAllowed;
214 // TODO(mkwst): This is potentially doing the wrong thing with unique
215 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See
216 // https://crbug.com/490074 for detail.
217 thread_safe_sender_->Send(new PlatformNotificationHostMsg_CheckPermission(
218 GURL(origin.toString()), &permission));
220 return permission;
223 size_t NotificationManager::maxActions() {
224 return kPlatformNotificationMaxActions;
227 bool NotificationManager::OnMessageReceived(const IPC::Message& message) {
228 bool handled = true;
229 IPC_BEGIN_MESSAGE_MAP(NotificationManager, message)
230 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidShow, OnDidShow);
231 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidShowPersistent,
232 OnDidShowPersistent)
233 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClose, OnDidClose);
234 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClick, OnDidClick);
235 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidGetNotifications,
236 OnDidGetNotifications)
237 IPC_MESSAGE_UNHANDLED(handled = false)
238 IPC_END_MESSAGE_MAP()
240 return handled;
243 void NotificationManager::OnDidShow(int notification_id) {
244 const auto& iter = active_page_notifications_.find(notification_id);
245 if (iter == active_page_notifications_.end())
246 return;
248 iter->second->dispatchShowEvent();
251 void NotificationManager::OnDidShowPersistent(int request_id, bool success) {
252 blink::WebNotificationShowCallbacks* callbacks =
253 pending_show_notification_requests_.Lookup(request_id);
254 DCHECK(callbacks);
256 if (!callbacks)
257 return;
259 if (success)
260 callbacks->onSuccess();
261 else
262 callbacks->onError();
264 pending_show_notification_requests_.Remove(request_id);
267 void NotificationManager::OnDidClose(int notification_id) {
268 const auto& iter = active_page_notifications_.find(notification_id);
269 if (iter == active_page_notifications_.end())
270 return;
272 iter->second->dispatchCloseEvent();
273 active_page_notifications_.erase(iter);
276 void NotificationManager::OnDidClick(int notification_id) {
277 const auto& iter = active_page_notifications_.find(notification_id);
278 if (iter == active_page_notifications_.end())
279 return;
281 iter->second->dispatchClickEvent();
284 void NotificationManager::OnDidGetNotifications(
285 int request_id,
286 const std::vector<PersistentNotificationInfo>& notification_infos) {
287 blink::WebNotificationGetCallbacks* callbacks =
288 pending_get_notification_requests_.Lookup(request_id);
289 DCHECK(callbacks);
290 if (!callbacks)
291 return;
293 blink::WebVector<blink::WebPersistentNotificationInfo> notifications(
294 notification_infos.size());
296 for (size_t i = 0; i < notification_infos.size(); ++i) {
297 blink::WebPersistentNotificationInfo web_notification_info;
298 web_notification_info.persistentId = notification_infos[i].first;
299 web_notification_info.data =
300 ToWebNotificationData(notification_infos[i].second);
302 notifications[i] = web_notification_info;
305 callbacks->onSuccess(notifications);
307 pending_get_notification_requests_.Remove(request_id);
310 void NotificationManager::DisplayPageNotification(
311 const blink::WebSecurityOrigin& origin,
312 const blink::WebNotificationData& notification_data,
313 blink::WebNotificationDelegate* delegate,
314 const SkBitmap& icon) {
315 int notification_id =
316 notification_dispatcher_->GenerateNotificationId(CurrentWorkerId());
318 active_page_notifications_[notification_id] = delegate;
319 // TODO(mkwst): This is potentially doing the wrong thing with unique
320 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See
321 // https://crbug.com/490074 for detail.
322 thread_safe_sender_->Send(new PlatformNotificationHostMsg_Show(
323 notification_id, GURL(origin.toString()), icon,
324 ToPlatformNotificationData(notification_data)));
327 void NotificationManager::DisplayPersistentNotification(
328 const blink::WebSecurityOrigin& origin,
329 const blink::WebNotificationData& notification_data,
330 int64_t service_worker_registration_id,
331 scoped_ptr<blink::WebNotificationShowCallbacks> callbacks,
332 const SkBitmap& icon) {
333 // TODO(peter): GenerateNotificationId is more of a request id. Consider
334 // renaming the method in the NotificationDispatcher if this makes sense.
335 int request_id =
336 notification_dispatcher_->GenerateNotificationId(CurrentWorkerId());
338 pending_show_notification_requests_.AddWithID(callbacks.release(),
339 request_id);
341 // TODO(mkwst): This is potentially doing the wrong thing with unique
342 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See
343 // https://crbug.com/490074 for detail.
344 thread_safe_sender_->Send(new PlatformNotificationHostMsg_ShowPersistent(
345 request_id, service_worker_registration_id, GURL(origin.toString()), icon,
346 ToPlatformNotificationData(notification_data)));
349 } // namespace content