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
;
20 PermissionName
GetPermissionName(blink::WebPermissionType 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
;
35 // The default statement is only there to prevent compilation failures if
36 // WebPermissionType enum gets extended.
38 return PERMISSION_NAME_GEOLOCATION
;
42 PermissionStatus
GetPermissionStatus(blink::WebPermissionStatus 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
;
53 return PERMISSION_STATUS_DENIED
;
56 blink::WebPermissionStatus
GetWebPermissionStatus(PermissionStatus 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
;
67 return blink::WebPermissionStatusDenied
;
70 const int kNoWorkerThread
= 0;
72 } // anonymous namespace
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
))
128 RegisterObserver(observer
);
130 GetNextPermissionChange(type
,
131 origin
.string().utf8(),
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
),
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),
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
),
206 GetPermissionStatus(status
),
207 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker
,
208 base::Unretained(this),
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
),
250 base::Bind(&PermissionDispatcher::OnPermissionResponse
,
251 base::Unretained(this),
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
),
272 blink::WebUserGestureIndicator::isProcessingUserGesture(),
273 base::Bind(&PermissionDispatcher::OnPermissionResponse
,
274 base::Unretained(this),
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(
299 blink::WebUserGestureIndicator::isProcessingUserGesture(),
300 base::Bind(&PermissionDispatcher::OnRequestPermissionsResponse
,
301 base::Unretained(this),
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
),
322 base::Bind(&PermissionDispatcher::OnPermissionResponse
,
323 base::Unretained(this),
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(
341 base::Bind(&PermissionDispatcher::RunPermissionCallbackOnWorkerThread
,
342 base::Passed(&callback
), status
));
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(
366 base::Bind(&PermissionDispatcher::RunPermissionsCallbackOnWorkerThread
,
367 base::Passed(&callback
),
368 base::Passed(&statuses
)));
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
))
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
),
407 base::Bind(&PermissionDispatcher::OnPermissionChanged
,
408 base::Unretained(this),
411 base::Unretained(observer
)));
414 } // namespace content