Upstreaming browser/ui/uikit_ui_util from iOS.
[chromium-blink-merge.git] / content / child / notifications / notification_manager.cc
blob95195455295633da3dab73e4b12351c0e8c3303e
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/child/worker_task_runner.h"
19 #include "content/common/notification_constants.h"
20 #include "content/public/common/platform_notification_data.h"
21 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
22 #include "third_party/WebKit/public/platform/modules/notifications/WebNotificationDelegate.h"
23 #include "third_party/skia/include/core/SkBitmap.h"
25 using blink::WebNotificationPermission;
27 namespace content {
28 namespace {
30 int CurrentWorkerId() {
31 return WorkerTaskRunner::Instance()->CurrentWorkerId();
34 } // namespace
36 static base::LazyInstance<base::ThreadLocalPointer<NotificationManager>>::Leaky
37 g_notification_manager_tls = LAZY_INSTANCE_INITIALIZER;
39 NotificationManager::NotificationManager(
40 ThreadSafeSender* thread_safe_sender,
41 base::SingleThreadTaskRunner* main_thread_task_runner,
42 NotificationDispatcher* notification_dispatcher)
43 : thread_safe_sender_(thread_safe_sender),
44 notification_dispatcher_(notification_dispatcher),
45 pending_notifications_(main_thread_task_runner) {
46 g_notification_manager_tls.Pointer()->Set(this);
49 NotificationManager::~NotificationManager() {
50 g_notification_manager_tls.Pointer()->Set(nullptr);
53 NotificationManager* NotificationManager::ThreadSpecificInstance(
54 ThreadSafeSender* thread_safe_sender,
55 base::SingleThreadTaskRunner* main_thread_task_runner,
56 NotificationDispatcher* notification_dispatcher) {
57 if (g_notification_manager_tls.Pointer()->Get())
58 return g_notification_manager_tls.Pointer()->Get();
60 NotificationManager* manager = new NotificationManager(
61 thread_safe_sender, main_thread_task_runner, notification_dispatcher);
62 if (CurrentWorkerId())
63 WorkerTaskRunner::Instance()->AddStopObserver(manager);
64 return manager;
67 void NotificationManager::OnWorkerRunLoopStopped() {
68 delete this;
71 void NotificationManager::show(
72 const blink::WebSecurityOrigin& origin,
73 const blink::WebNotificationData& notification_data,
74 blink::WebNotificationDelegate* delegate) {
75 if (notification_data.icon.isEmpty()) {
76 DisplayPageNotification(origin, notification_data, delegate, SkBitmap());
77 return;
80 pending_notifications_.FetchPageNotificationResources(
81 notification_data,
82 delegate,
83 base::Bind(&NotificationManager::DisplayPageNotification,
84 base::Unretained(this), // this owns |pending_notifications_|
85 origin,
86 notification_data,
87 delegate));
90 void NotificationManager::showPersistent(
91 const blink::WebSecurityOrigin& origin,
92 const blink::WebNotificationData& notification_data,
93 blink::WebServiceWorkerRegistration* service_worker_registration,
94 blink::WebNotificationShowCallbacks* callbacks) {
95 DCHECK(service_worker_registration);
96 int64_t service_worker_registration_id =
97 static_cast<WebServiceWorkerRegistrationImpl*>(
98 service_worker_registration)->registration_id();
100 scoped_ptr<blink::WebNotificationShowCallbacks> owned_callbacks(callbacks);
102 // Verify that the author-provided payload size does not exceed our limit.
103 // This is an implementation-defined limit to prevent abuse of notification
104 // data as a storage mechanism. A UMA histogram records the requested sizes,
105 // which enables us to track how much data authors are attempting to store.
107 // If the size exceeds this limit, reject the showNotification() promise. This
108 // is outside of the boundaries set by the specification, but it gives authors
109 // an indication that something has gone wrong.
110 size_t author_data_size = notification_data.data.size();
111 UMA_HISTOGRAM_MEMORY_KB("Notifications.AuthorDataSizeKB",
112 static_cast<int>(ceil(author_data_size / 1024.0)));
114 if (author_data_size > PlatformNotificationData::kMaximumDeveloperDataSize) {
115 owned_callbacks->onError();
116 return;
119 if (notification_data.icon.isEmpty()) {
120 DisplayPersistentNotification(origin,
121 notification_data,
122 service_worker_registration_id,
123 owned_callbacks.Pass(),
124 SkBitmap());
125 return;
128 pending_notifications_.FetchPersistentNotificationResources(
129 notification_data,
130 base::Bind(&NotificationManager::DisplayPersistentNotification,
131 base::Unretained(this), // this owns |pending_notifications_|
132 origin,
133 notification_data,
134 service_worker_registration_id,
135 base::Passed(&owned_callbacks)));
138 void NotificationManager::getNotifications(
139 const blink::WebString& filter_tag,
140 blink::WebServiceWorkerRegistration* service_worker_registration,
141 blink::WebNotificationGetCallbacks* callbacks) {
142 DCHECK(service_worker_registration);
143 DCHECK(callbacks);
145 WebServiceWorkerRegistrationImpl* service_worker_registration_impl =
146 static_cast<WebServiceWorkerRegistrationImpl*>(
147 service_worker_registration);
149 GURL origin = GURL(service_worker_registration_impl->scope()).GetOrigin();
150 int64_t service_worker_registration_id =
151 service_worker_registration_impl->registration_id();
153 // TODO(peter): GenerateNotificationId is more of a request id. Consider
154 // renaming the method in the NotificationDispatcher if this makes sense.
155 int request_id =
156 notification_dispatcher_->GenerateNotificationId(CurrentWorkerId());
158 pending_get_notification_requests_.AddWithID(callbacks, request_id);
160 thread_safe_sender_->Send(
161 new PlatformNotificationHostMsg_GetNotifications(
162 request_id,
163 service_worker_registration_id,
164 origin,
165 base::UTF16ToUTF8(base::StringPiece16(filter_tag))));
168 void NotificationManager::close(blink::WebNotificationDelegate* delegate) {
169 if (pending_notifications_.CancelPageNotificationFetches(delegate))
170 return;
172 for (auto& iter : active_page_notifications_) {
173 if (iter.second != delegate)
174 continue;
176 thread_safe_sender_->Send(
177 new PlatformNotificationHostMsg_Close(iter.first));
178 active_page_notifications_.erase(iter.first);
179 return;
182 // It should not be possible for Blink to call close() on a Notification which
183 // does not exist in either the pending or active notification lists.
184 NOTREACHED();
187 void NotificationManager::closePersistent(
188 const blink::WebSecurityOrigin& origin,
189 int64_t persistent_notification_id) {
190 thread_safe_sender_->Send(new PlatformNotificationHostMsg_ClosePersistent(
191 // TODO(mkwst): This is potentially doing the wrong thing with unique
192 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See
193 // https://crbug.com/490074 for detail.
194 GURL(origin.toString()), persistent_notification_id));
197 void NotificationManager::notifyDelegateDestroyed(
198 blink::WebNotificationDelegate* delegate) {
199 if (pending_notifications_.CancelPageNotificationFetches(delegate))
200 return;
202 for (auto& iter : active_page_notifications_) {
203 if (iter.second != delegate)
204 continue;
206 active_page_notifications_.erase(iter.first);
207 return;
211 WebNotificationPermission NotificationManager::checkPermission(
212 const blink::WebSecurityOrigin& origin) {
213 WebNotificationPermission permission =
214 blink::WebNotificationPermissionAllowed;
215 // TODO(mkwst): This is potentially doing the wrong thing with unique
216 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See
217 // https://crbug.com/490074 for detail.
218 thread_safe_sender_->Send(new PlatformNotificationHostMsg_CheckPermission(
219 GURL(origin.toString()), &permission));
221 return permission;
224 size_t NotificationManager::maxActions() {
225 return kPlatformNotificationMaxActions;
228 bool NotificationManager::OnMessageReceived(const IPC::Message& message) {
229 bool handled = true;
230 IPC_BEGIN_MESSAGE_MAP(NotificationManager, message)
231 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidShow, OnDidShow);
232 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidShowPersistent,
233 OnDidShowPersistent)
234 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClose, OnDidClose);
235 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClick, OnDidClick);
236 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidGetNotifications,
237 OnDidGetNotifications)
238 IPC_MESSAGE_UNHANDLED(handled = false)
239 IPC_END_MESSAGE_MAP()
241 return handled;
244 void NotificationManager::OnDidShow(int notification_id) {
245 const auto& iter = active_page_notifications_.find(notification_id);
246 if (iter == active_page_notifications_.end())
247 return;
249 iter->second->dispatchShowEvent();
252 void NotificationManager::OnDidShowPersistent(int request_id, bool success) {
253 blink::WebNotificationShowCallbacks* callbacks =
254 pending_show_notification_requests_.Lookup(request_id);
255 DCHECK(callbacks);
257 if (!callbacks)
258 return;
260 if (success)
261 callbacks->onSuccess();
262 else
263 callbacks->onError();
265 pending_show_notification_requests_.Remove(request_id);
268 void NotificationManager::OnDidClose(int notification_id) {
269 const auto& iter = active_page_notifications_.find(notification_id);
270 if (iter == active_page_notifications_.end())
271 return;
273 iter->second->dispatchCloseEvent();
274 active_page_notifications_.erase(iter);
277 void NotificationManager::OnDidClick(int notification_id) {
278 const auto& iter = active_page_notifications_.find(notification_id);
279 if (iter == active_page_notifications_.end())
280 return;
282 iter->second->dispatchClickEvent();
285 void NotificationManager::OnDidGetNotifications(
286 int request_id,
287 const std::vector<PersistentNotificationInfo>& notification_infos) {
288 blink::WebNotificationGetCallbacks* callbacks =
289 pending_get_notification_requests_.Lookup(request_id);
290 DCHECK(callbacks);
291 if (!callbacks)
292 return;
294 scoped_ptr<blink::WebVector<blink::WebPersistentNotificationInfo>>
295 notifications(new blink::WebVector<blink::WebPersistentNotificationInfo>(
296 notification_infos.size()));
298 for (size_t i = 0; i < notification_infos.size(); ++i) {
299 blink::WebPersistentNotificationInfo web_notification_info;
300 web_notification_info.persistentId = notification_infos[i].first;
301 web_notification_info.data =
302 ToWebNotificationData(notification_infos[i].second);
304 (*notifications)[i] = web_notification_info;
307 callbacks->onSuccess(notifications.release());
309 pending_get_notification_requests_.Remove(request_id);
312 void NotificationManager::DisplayPageNotification(
313 const blink::WebSecurityOrigin& origin,
314 const blink::WebNotificationData& notification_data,
315 blink::WebNotificationDelegate* delegate,
316 const SkBitmap& icon) {
317 int notification_id =
318 notification_dispatcher_->GenerateNotificationId(CurrentWorkerId());
320 active_page_notifications_[notification_id] = delegate;
321 // TODO(mkwst): This is potentially doing the wrong thing with unique
322 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See
323 // https://crbug.com/490074 for detail.
324 thread_safe_sender_->Send(new PlatformNotificationHostMsg_Show(
325 notification_id, GURL(origin.toString()), icon,
326 ToPlatformNotificationData(notification_data)));
329 void NotificationManager::DisplayPersistentNotification(
330 const blink::WebSecurityOrigin& origin,
331 const blink::WebNotificationData& notification_data,
332 int64_t service_worker_registration_id,
333 scoped_ptr<blink::WebNotificationShowCallbacks> callbacks,
334 const SkBitmap& icon) {
335 // TODO(peter): GenerateNotificationId is more of a request id. Consider
336 // renaming the method in the NotificationDispatcher if this makes sense.
337 int request_id =
338 notification_dispatcher_->GenerateNotificationId(CurrentWorkerId());
340 pending_show_notification_requests_.AddWithID(callbacks.release(),
341 request_id);
343 // TODO(mkwst): This is potentially doing the wrong thing with unique
344 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See
345 // https://crbug.com/490074 for detail.
346 thread_safe_sender_->Send(new PlatformNotificationHostMsg_ShowPersistent(
347 request_id, service_worker_registration_id, GURL(origin.toString()), icon,
348 ToPlatformNotificationData(notification_data)));
351 } // namespace content