| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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/permissions/permission_dispatcher.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <utility> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/callback.h" | |
| 12 #include "content/public/child/worker_thread.h" | |
| 13 #include "services/shell/public/cpp/interface_provider.h" | |
| 14 #include "third_party/WebKit/public/platform/WebURL.h" | |
| 15 #include "third_party/WebKit/public/platform/modules/permissions/WebPermissionOb
server.h" | |
| 16 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h" | |
| 17 | |
| 18 using blink::WebPermissionObserver; | |
| 19 using blink::mojom::PermissionName; | |
| 20 using blink::mojom::PermissionStatus; | |
| 21 | |
| 22 namespace content { | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 PermissionName GetPermissionName(blink::WebPermissionType type) { | |
| 27 switch (type) { | |
| 28 case blink::WebPermissionTypeGeolocation: | |
| 29 return PermissionName::GEOLOCATION; | |
| 30 case blink::WebPermissionTypeNotifications: | |
| 31 return PermissionName::NOTIFICATIONS; | |
| 32 case blink::WebPermissionTypePushNotifications: | |
| 33 return PermissionName::PUSH_NOTIFICATIONS; | |
| 34 case blink::WebPermissionTypeMidiSysEx: | |
| 35 return PermissionName::MIDI_SYSEX; | |
| 36 case blink::WebPermissionTypeDurableStorage: | |
| 37 return PermissionName::DURABLE_STORAGE; | |
| 38 case blink::WebPermissionTypeMidi: | |
| 39 return PermissionName::MIDI; | |
| 40 case blink::WebPermissionTypeBackgroundSync: | |
| 41 return PermissionName::BACKGROUND_SYNC; | |
| 42 default: | |
| 43 // The default statement is only there to prevent compilation failures if | |
| 44 // WebPermissionType enum gets extended. | |
| 45 NOTREACHED(); | |
| 46 return PermissionName::GEOLOCATION; | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 PermissionStatus GetPermissionStatus(blink::WebPermissionStatus status) { | |
| 51 switch (status) { | |
| 52 case blink::WebPermissionStatusGranted: | |
| 53 return PermissionStatus::GRANTED; | |
| 54 case blink::WebPermissionStatusDenied: | |
| 55 return PermissionStatus::DENIED; | |
| 56 case blink::WebPermissionStatusPrompt: | |
| 57 return PermissionStatus::ASK; | |
| 58 } | |
| 59 | |
| 60 NOTREACHED(); | |
| 61 return PermissionStatus::DENIED; | |
| 62 } | |
| 63 | |
| 64 blink::WebPermissionStatus GetWebPermissionStatus(PermissionStatus status) { | |
| 65 switch (status) { | |
| 66 case PermissionStatus::GRANTED: | |
| 67 return blink::WebPermissionStatusGranted; | |
| 68 case PermissionStatus::DENIED: | |
| 69 return blink::WebPermissionStatusDenied; | |
| 70 case PermissionStatus::ASK: | |
| 71 return blink::WebPermissionStatusPrompt; | |
| 72 } | |
| 73 | |
| 74 NOTREACHED(); | |
| 75 return blink::WebPermissionStatusDenied; | |
| 76 } | |
| 77 | |
| 78 const int kNoWorkerThread = 0; | |
| 79 | |
| 80 } // anonymous namespace | |
| 81 | |
| 82 // static | |
| 83 bool PermissionDispatcher::IsObservable(blink::WebPermissionType type) { | |
| 84 return type == blink::WebPermissionTypeGeolocation || | |
| 85 type == blink::WebPermissionTypeNotifications || | |
| 86 type == blink::WebPermissionTypePushNotifications || | |
| 87 type == blink::WebPermissionTypeMidiSysEx || | |
| 88 type == blink::WebPermissionTypeMidi || | |
| 89 type == blink::WebPermissionTypeBackgroundSync; | |
| 90 } | |
| 91 | |
| 92 PermissionDispatcher::PermissionDispatcher( | |
| 93 shell::InterfaceProvider* remote_interfaces) | |
| 94 : remote_interfaces_(remote_interfaces) { | |
| 95 } | |
| 96 | |
| 97 PermissionDispatcher::~PermissionDispatcher() { | |
| 98 } | |
| 99 | |
| 100 void PermissionDispatcher::queryPermission( | |
| 101 blink::WebPermissionType type, | |
| 102 const blink::WebURL& origin, | |
| 103 blink::WebPermissionCallback* callback) { | |
| 104 QueryPermissionInternal( | |
| 105 type, origin.string().utf8(), callback, kNoWorkerThread); | |
| 106 } | |
| 107 | |
| 108 void PermissionDispatcher::requestPermission( | |
| 109 blink::WebPermissionType type, | |
| 110 const blink::WebURL& origin, | |
| 111 blink::WebPermissionCallback* callback) { | |
| 112 RequestPermissionInternal( | |
| 113 type, origin.string().utf8(), callback, kNoWorkerThread); | |
| 114 } | |
| 115 | |
| 116 void PermissionDispatcher::requestPermissions( | |
| 117 const blink::WebVector<blink::WebPermissionType>& types, | |
| 118 const blink::WebURL& origin, | |
| 119 blink::WebPermissionsCallback* callback) { | |
| 120 RequestPermissionsInternal( | |
| 121 types, origin.string().utf8(), callback, kNoWorkerThread); | |
| 122 } | |
| 123 | |
| 124 void PermissionDispatcher::revokePermission( | |
| 125 blink::WebPermissionType type, | |
| 126 const blink::WebURL& origin, | |
| 127 blink::WebPermissionCallback* callback) { | |
| 128 RevokePermissionInternal( | |
| 129 type, origin.string().utf8(), callback, kNoWorkerThread); | |
| 130 } | |
| 131 | |
| 132 void PermissionDispatcher::startListening( | |
| 133 blink::WebPermissionType type, | |
| 134 const blink::WebURL& origin, | |
| 135 WebPermissionObserver* observer) { | |
| 136 if (!IsObservable(type)) | |
| 137 return; | |
| 138 | |
| 139 RegisterObserver(observer); | |
| 140 | |
| 141 GetNextPermissionChange(type, origin.string().utf8(), observer, | |
| 142 // We initialize with an arbitrary value because the | |
| 143 // mojo service wants a value. Worst case, the | |
| 144 // observer will get notified about a non-change which | |
| 145 // should be a no-op. After the first notification, | |
| 146 // GetNextPermissionChange will be called with the | |
| 147 // latest known value. | |
| 148 PermissionStatus::ASK); | |
| 149 } | |
| 150 | |
| 151 void PermissionDispatcher::stopListening(WebPermissionObserver* observer) { | |
| 152 UnregisterObserver(observer); | |
| 153 } | |
| 154 | |
| 155 void PermissionDispatcher::QueryPermissionForWorker( | |
| 156 blink::WebPermissionType type, | |
| 157 const std::string& origin, | |
| 158 blink::WebPermissionCallback* callback, | |
| 159 int worker_thread_id) { | |
| 160 QueryPermissionInternal(type, origin, callback, worker_thread_id); | |
| 161 } | |
| 162 | |
| 163 void PermissionDispatcher::RequestPermissionForWorker( | |
| 164 blink::WebPermissionType type, | |
| 165 const std::string& origin, | |
| 166 blink::WebPermissionCallback* callback, | |
| 167 int worker_thread_id) { | |
| 168 RequestPermissionInternal(type, origin, callback, worker_thread_id); | |
| 169 } | |
| 170 | |
| 171 void PermissionDispatcher::RequestPermissionsForWorker( | |
| 172 const blink::WebVector<blink::WebPermissionType>& types, | |
| 173 const std::string& origin, | |
| 174 blink::WebPermissionsCallback* callback, | |
| 175 int worker_thread_id) { | |
| 176 RequestPermissionsInternal(types, origin, callback, worker_thread_id); | |
| 177 } | |
| 178 | |
| 179 void PermissionDispatcher::RevokePermissionForWorker( | |
| 180 blink::WebPermissionType type, | |
| 181 const std::string& origin, | |
| 182 blink::WebPermissionCallback* callback, | |
| 183 int worker_thread_id) { | |
| 184 RevokePermissionInternal(type, origin, callback, worker_thread_id); | |
| 185 } | |
| 186 | |
| 187 void PermissionDispatcher::StartListeningForWorker( | |
| 188 blink::WebPermissionType type, | |
| 189 const std::string& origin, | |
| 190 int worker_thread_id, | |
| 191 const base::Callback<void(blink::WebPermissionStatus)>& callback) { | |
| 192 GetPermissionServicePtr()->GetNextPermissionChange( | |
| 193 GetPermissionName(type), origin, | |
| 194 // We initialize with an arbitrary value because the mojo service wants a | |
| 195 // value. Worst case, the observer will get notified about a non-change | |
| 196 // which should be a no-op. After the first notification, | |
| 197 // GetNextPermissionChange will be called with the latest known value. | |
| 198 PermissionStatus::ASK, | |
| 199 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker, | |
| 200 base::Unretained(this), worker_thread_id, callback)); | |
| 201 } | |
| 202 | |
| 203 void PermissionDispatcher::GetNextPermissionChangeForWorker( | |
| 204 blink::WebPermissionType type, | |
| 205 const std::string& origin, | |
| 206 blink::WebPermissionStatus status, | |
| 207 int worker_thread_id, | |
| 208 const base::Callback<void(blink::WebPermissionStatus)>& callback) { | |
| 209 GetPermissionServicePtr()->GetNextPermissionChange( | |
| 210 GetPermissionName(type), | |
| 211 origin, | |
| 212 GetPermissionStatus(status), | |
| 213 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker, | |
| 214 base::Unretained(this), | |
| 215 worker_thread_id, | |
| 216 callback)); | |
| 217 } | |
| 218 | |
| 219 // static | |
| 220 void PermissionDispatcher::RunPermissionCallbackOnWorkerThread( | |
| 221 std::unique_ptr<blink::WebPermissionCallback> callback, | |
| 222 blink::WebPermissionStatus status) { | |
| 223 callback->onSuccess(status); | |
| 224 } | |
| 225 | |
| 226 void PermissionDispatcher::RunPermissionsCallbackOnWorkerThread( | |
| 227 std::unique_ptr<blink::WebPermissionsCallback> callback, | |
| 228 std::unique_ptr<blink::WebVector<blink::WebPermissionStatus>> statuses) { | |
| 229 callback->onSuccess(std::move(statuses)); | |
| 230 } | |
| 231 | |
| 232 blink::mojom::PermissionService* | |
| 233 PermissionDispatcher::GetPermissionServicePtr() { | |
| 234 if (!permission_service_.get()) { | |
| 235 remote_interfaces_->GetInterface(mojo::GetProxy(&permission_service_)); | |
| 236 } | |
| 237 return permission_service_.get(); | |
| 238 } | |
| 239 | |
| 240 void PermissionDispatcher::QueryPermissionInternal( | |
| 241 blink::WebPermissionType type, | |
| 242 const std::string& origin, | |
| 243 blink::WebPermissionCallback* callback, | |
| 244 int worker_thread_id) { | |
| 245 // We need to save the |callback| in an ScopedPtrHashMap so if |this| gets | |
| 246 // deleted, the callback will not leak. In the case of |this| gets deleted, | |
| 247 // the |permission_service_| pipe will be destroyed too so OnQueryPermission | |
| 248 // will not be called. | |
| 249 uintptr_t callback_key = reinterpret_cast<uintptr_t>(callback); | |
| 250 permission_callbacks_.add( | |
| 251 callback_key, std::unique_ptr<blink::WebPermissionCallback>(callback)); | |
| 252 | |
| 253 GetPermissionServicePtr()->HasPermission( | |
| 254 GetPermissionName(type), | |
| 255 origin, | |
| 256 base::Bind(&PermissionDispatcher::OnPermissionResponse, | |
| 257 base::Unretained(this), | |
| 258 worker_thread_id, | |
| 259 callback_key)); | |
| 260 } | |
| 261 | |
| 262 void PermissionDispatcher::RequestPermissionInternal( | |
| 263 blink::WebPermissionType type, | |
| 264 const std::string& origin, | |
| 265 blink::WebPermissionCallback* callback, | |
| 266 int worker_thread_id) { | |
| 267 // We need to save the |callback| in an ScopedPtrHashMap so if |this| gets | |
| 268 // deleted, the callback will not leak. In the case of |this| gets deleted, | |
| 269 // the |permission_service_| pipe will be destroyed too so OnQueryPermission | |
| 270 // will not be called. | |
| 271 uintptr_t callback_key = reinterpret_cast<uintptr_t>(callback); | |
| 272 permission_callbacks_.add( | |
| 273 callback_key, std::unique_ptr<blink::WebPermissionCallback>(callback)); | |
| 274 | |
| 275 GetPermissionServicePtr()->RequestPermission( | |
| 276 GetPermissionName(type), | |
| 277 origin, | |
| 278 blink::WebUserGestureIndicator::isProcessingUserGesture(), | |
| 279 base::Bind(&PermissionDispatcher::OnPermissionResponse, | |
| 280 base::Unretained(this), | |
| 281 worker_thread_id, | |
| 282 callback_key)); | |
| 283 } | |
| 284 | |
| 285 void PermissionDispatcher::RequestPermissionsInternal( | |
| 286 const blink::WebVector<blink::WebPermissionType>& types, | |
| 287 const std::string& origin, | |
| 288 blink::WebPermissionsCallback* callback, | |
| 289 int worker_thread_id) { | |
| 290 // We need to save the |callback| in an ScopedVector so if |this| gets | |
| 291 // deleted, the callback will not leak. In the case of |this| gets deleted, | |
| 292 // the |permission_service_| pipe will be destroyed too so OnQueryPermission | |
| 293 // will not be called. | |
| 294 uintptr_t callback_key = reinterpret_cast<uintptr_t>(callback); | |
| 295 permissions_callbacks_.add( | |
| 296 callback_key, std::unique_ptr<blink::WebPermissionsCallback>(callback)); | |
| 297 | |
| 298 mojo::Array<PermissionName> names(types.size()); | |
| 299 for (size_t i = 0; i < types.size(); ++i) | |
| 300 names[i] = GetPermissionName(types[i]); | |
| 301 | |
| 302 GetPermissionServicePtr()->RequestPermissions( | |
| 303 std::move(names), origin, | |
| 304 blink::WebUserGestureIndicator::isProcessingUserGesture(), | |
| 305 base::Bind(&PermissionDispatcher::OnRequestPermissionsResponse, | |
| 306 base::Unretained(this), worker_thread_id, callback_key)); | |
| 307 } | |
| 308 | |
| 309 void PermissionDispatcher::RevokePermissionInternal( | |
| 310 blink::WebPermissionType type, | |
| 311 const std::string& origin, | |
| 312 blink::WebPermissionCallback* callback, | |
| 313 int worker_thread_id) { | |
| 314 // We need to save the |callback| in an ScopedPtrHashMap so if |this| gets | |
| 315 // deleted, the callback will not leak. In the case of |this| gets deleted, | |
| 316 // the |permission_service_| pipe will be destroyed too so OnQueryPermission | |
| 317 // will not be called. | |
| 318 uintptr_t callback_key = reinterpret_cast<uintptr_t>(callback); | |
| 319 permission_callbacks_.add( | |
| 320 callback_key, std::unique_ptr<blink::WebPermissionCallback>(callback)); | |
| 321 | |
| 322 GetPermissionServicePtr()->RevokePermission( | |
| 323 GetPermissionName(type), | |
| 324 origin, | |
| 325 base::Bind(&PermissionDispatcher::OnPermissionResponse, | |
| 326 base::Unretained(this), | |
| 327 worker_thread_id, | |
| 328 callback_key)); | |
| 329 } | |
| 330 | |
| 331 void PermissionDispatcher::OnPermissionResponse(int worker_thread_id, | |
| 332 uintptr_t callback_key, | |
| 333 PermissionStatus result) { | |
| 334 std::unique_ptr<blink::WebPermissionCallback> callback = | |
| 335 permission_callbacks_.take_and_erase(callback_key); | |
| 336 blink::WebPermissionStatus status = GetWebPermissionStatus(result); | |
| 337 | |
| 338 if (worker_thread_id != kNoWorkerThread) { | |
| 339 // If the worker is no longer running, ::PostTask() will return false and | |
| 340 // gracefully fail, destroying the callback too. | |
| 341 WorkerThread::PostTask( | |
| 342 worker_thread_id, | |
| 343 base::Bind(&PermissionDispatcher::RunPermissionCallbackOnWorkerThread, | |
| 344 base::Passed(&callback), status)); | |
| 345 return; | |
| 346 } | |
| 347 | |
| 348 callback->onSuccess(status); | |
| 349 } | |
| 350 | |
| 351 void PermissionDispatcher::OnRequestPermissionsResponse( | |
| 352 int worker_thread_id, | |
| 353 uintptr_t callback_key, | |
| 354 mojo::Array<PermissionStatus> result) { | |
| 355 std::unique_ptr<blink::WebPermissionsCallback> callback = | |
| 356 permissions_callbacks_.take_and_erase(callback_key); | |
| 357 std::unique_ptr<blink::WebVector<blink::WebPermissionStatus>> statuses( | |
| 358 new blink::WebVector<blink::WebPermissionStatus>(result.size())); | |
| 359 | |
| 360 for (size_t i = 0; i < result.size(); i++) | |
| 361 statuses->operator[](i) = GetWebPermissionStatus(result[i]); | |
| 362 | |
| 363 if (worker_thread_id != kNoWorkerThread) { | |
| 364 // If the worker is no longer running, ::PostTask() will return false and | |
| 365 // gracefully fail, destroying the callback too. | |
| 366 WorkerThread::PostTask( | |
| 367 worker_thread_id, | |
| 368 base::Bind(&PermissionDispatcher::RunPermissionsCallbackOnWorkerThread, | |
| 369 base::Passed(&callback), base::Passed(&statuses))); | |
| 370 return; | |
| 371 } | |
| 372 | |
| 373 callback->onSuccess(std::move(statuses)); | |
| 374 } | |
| 375 | |
| 376 void PermissionDispatcher::OnPermissionChanged(blink::WebPermissionType type, | |
| 377 const std::string& origin, | |
| 378 WebPermissionObserver* observer, | |
| 379 PermissionStatus status) { | |
| 380 if (!IsObserverRegistered(observer)) | |
| 381 return; | |
| 382 | |
| 383 observer->permissionChanged(type, GetWebPermissionStatus(status)); | |
| 384 | |
| 385 GetNextPermissionChange(type, origin, observer, status); | |
| 386 } | |
| 387 | |
| 388 void PermissionDispatcher::OnPermissionChangedForWorker( | |
| 389 int worker_thread_id, | |
| 390 const base::Callback<void(blink::WebPermissionStatus)>& callback, | |
| 391 PermissionStatus status) { | |
| 392 DCHECK(worker_thread_id != kNoWorkerThread); | |
| 393 | |
| 394 WorkerThread::PostTask(worker_thread_id, | |
| 395 base::Bind(callback, GetWebPermissionStatus(status))); | |
| 396 } | |
| 397 | |
| 398 void PermissionDispatcher::GetNextPermissionChange( | |
| 399 blink::WebPermissionType type, | |
| 400 const std::string& origin, | |
| 401 WebPermissionObserver* observer, | |
| 402 PermissionStatus current_status) { | |
| 403 GetPermissionServicePtr()->GetNextPermissionChange( | |
| 404 GetPermissionName(type), | |
| 405 origin, | |
| 406 current_status, | |
| 407 base::Bind(&PermissionDispatcher::OnPermissionChanged, | |
| 408 base::Unretained(this), | |
| 409 type, | |
| 410 origin, | |
| 411 base::Unretained(observer))); | |
| 412 } | |
| 413 | |
| 414 } // namespace content | |
| OLD | NEW |