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
;
31 // The default statement is only there to prevent compilation failures if
32 // WebPermissionType enum gets extended.
34 return PERMISSION_NAME_GEOLOCATION
;
38 PermissionStatus
GetPermissionStatus(blink::WebPermissionStatus status
) {
40 case blink::WebPermissionStatusGranted
:
41 return PERMISSION_STATUS_GRANTED
;
42 case blink::WebPermissionStatusDenied
:
43 return PERMISSION_STATUS_DENIED
;
44 case blink::WebPermissionStatusPrompt
:
45 return PERMISSION_STATUS_ASK
;
49 return PERMISSION_STATUS_DENIED
;
52 blink::WebPermissionStatus
GetWebPermissionStatus(PermissionStatus status
) {
54 case PERMISSION_STATUS_GRANTED
:
55 return blink::WebPermissionStatusGranted
;
56 case PERMISSION_STATUS_DENIED
:
57 return blink::WebPermissionStatusDenied
;
58 case PERMISSION_STATUS_ASK
:
59 return blink::WebPermissionStatusPrompt
;
63 return blink::WebPermissionStatusDenied
;
66 const int kNoWorkerThread
= 0;
68 } // anonymous namespace
71 bool PermissionDispatcher::IsObservable(blink::WebPermissionType type
) {
72 return type
== blink::WebPermissionTypeGeolocation
||
73 type
== blink::WebPermissionTypeNotifications
||
74 type
== blink::WebPermissionTypePushNotifications
||
75 type
== blink::WebPermissionTypeMidiSysEx
;
78 PermissionDispatcher::CallbackInformation::CallbackInformation(
79 blink::WebPermissionCallback
* callback
,
81 : callback_(callback
),
82 worker_thread_id_(worker_thread_id
) {
85 blink::WebPermissionCallback
*
86 PermissionDispatcher::CallbackInformation::callback() const {
87 return callback_
.get();
90 int PermissionDispatcher::CallbackInformation::worker_thread_id() const {
91 return worker_thread_id_
;
94 blink::WebPermissionCallback
*
95 PermissionDispatcher::CallbackInformation::ReleaseCallback() {
96 return callback_
.release();
99 PermissionDispatcher::CallbackInformation::~CallbackInformation() {
102 PermissionDispatcher::PermissionDispatcher(ServiceRegistry
* service_registry
)
103 : service_registry_(service_registry
) {
106 PermissionDispatcher::~PermissionDispatcher() {
109 void PermissionDispatcher::queryPermission(
110 blink::WebPermissionType type
,
111 const blink::WebURL
& origin
,
112 blink::WebPermissionCallback
* callback
) {
113 QueryPermissionInternal(
114 type
, origin
.string().utf8(), callback
, kNoWorkerThread
);
117 void PermissionDispatcher::requestPermission(
118 blink::WebPermissionType type
,
119 const blink::WebURL
& origin
,
120 blink::WebPermissionCallback
* callback
) {
121 RequestPermissionInternal(
122 type
, origin
.string().utf8(), callback
, kNoWorkerThread
);
125 void PermissionDispatcher::revokePermission(
126 blink::WebPermissionType type
,
127 const blink::WebURL
& origin
,
128 blink::WebPermissionCallback
* callback
) {
129 RevokePermissionInternal(
130 type
, origin
.string().utf8(), callback
, kNoWorkerThread
);
133 void PermissionDispatcher::startListening(
134 blink::WebPermissionType type
,
135 const blink::WebURL
& origin
,
136 WebPermissionObserver
* observer
) {
137 if (!IsObservable(type
))
140 RegisterObserver(observer
);
142 GetNextPermissionChange(type
,
143 origin
.string().utf8(),
145 // We initialize with an arbitrary value because the
146 // mojo service wants a value. Worst case, the
147 // observer will get notified about a non-change which
148 // should be a no-op. After the first notification,
149 // GetNextPermissionChange will be called with the
150 // latest known value.
151 PERMISSION_STATUS_ASK
);
154 void PermissionDispatcher::stopListening(WebPermissionObserver
* observer
) {
155 UnregisterObserver(observer
);
158 void PermissionDispatcher::QueryPermissionForWorker(
159 blink::WebPermissionType type
,
160 const std::string
& origin
,
161 blink::WebPermissionCallback
* callback
,
162 int worker_thread_id
) {
163 QueryPermissionInternal(type
, origin
, callback
, worker_thread_id
);
166 void PermissionDispatcher::RequestPermissionForWorker(
167 blink::WebPermissionType type
,
168 const std::string
& origin
,
169 blink::WebPermissionCallback
* callback
,
170 int worker_thread_id
) {
171 RequestPermissionInternal(type
, origin
, callback
, worker_thread_id
);
174 void PermissionDispatcher::RevokePermissionForWorker(
175 blink::WebPermissionType type
,
176 const std::string
& origin
,
177 blink::WebPermissionCallback
* callback
,
178 int worker_thread_id
) {
179 RevokePermissionInternal(type
, origin
, callback
, worker_thread_id
);
182 void PermissionDispatcher::StartListeningForWorker(
183 blink::WebPermissionType type
,
184 const std::string
& origin
,
185 int worker_thread_id
,
186 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
) {
187 GetPermissionServicePtr()->GetNextPermissionChange(
188 GetPermissionName(type
),
190 // We initialize with an arbitrary value because the mojo service wants a
191 // value. Worst case, the observer will get notified about a non-change
192 // which should be a no-op. After the first notification,
193 // GetNextPermissionChange will be called with the latest known value.
194 PERMISSION_STATUS_ASK
,
195 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker
,
196 base::Unretained(this),
201 void PermissionDispatcher::GetNextPermissionChangeForWorker(
202 blink::WebPermissionType type
,
203 const std::string
& origin
,
204 blink::WebPermissionStatus status
,
205 int worker_thread_id
,
206 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
) {
207 GetPermissionServicePtr()->GetNextPermissionChange(
208 GetPermissionName(type
),
210 GetPermissionStatus(status
),
211 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker
,
212 base::Unretained(this),
218 void PermissionDispatcher::RunCallbackOnWorkerThread(
219 blink::WebPermissionCallback
* callback
,
220 scoped_ptr
<blink::WebPermissionStatus
> status
) {
221 callback
->onSuccess(status
.release());
225 PermissionServicePtr
& PermissionDispatcher::GetPermissionServicePtr() {
226 if (!permission_service_
.get()) {
227 service_registry_
->ConnectToRemoteService(
228 mojo::GetProxy(&permission_service_
));
230 return permission_service_
;
233 void PermissionDispatcher::QueryPermissionInternal(
234 blink::WebPermissionType type
,
235 const std::string
& origin
,
236 blink::WebPermissionCallback
* callback
,
237 int worker_thread_id
) {
238 // We need to save the |callback| in an IDMap so if |this| gets deleted, the
239 // callback will not leak. In the case of |this| gets deleted, the
240 // |permission_service_| pipe will be destroyed too so OnQueryPermission will
242 int request_id
= pending_callbacks_
.Add(
243 new CallbackInformation(callback
, worker_thread_id
));
244 GetPermissionServicePtr()->HasPermission(
245 GetPermissionName(type
),
247 base::Bind(&PermissionDispatcher::OnPermissionResponse
,
248 base::Unretained(this),
252 void PermissionDispatcher::RequestPermissionInternal(
253 blink::WebPermissionType type
,
254 const std::string
& origin
,
255 blink::WebPermissionCallback
* callback
,
256 int worker_thread_id
) {
257 // We need to save the |callback| in an IDMap so if |this| gets deleted, the
258 // callback will not leak. In the case of |this| gets deleted, the
259 // |permission_service_| pipe will be destroyed too so OnQueryPermission will
261 int request_id
= pending_callbacks_
.Add(
262 new CallbackInformation(callback
, worker_thread_id
));
263 GetPermissionServicePtr()->RequestPermission(
264 GetPermissionName(type
),
266 blink::WebUserGestureIndicator::isProcessingUserGesture(),
267 base::Bind(&PermissionDispatcher::OnPermissionResponse
,
268 base::Unretained(this),
272 void PermissionDispatcher::RevokePermissionInternal(
273 blink::WebPermissionType type
,
274 const std::string
& origin
,
275 blink::WebPermissionCallback
* callback
,
276 int worker_thread_id
) {
277 // We need to save the |callback| in an IDMap so if |this| gets deleted, the
278 // callback will not leak. In the case of |this| gets deleted, the
279 // |permission_service_| pipe will be destroyed too so OnQueryPermission will
281 int request_id
= pending_callbacks_
.Add(
282 new CallbackInformation(callback
, worker_thread_id
));
283 GetPermissionServicePtr()->RevokePermission(
284 GetPermissionName(type
),
286 base::Bind(&PermissionDispatcher::OnPermissionResponse
,
287 base::Unretained(this),
291 void PermissionDispatcher::OnPermissionResponse(int request_id
,
292 PermissionStatus result
) {
293 CallbackInformation
* callback_information
=
294 pending_callbacks_
.Lookup(request_id
);
295 DCHECK(callback_information
&& callback_information
->callback());
296 scoped_ptr
<blink::WebPermissionStatus
> status(
297 new blink::WebPermissionStatus(GetWebPermissionStatus(result
)));
299 if (callback_information
->worker_thread_id() != kNoWorkerThread
) {
300 blink::WebPermissionCallback
* callback
=
301 callback_information
->ReleaseCallback();
302 int worker_thread_id
= callback_information
->worker_thread_id();
303 pending_callbacks_
.Remove(request_id
);
305 // If the worker is no longer running, ::PostTask() will return false and
306 // gracefully fail, destroying the callback too.
307 WorkerTaskRunner::Instance()->PostTask(
309 base::Bind(&PermissionDispatcher::RunCallbackOnWorkerThread
,
310 base::Unretained(callback
),
311 base::Passed(&status
)));
315 callback_information
->callback()->onSuccess(status
.release());
316 pending_callbacks_
.Remove(request_id
);
319 void PermissionDispatcher::OnPermissionChanged(
320 blink::WebPermissionType type
,
321 const std::string
& origin
,
322 WebPermissionObserver
* observer
,
323 PermissionStatus status
) {
324 if (!IsObserverRegistered(observer
))
327 observer
->permissionChanged(type
, GetWebPermissionStatus(status
));
329 GetNextPermissionChange(type
, origin
, observer
, status
);
332 void PermissionDispatcher::OnPermissionChangedForWorker(
333 int worker_thread_id
,
334 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
,
335 PermissionStatus status
) {
336 DCHECK(worker_thread_id
!= kNoWorkerThread
);
338 WorkerTaskRunner::Instance()->PostTask(
339 worker_thread_id
, base::Bind(callback
, GetWebPermissionStatus(status
)));
342 void PermissionDispatcher::GetNextPermissionChange(
343 blink::WebPermissionType type
,
344 const std::string
& origin
,
345 WebPermissionObserver
* observer
,
346 PermissionStatus current_status
) {
347 GetPermissionServicePtr()->GetNextPermissionChange(
348 GetPermissionName(type
),
351 base::Bind(&PermissionDispatcher::OnPermissionChanged
,
352 base::Unretained(this),
355 base::Unretained(observer
)));
358 } // namespace content