Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / child / permissions / permission_dispatcher.cc
blob4c2e054e0cf5b8229bd507016c9871a0c6c9c457
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.
5 #include "content/child/permissions/permission_dispatcher.h"
7 #include "base/callback.h"
8 #include "content/child/worker_task_runner.h"
9 #include "content/public/common/service_registry.h"
10 #include "third_party/WebKit/public/platform/WebURL.h"
11 #include "third_party/WebKit/public/platform/modules/permissions/WebPermissionObserver.h"
12 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
14 using blink::WebPermissionObserver;
16 namespace content {
18 namespace {
20 PermissionName GetPermissionName(blink::WebPermissionType type) {
21 switch (type) {
22 case blink::WebPermissionTypeGeolocation:
23 return PERMISSION_NAME_GEOLOCATION;
24 case blink::WebPermissionTypeNotifications:
25 return PERMISSION_NAME_NOTIFICATIONS;
26 case blink::WebPermissionTypePushNotifications:
27 return PERMISSION_NAME_PUSH_NOTIFICATIONS;
28 case blink::WebPermissionTypeMidiSysEx:
29 return PERMISSION_NAME_MIDI_SYSEX;
30 case blink::WebPermissionTypeDurableStorage:
31 return PERMISSION_NAME_DURABLE_STORAGE;
32 case blink::WebPermissionTypeMidi:
33 return PERMISSION_NAME_MIDI;
34 default:
35 // The default statement is only there to prevent compilation failures if
36 // WebPermissionType enum gets extended.
37 NOTREACHED();
38 return PERMISSION_NAME_GEOLOCATION;
42 PermissionStatus GetPermissionStatus(blink::WebPermissionStatus status) {
43 switch (status) {
44 case blink::WebPermissionStatusGranted:
45 return PERMISSION_STATUS_GRANTED;
46 case blink::WebPermissionStatusDenied:
47 return PERMISSION_STATUS_DENIED;
48 case blink::WebPermissionStatusPrompt:
49 return PERMISSION_STATUS_ASK;
52 NOTREACHED();
53 return PERMISSION_STATUS_DENIED;
56 blink::WebPermissionStatus GetWebPermissionStatus(PermissionStatus status) {
57 switch (status) {
58 case PERMISSION_STATUS_GRANTED:
59 return blink::WebPermissionStatusGranted;
60 case PERMISSION_STATUS_DENIED:
61 return blink::WebPermissionStatusDenied;
62 case PERMISSION_STATUS_ASK:
63 return blink::WebPermissionStatusPrompt;
66 NOTREACHED();
67 return blink::WebPermissionStatusDenied;
70 const int kNoWorkerThread = 0;
72 } // anonymous namespace
74 // static
75 bool PermissionDispatcher::IsObservable(blink::WebPermissionType type) {
76 return type == blink::WebPermissionTypeGeolocation ||
77 type == blink::WebPermissionTypeNotifications ||
78 type == blink::WebPermissionTypePushNotifications ||
79 type == blink::WebPermissionTypeMidiSysEx;
82 PermissionDispatcher::PermissionDispatcher(ServiceRegistry* service_registry)
83 : service_registry_(service_registry) {
86 PermissionDispatcher::~PermissionDispatcher() {
89 void PermissionDispatcher::queryPermission(
90 blink::WebPermissionType type,
91 const blink::WebURL& origin,
92 blink::WebPermissionCallback* callback) {
93 QueryPermissionInternal(
94 type, origin.string().utf8(), callback, kNoWorkerThread);
97 void PermissionDispatcher::requestPermission(
98 blink::WebPermissionType type,
99 const blink::WebURL& origin,
100 blink::WebPermissionCallback* callback) {
101 RequestPermissionInternal(
102 type, origin.string().utf8(), callback, kNoWorkerThread);
105 void PermissionDispatcher::requestPermissions(
106 const blink::WebVector<blink::WebPermissionType>& types,
107 const blink::WebURL& origin,
108 blink::WebPermissionsCallback* callback) {
109 RequestPermissionsInternal(
110 types, origin.string().utf8(), callback, kNoWorkerThread);
113 void PermissionDispatcher::revokePermission(
114 blink::WebPermissionType type,
115 const blink::WebURL& origin,
116 blink::WebPermissionCallback* callback) {
117 RevokePermissionInternal(
118 type, origin.string().utf8(), callback, kNoWorkerThread);
121 void PermissionDispatcher::startListening(
122 blink::WebPermissionType type,
123 const blink::WebURL& origin,
124 WebPermissionObserver* observer) {
125 if (!IsObservable(type))
126 return;
128 RegisterObserver(observer);
130 GetNextPermissionChange(type,
131 origin.string().utf8(),
132 observer,
133 // We initialize with an arbitrary value because the
134 // mojo service wants a value. Worst case, the
135 // observer will get notified about a non-change which
136 // should be a no-op. After the first notification,
137 // GetNextPermissionChange will be called with the
138 // latest known value.
139 PERMISSION_STATUS_ASK);
142 void PermissionDispatcher::stopListening(WebPermissionObserver* observer) {
143 UnregisterObserver(observer);
146 void PermissionDispatcher::QueryPermissionForWorker(
147 blink::WebPermissionType type,
148 const std::string& origin,
149 blink::WebPermissionCallback* callback,
150 int worker_thread_id) {
151 QueryPermissionInternal(type, origin, callback, worker_thread_id);
154 void PermissionDispatcher::RequestPermissionForWorker(
155 blink::WebPermissionType type,
156 const std::string& origin,
157 blink::WebPermissionCallback* callback,
158 int worker_thread_id) {
159 RequestPermissionInternal(type, origin, callback, worker_thread_id);
162 void PermissionDispatcher::RequestPermissionsForWorker(
163 const blink::WebVector<blink::WebPermissionType>& types,
164 const std::string& origin,
165 blink::WebPermissionsCallback* callback,
166 int worker_thread_id) {
167 RequestPermissionsInternal(types, origin, callback, worker_thread_id);
170 void PermissionDispatcher::RevokePermissionForWorker(
171 blink::WebPermissionType type,
172 const std::string& origin,
173 blink::WebPermissionCallback* callback,
174 int worker_thread_id) {
175 RevokePermissionInternal(type, origin, callback, worker_thread_id);
178 void PermissionDispatcher::StartListeningForWorker(
179 blink::WebPermissionType type,
180 const std::string& origin,
181 int worker_thread_id,
182 const base::Callback<void(blink::WebPermissionStatus)>& callback) {
183 GetPermissionServicePtr()->GetNextPermissionChange(
184 GetPermissionName(type),
185 origin,
186 // We initialize with an arbitrary value because the mojo service wants a
187 // value. Worst case, the observer will get notified about a non-change
188 // which should be a no-op. After the first notification,
189 // GetNextPermissionChange will be called with the latest known value.
190 PERMISSION_STATUS_ASK,
191 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker,
192 base::Unretained(this),
193 worker_thread_id,
194 callback));
197 void PermissionDispatcher::GetNextPermissionChangeForWorker(
198 blink::WebPermissionType type,
199 const std::string& origin,
200 blink::WebPermissionStatus status,
201 int worker_thread_id,
202 const base::Callback<void(blink::WebPermissionStatus)>& callback) {
203 GetPermissionServicePtr()->GetNextPermissionChange(
204 GetPermissionName(type),
205 origin,
206 GetPermissionStatus(status),
207 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker,
208 base::Unretained(this),
209 worker_thread_id,
210 callback));
213 // static
214 void PermissionDispatcher::RunPermissionCallbackOnWorkerThread(
215 scoped_ptr<blink::WebPermissionCallback> callback,
216 blink::WebPermissionStatus status) {
217 callback->onSuccess(status);
220 void PermissionDispatcher::RunPermissionsCallbackOnWorkerThread(
221 scoped_ptr<blink::WebPermissionsCallback> callback,
222 scoped_ptr<blink::WebVector<blink::WebPermissionStatus>> statuses) {
223 callback->onSuccess(blink::adoptWebPtr(statuses.release()));
226 PermissionServicePtr& PermissionDispatcher::GetPermissionServicePtr() {
227 if (!permission_service_.get()) {
228 service_registry_->ConnectToRemoteService(
229 mojo::GetProxy(&permission_service_));
231 return permission_service_;
234 void PermissionDispatcher::QueryPermissionInternal(
235 blink::WebPermissionType type,
236 const std::string& origin,
237 blink::WebPermissionCallback* callback,
238 int worker_thread_id) {
239 // We need to save the |callback| in an ScopedPtrHashMap so if |this| gets
240 // deleted, the callback will not leak. In the case of |this| gets deleted,
241 // the |permission_service_| pipe will be destroyed too so OnQueryPermission
242 // will not be called.
243 uintptr_t callback_key = reinterpret_cast<uintptr_t>(callback);
244 permission_callbacks_.add(callback_key,
245 scoped_ptr<blink::WebPermissionCallback>(callback));
247 GetPermissionServicePtr()->HasPermission(
248 GetPermissionName(type),
249 origin,
250 base::Bind(&PermissionDispatcher::OnPermissionResponse,
251 base::Unretained(this),
252 worker_thread_id,
253 callback_key));
256 void PermissionDispatcher::RequestPermissionInternal(
257 blink::WebPermissionType type,
258 const std::string& origin,
259 blink::WebPermissionCallback* callback,
260 int worker_thread_id) {
261 // We need to save the |callback| in an ScopedPtrHashMap so if |this| gets
262 // deleted, the callback will not leak. In the case of |this| gets deleted,
263 // the |permission_service_| pipe will be destroyed too so OnQueryPermission
264 // will not be called.
265 uintptr_t callback_key = reinterpret_cast<uintptr_t>(callback);
266 permission_callbacks_.add(callback_key,
267 scoped_ptr<blink::WebPermissionCallback>(callback));
269 GetPermissionServicePtr()->RequestPermission(
270 GetPermissionName(type),
271 origin,
272 blink::WebUserGestureIndicator::isProcessingUserGesture(),
273 base::Bind(&PermissionDispatcher::OnPermissionResponse,
274 base::Unretained(this),
275 worker_thread_id,
276 callback_key));
279 void PermissionDispatcher::RequestPermissionsInternal(
280 const blink::WebVector<blink::WebPermissionType>& types,
281 const std::string& origin,
282 blink::WebPermissionsCallback* callback,
283 int worker_thread_id) {
284 // We need to save the |callback| in an ScopedVector so if |this| gets
285 // deleted, the callback will not leak. In the case of |this| gets deleted,
286 // the |permission_service_| pipe will be destroyed too so OnQueryPermission
287 // will not be called.
288 uintptr_t callback_key = reinterpret_cast<uintptr_t>(callback);
289 permissions_callbacks_.add(callback_key,
290 scoped_ptr<blink::WebPermissionsCallback>(callback));
292 mojo::Array<PermissionName> names(types.size());
293 for (size_t i = 0; i < types.size(); ++i)
294 names[i] = GetPermissionName(types[i]);
296 GetPermissionServicePtr()->RequestPermissions(
297 names.Pass(),
298 origin,
299 blink::WebUserGestureIndicator::isProcessingUserGesture(),
300 base::Bind(&PermissionDispatcher::OnRequestPermissionsResponse,
301 base::Unretained(this),
302 worker_thread_id,
303 callback_key));
306 void PermissionDispatcher::RevokePermissionInternal(
307 blink::WebPermissionType type,
308 const std::string& origin,
309 blink::WebPermissionCallback* callback,
310 int worker_thread_id) {
311 // We need to save the |callback| in an ScopedPtrHashMap so if |this| gets
312 // deleted, the callback will not leak. In the case of |this| gets deleted,
313 // the |permission_service_| pipe will be destroyed too so OnQueryPermission
314 // will not be called.
315 uintptr_t callback_key = reinterpret_cast<uintptr_t>(callback);
316 permission_callbacks_.add(callback_key,
317 scoped_ptr<blink::WebPermissionCallback>(callback));
319 GetPermissionServicePtr()->RevokePermission(
320 GetPermissionName(type),
321 origin,
322 base::Bind(&PermissionDispatcher::OnPermissionResponse,
323 base::Unretained(this),
324 worker_thread_id,
325 callback_key));
328 void PermissionDispatcher::OnPermissionResponse(
329 int worker_thread_id,
330 uintptr_t callback_key,
331 PermissionStatus result) {
332 scoped_ptr<blink::WebPermissionCallback> callback =
333 permission_callbacks_.take_and_erase(callback_key);
334 blink::WebPermissionStatus status = GetWebPermissionStatus(result);
336 if (worker_thread_id != kNoWorkerThread) {
337 // If the worker is no longer running, ::PostTask() will return false and
338 // gracefully fail, destroying the callback too.
339 WorkerTaskRunner::Instance()->PostTask(
340 worker_thread_id,
341 base::Bind(&PermissionDispatcher::RunPermissionCallbackOnWorkerThread,
342 base::Passed(&callback), status));
343 return;
346 callback->onSuccess(status);
349 void PermissionDispatcher::OnRequestPermissionsResponse(
350 int worker_thread_id,
351 uintptr_t callback_key,
352 const mojo::Array<PermissionStatus>& result) {
353 scoped_ptr<blink::WebPermissionsCallback> callback =
354 permissions_callbacks_.take_and_erase(callback_key);
355 scoped_ptr<blink::WebVector<blink::WebPermissionStatus>> statuses(
356 new blink::WebVector<blink::WebPermissionStatus>(result.size()));
358 for (size_t i = 0; i < result.size(); i++)
359 statuses->operator[](i) = GetWebPermissionStatus(result[i]);
361 if (worker_thread_id != kNoWorkerThread) {
362 // If the worker is no longer running, ::PostTask() will return false and
363 // gracefully fail, destroying the callback too.
364 WorkerTaskRunner::Instance()->PostTask(
365 worker_thread_id,
366 base::Bind(&PermissionDispatcher::RunPermissionsCallbackOnWorkerThread,
367 base::Passed(&callback),
368 base::Passed(&statuses)));
369 return;
372 callback->onSuccess(blink::adoptWebPtr(statuses.release()));
375 void PermissionDispatcher::OnPermissionChanged(
376 blink::WebPermissionType type,
377 const std::string& origin,
378 WebPermissionObserver* observer,
379 PermissionStatus status) {
380 if (!IsObserverRegistered(observer))
381 return;
383 observer->permissionChanged(type, GetWebPermissionStatus(status));
385 GetNextPermissionChange(type, origin, observer, status);
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);
394 WorkerTaskRunner::Instance()->PostTask(
395 worker_thread_id, base::Bind(callback, GetWebPermissionStatus(status)));
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)));
414 } // namespace content