| OLD | NEW |
| (Empty) |
| 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. | |
| 4 | |
| 5 #include "content/child/notifications/notification_manager.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/lazy_instance.h" | |
| 10 #include "base/metrics/histogram_macros.h" | |
| 11 #include "base/strings/utf_string_conversions.h" | |
| 12 #include "base/threading/thread_local.h" | |
| 13 #include "content/child/notifications/notification_data_conversions.h" | |
| 14 #include "content/child/notifications/notification_dispatcher.h" | |
| 15 #include "content/child/service_worker/web_service_worker_registration_impl.h" | |
| 16 #include "content/child/thread_safe_sender.h" | |
| 17 #include "content/public/common/notification_resources.h" | |
| 18 #include "content/public/common/platform_notification_data.h" | |
| 19 #include "third_party/WebKit/public/platform/URLConversion.h" | |
| 20 #include "third_party/WebKit/public/platform/WebSecurityOrigin.h" | |
| 21 #include "third_party/WebKit/public/platform/modules/notifications/WebNotificati
onDelegate.h" | |
| 22 | |
| 23 namespace content { | |
| 24 namespace { | |
| 25 | |
| 26 int CurrentWorkerId() { | |
| 27 return WorkerThread::GetCurrentId(); | |
| 28 } | |
| 29 | |
| 30 NotificationResources ToNotificationResources( | |
| 31 std::unique_ptr<blink::WebNotificationResources> web_resources) { | |
| 32 NotificationResources resources; | |
| 33 resources.notification_icon = web_resources->icon; | |
| 34 resources.badge = web_resources->badge; | |
| 35 for (const auto& action_icon : web_resources->actionIcons) | |
| 36 resources.action_icons.push_back(action_icon); | |
| 37 return resources; | |
| 38 } | |
| 39 | |
| 40 } // namespace | |
| 41 | |
| 42 static base::LazyInstance<base::ThreadLocalPointer<NotificationManager>>::Leaky | |
| 43 g_notification_manager_tls = LAZY_INSTANCE_INITIALIZER; | |
| 44 | |
| 45 NotificationManager::NotificationManager( | |
| 46 ThreadSafeSender* thread_safe_sender, | |
| 47 NotificationDispatcher* notification_dispatcher) | |
| 48 : thread_safe_sender_(thread_safe_sender), | |
| 49 notification_dispatcher_(notification_dispatcher) { | |
| 50 g_notification_manager_tls.Pointer()->Set(this); | |
| 51 } | |
| 52 | |
| 53 NotificationManager::~NotificationManager() { | |
| 54 g_notification_manager_tls.Pointer()->Set(nullptr); | |
| 55 } | |
| 56 | |
| 57 NotificationManager* NotificationManager::ThreadSpecificInstance( | |
| 58 ThreadSafeSender* thread_safe_sender, | |
| 59 NotificationDispatcher* notification_dispatcher) { | |
| 60 if (g_notification_manager_tls.Pointer()->Get()) | |
| 61 return g_notification_manager_tls.Pointer()->Get(); | |
| 62 | |
| 63 NotificationManager* manager = | |
| 64 new NotificationManager(thread_safe_sender, notification_dispatcher); | |
| 65 if (CurrentWorkerId()) | |
| 66 WorkerThread::AddObserver(manager); | |
| 67 return manager; | |
| 68 } | |
| 69 | |
| 70 void NotificationManager::WillStopCurrentWorkerThread() { | |
| 71 delete this; | |
| 72 } | |
| 73 | |
| 74 void NotificationManager::show( | |
| 75 const blink::WebSecurityOrigin& origin, | |
| 76 const blink::WebNotificationData& notification_data, | |
| 77 std::unique_ptr<blink::WebNotificationResources> notification_resources, | |
| 78 blink::WebNotificationDelegate* delegate) { | |
| 79 DCHECK_EQ(0u, notification_data.actions.size()); | |
| 80 DCHECK_EQ(0u, notification_resources->actionIcons.size()); | |
| 81 | |
| 82 int notification_id = | |
| 83 notification_dispatcher_->GenerateNotificationId(CurrentWorkerId()); | |
| 84 | |
| 85 active_page_notifications_[notification_id] = delegate; | |
| 86 // TODO(mkwst): This is potentially doing the wrong thing with unique | |
| 87 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See | |
| 88 // https://crbug.com/490074 for detail. | |
| 89 thread_safe_sender_->Send(new PlatformNotificationHostMsg_Show( | |
| 90 notification_id, blink::WebStringToGURL(origin.toString()), | |
| 91 ToPlatformNotificationData(notification_data), | |
| 92 ToNotificationResources(std::move(notification_resources)))); | |
| 93 } | |
| 94 | |
| 95 void NotificationManager::showPersistent( | |
| 96 const blink::WebSecurityOrigin& origin, | |
| 97 const blink::WebNotificationData& notification_data, | |
| 98 std::unique_ptr<blink::WebNotificationResources> notification_resources, | |
| 99 blink::WebServiceWorkerRegistration* service_worker_registration, | |
| 100 blink::WebNotificationShowCallbacks* callbacks) { | |
| 101 DCHECK(service_worker_registration); | |
| 102 DCHECK_EQ(notification_data.actions.size(), | |
| 103 notification_resources->actionIcons.size()); | |
| 104 | |
| 105 int64_t service_worker_registration_id = | |
| 106 static_cast<WebServiceWorkerRegistrationImpl*>( | |
| 107 service_worker_registration) | |
| 108 ->registration_id(); | |
| 109 | |
| 110 std::unique_ptr<blink::WebNotificationShowCallbacks> owned_callbacks( | |
| 111 callbacks); | |
| 112 | |
| 113 // Verify that the author-provided payload size does not exceed our limit. | |
| 114 // This is an implementation-defined limit to prevent abuse of notification | |
| 115 // data as a storage mechanism. A UMA histogram records the requested sizes, | |
| 116 // which enables us to track how much data authors are attempting to store. | |
| 117 // | |
| 118 // If the size exceeds this limit, reject the showNotification() promise. This | |
| 119 // is outside of the boundaries set by the specification, but it gives authors | |
| 120 // an indication that something has gone wrong. | |
| 121 size_t author_data_size = notification_data.data.size(); | |
| 122 | |
| 123 UMA_HISTOGRAM_COUNTS_1000("Notifications.AuthorDataSize", author_data_size); | |
| 124 | |
| 125 if (author_data_size > PlatformNotificationData::kMaximumDeveloperDataSize) { | |
| 126 owned_callbacks->onError(); | |
| 127 return; | |
| 128 } | |
| 129 | |
| 130 // TODO(peter): GenerateNotificationId is more of a request id. Consider | |
| 131 // renaming the method in the NotificationDispatcher if this makes sense. | |
| 132 int request_id = | |
| 133 notification_dispatcher_->GenerateNotificationId(CurrentWorkerId()); | |
| 134 | |
| 135 pending_show_notification_requests_.AddWithID(owned_callbacks.release(), | |
| 136 request_id); | |
| 137 | |
| 138 // TODO(mkwst): This is potentially doing the wrong thing with unique | |
| 139 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See | |
| 140 // https://crbug.com/490074 for detail. | |
| 141 thread_safe_sender_->Send(new PlatformNotificationHostMsg_ShowPersistent( | |
| 142 request_id, service_worker_registration_id, | |
| 143 blink::WebStringToGURL(origin.toString()), | |
| 144 ToPlatformNotificationData(notification_data), | |
| 145 ToNotificationResources(std::move(notification_resources)))); | |
| 146 } | |
| 147 | |
| 148 void NotificationManager::getNotifications( | |
| 149 const blink::WebString& filter_tag, | |
| 150 blink::WebServiceWorkerRegistration* service_worker_registration, | |
| 151 blink::WebNotificationGetCallbacks* callbacks) { | |
| 152 DCHECK(service_worker_registration); | |
| 153 DCHECK(callbacks); | |
| 154 | |
| 155 WebServiceWorkerRegistrationImpl* service_worker_registration_impl = | |
| 156 static_cast<WebServiceWorkerRegistrationImpl*>( | |
| 157 service_worker_registration); | |
| 158 | |
| 159 GURL origin = GURL(service_worker_registration_impl->scope()).GetOrigin(); | |
| 160 int64_t service_worker_registration_id = | |
| 161 service_worker_registration_impl->registration_id(); | |
| 162 | |
| 163 // TODO(peter): GenerateNotificationId is more of a request id. Consider | |
| 164 // renaming the method in the NotificationDispatcher if this makes sense. | |
| 165 int request_id = | |
| 166 notification_dispatcher_->GenerateNotificationId(CurrentWorkerId()); | |
| 167 | |
| 168 pending_get_notification_requests_.AddWithID(callbacks, request_id); | |
| 169 | |
| 170 thread_safe_sender_->Send(new PlatformNotificationHostMsg_GetNotifications( | |
| 171 request_id, service_worker_registration_id, origin, | |
| 172 base::UTF16ToUTF8(base::StringPiece16(filter_tag)))); | |
| 173 } | |
| 174 | |
| 175 void NotificationManager::close(blink::WebNotificationDelegate* delegate) { | |
| 176 for (auto& iter : active_page_notifications_) { | |
| 177 if (iter.second != delegate) | |
| 178 continue; | |
| 179 | |
| 180 thread_safe_sender_->Send( | |
| 181 new PlatformNotificationHostMsg_Close(iter.first)); | |
| 182 active_page_notifications_.erase(iter.first); | |
| 183 return; | |
| 184 } | |
| 185 | |
| 186 // It should not be possible for Blink to call close() on a Notification which | |
| 187 // does not exist in either the pending or active notification lists. | |
| 188 NOTREACHED(); | |
| 189 } | |
| 190 | |
| 191 void NotificationManager::closePersistent( | |
| 192 const blink::WebSecurityOrigin& origin, | |
| 193 int64_t persistent_notification_id) { | |
| 194 thread_safe_sender_->Send(new PlatformNotificationHostMsg_ClosePersistent( | |
| 195 // TODO(mkwst): This is potentially doing the wrong thing with unique | |
| 196 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See | |
| 197 // https://crbug.com/490074 for detail. | |
| 198 blink::WebStringToGURL(origin.toString()), persistent_notification_id)); | |
| 199 } | |
| 200 | |
| 201 void NotificationManager::notifyDelegateDestroyed( | |
| 202 blink::WebNotificationDelegate* delegate) { | |
| 203 for (auto& iter : active_page_notifications_) { | |
| 204 if (iter.second != delegate) | |
| 205 continue; | |
| 206 | |
| 207 active_page_notifications_.erase(iter.first); | |
| 208 return; | |
| 209 } | |
| 210 } | |
| 211 | |
| 212 blink::mojom::PermissionStatus NotificationManager::checkPermission( | |
| 213 const blink::WebSecurityOrigin& origin) { | |
| 214 blink::mojom::PermissionStatus permission_status = | |
| 215 blink::mojom::PermissionStatus::DENIED; | |
| 216 | |
| 217 // TODO(mkwst): This is potentially doing the wrong thing with unique | |
| 218 // origins. Perhaps also 'file:', 'blob:' and 'filesystem:'. See | |
| 219 // https://crbug.com/490074 for detail. | |
| 220 thread_safe_sender_->Send(new PlatformNotificationHostMsg_CheckPermission( | |
| 221 blink::WebStringToGURL(origin.toString()), &permission_status)); | |
| 222 | |
| 223 return permission_status; | |
| 224 } | |
| 225 | |
| 226 bool NotificationManager::OnMessageReceived(const IPC::Message& message) { | |
| 227 bool handled = true; | |
| 228 IPC_BEGIN_MESSAGE_MAP(NotificationManager, message) | |
| 229 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidShow, OnDidShow); | |
| 230 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidShowPersistent, | |
| 231 OnDidShowPersistent) | |
| 232 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClose, OnDidClose); | |
| 233 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidClick, OnDidClick); | |
| 234 IPC_MESSAGE_HANDLER(PlatformNotificationMsg_DidGetNotifications, | |
| 235 OnDidGetNotifications) | |
| 236 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 237 IPC_END_MESSAGE_MAP() | |
| 238 | |
| 239 return handled; | |
| 240 } | |
| 241 | |
| 242 void NotificationManager::OnDidShow(int notification_id) { | |
| 243 const auto& iter = active_page_notifications_.find(notification_id); | |
| 244 if (iter == active_page_notifications_.end()) | |
| 245 return; | |
| 246 | |
| 247 iter->second->dispatchShowEvent(); | |
| 248 } | |
| 249 | |
| 250 void NotificationManager::OnDidShowPersistent(int request_id, bool success) { | |
| 251 blink::WebNotificationShowCallbacks* callbacks = | |
| 252 pending_show_notification_requests_.Lookup(request_id); | |
| 253 DCHECK(callbacks); | |
| 254 | |
| 255 if (!callbacks) | |
| 256 return; | |
| 257 | |
| 258 if (success) | |
| 259 callbacks->onSuccess(); | |
| 260 else | |
| 261 callbacks->onError(); | |
| 262 | |
| 263 pending_show_notification_requests_.Remove(request_id); | |
| 264 } | |
| 265 | |
| 266 void NotificationManager::OnDidClose(int notification_id) { | |
| 267 const auto& iter = active_page_notifications_.find(notification_id); | |
| 268 if (iter == active_page_notifications_.end()) | |
| 269 return; | |
| 270 | |
| 271 iter->second->dispatchCloseEvent(); | |
| 272 active_page_notifications_.erase(iter); | |
| 273 } | |
| 274 | |
| 275 void NotificationManager::OnDidClick(int notification_id) { | |
| 276 const auto& iter = active_page_notifications_.find(notification_id); | |
| 277 if (iter == active_page_notifications_.end()) | |
| 278 return; | |
| 279 | |
| 280 iter->second->dispatchClickEvent(); | |
| 281 } | |
| 282 | |
| 283 void NotificationManager::OnDidGetNotifications( | |
| 284 int request_id, | |
| 285 const std::vector<PersistentNotificationInfo>& notification_infos) { | |
| 286 blink::WebNotificationGetCallbacks* callbacks = | |
| 287 pending_get_notification_requests_.Lookup(request_id); | |
| 288 DCHECK(callbacks); | |
| 289 if (!callbacks) | |
| 290 return; | |
| 291 | |
| 292 blink::WebVector<blink::WebPersistentNotificationInfo> notifications( | |
| 293 notification_infos.size()); | |
| 294 | |
| 295 for (size_t i = 0; i < notification_infos.size(); ++i) { | |
| 296 blink::WebPersistentNotificationInfo web_notification_info; | |
| 297 web_notification_info.persistentId = notification_infos[i].first; | |
| 298 web_notification_info.data = | |
| 299 ToWebNotificationData(notification_infos[i].second); | |
| 300 | |
| 301 notifications[i] = web_notification_info; | |
| 302 } | |
| 303 | |
| 304 callbacks->onSuccess(notifications); | |
| 305 | |
| 306 pending_get_notification_requests_.Remove(request_id); | |
| 307 } | |
| 308 | |
| 309 } // namespace content | |
| OLD | NEW |