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::CallbackInformation::CallbackInformation(
83 blink::WebPermissionCallback
* callback
,
85 : callback_(callback
),
86 worker_thread_id_(worker_thread_id
) {
89 blink::WebPermissionCallback
*
90 PermissionDispatcher::CallbackInformation::callback() const {
91 return callback_
.get();
94 int PermissionDispatcher::CallbackInformation::worker_thread_id() const {
95 return worker_thread_id_
;
98 blink::WebPermissionCallback
*
99 PermissionDispatcher::CallbackInformation::ReleaseCallback() {
100 return callback_
.release();
103 PermissionDispatcher::CallbackInformation::~CallbackInformation() {
106 PermissionDispatcher::PermissionDispatcher(ServiceRegistry
* service_registry
)
107 : service_registry_(service_registry
) {
110 PermissionDispatcher::~PermissionDispatcher() {
113 void PermissionDispatcher::queryPermission(
114 blink::WebPermissionType type
,
115 const blink::WebURL
& origin
,
116 blink::WebPermissionCallback
* callback
) {
117 QueryPermissionInternal(
118 type
, origin
.string().utf8(), callback
, kNoWorkerThread
);
121 void PermissionDispatcher::requestPermission(
122 blink::WebPermissionType type
,
123 const blink::WebURL
& origin
,
124 blink::WebPermissionCallback
* callback
) {
125 RequestPermissionInternal(
126 type
, origin
.string().utf8(), callback
, kNoWorkerThread
);
129 void PermissionDispatcher::revokePermission(
130 blink::WebPermissionType type
,
131 const blink::WebURL
& origin
,
132 blink::WebPermissionCallback
* callback
) {
133 RevokePermissionInternal(
134 type
, origin
.string().utf8(), callback
, kNoWorkerThread
);
137 void PermissionDispatcher::startListening(
138 blink::WebPermissionType type
,
139 const blink::WebURL
& origin
,
140 WebPermissionObserver
* observer
) {
141 if (!IsObservable(type
))
144 RegisterObserver(observer
);
146 GetNextPermissionChange(type
,
147 origin
.string().utf8(),
149 // We initialize with an arbitrary value because the
150 // mojo service wants a value. Worst case, the
151 // observer will get notified about a non-change which
152 // should be a no-op. After the first notification,
153 // GetNextPermissionChange will be called with the
154 // latest known value.
155 PERMISSION_STATUS_ASK
);
158 void PermissionDispatcher::stopListening(WebPermissionObserver
* observer
) {
159 UnregisterObserver(observer
);
162 void PermissionDispatcher::QueryPermissionForWorker(
163 blink::WebPermissionType type
,
164 const std::string
& origin
,
165 blink::WebPermissionCallback
* callback
,
166 int worker_thread_id
) {
167 QueryPermissionInternal(type
, origin
, callback
, worker_thread_id
);
170 void PermissionDispatcher::RequestPermissionForWorker(
171 blink::WebPermissionType type
,
172 const std::string
& origin
,
173 blink::WebPermissionCallback
* callback
,
174 int worker_thread_id
) {
175 RequestPermissionInternal(type
, origin
, callback
, worker_thread_id
);
178 void PermissionDispatcher::RevokePermissionForWorker(
179 blink::WebPermissionType type
,
180 const std::string
& origin
,
181 blink::WebPermissionCallback
* callback
,
182 int worker_thread_id
) {
183 RevokePermissionInternal(type
, origin
, callback
, worker_thread_id
);
186 void PermissionDispatcher::StartListeningForWorker(
187 blink::WebPermissionType type
,
188 const std::string
& origin
,
189 int worker_thread_id
,
190 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
) {
191 GetPermissionServicePtr()->GetNextPermissionChange(
192 GetPermissionName(type
),
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 PERMISSION_STATUS_ASK
,
199 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker
,
200 base::Unretained(this),
205 void PermissionDispatcher::GetNextPermissionChangeForWorker(
206 blink::WebPermissionType type
,
207 const std::string
& origin
,
208 blink::WebPermissionStatus status
,
209 int worker_thread_id
,
210 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
) {
211 GetPermissionServicePtr()->GetNextPermissionChange(
212 GetPermissionName(type
),
214 GetPermissionStatus(status
),
215 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker
,
216 base::Unretained(this),
222 void PermissionDispatcher::RunCallbackOnWorkerThread(
223 blink::WebPermissionCallback
* callback
,
224 scoped_ptr
<blink::WebPermissionStatus
> status
) {
225 callback
->onSuccess(status
.release());
229 PermissionServicePtr
& PermissionDispatcher::GetPermissionServicePtr() {
230 if (!permission_service_
.get()) {
231 service_registry_
->ConnectToRemoteService(
232 mojo::GetProxy(&permission_service_
));
234 return permission_service_
;
237 void PermissionDispatcher::QueryPermissionInternal(
238 blink::WebPermissionType type
,
239 const std::string
& origin
,
240 blink::WebPermissionCallback
* callback
,
241 int worker_thread_id
) {
242 // We need to save the |callback| in an IDMap so if |this| gets deleted, the
243 // callback will not leak. In the case of |this| gets deleted, the
244 // |permission_service_| pipe will be destroyed too so OnQueryPermission will
246 int request_id
= pending_callbacks_
.Add(
247 new CallbackInformation(callback
, worker_thread_id
));
248 GetPermissionServicePtr()->HasPermission(
249 GetPermissionName(type
),
251 base::Bind(&PermissionDispatcher::OnPermissionResponse
,
252 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 IDMap so if |this| gets deleted, the
262 // callback will not leak. In the case of |this| gets deleted, the
263 // |permission_service_| pipe will be destroyed too so OnQueryPermission will
265 int request_id
= pending_callbacks_
.Add(
266 new CallbackInformation(callback
, worker_thread_id
));
267 GetPermissionServicePtr()->RequestPermission(
268 GetPermissionName(type
),
270 blink::WebUserGestureIndicator::isProcessingUserGesture(),
271 base::Bind(&PermissionDispatcher::OnPermissionResponse
,
272 base::Unretained(this),
276 void PermissionDispatcher::RevokePermissionInternal(
277 blink::WebPermissionType type
,
278 const std::string
& origin
,
279 blink::WebPermissionCallback
* callback
,
280 int worker_thread_id
) {
281 // We need to save the |callback| in an IDMap so if |this| gets deleted, the
282 // callback will not leak. In the case of |this| gets deleted, the
283 // |permission_service_| pipe will be destroyed too so OnQueryPermission will
285 int request_id
= pending_callbacks_
.Add(
286 new CallbackInformation(callback
, worker_thread_id
));
287 GetPermissionServicePtr()->RevokePermission(
288 GetPermissionName(type
),
290 base::Bind(&PermissionDispatcher::OnPermissionResponse
,
291 base::Unretained(this),
295 void PermissionDispatcher::OnPermissionResponse(int request_id
,
296 PermissionStatus result
) {
297 CallbackInformation
* callback_information
=
298 pending_callbacks_
.Lookup(request_id
);
299 DCHECK(callback_information
&& callback_information
->callback());
300 scoped_ptr
<blink::WebPermissionStatus
> status(
301 new blink::WebPermissionStatus(GetWebPermissionStatus(result
)));
303 if (callback_information
->worker_thread_id() != kNoWorkerThread
) {
304 blink::WebPermissionCallback
* callback
=
305 callback_information
->ReleaseCallback();
306 int worker_thread_id
= callback_information
->worker_thread_id();
307 pending_callbacks_
.Remove(request_id
);
309 // If the worker is no longer running, ::PostTask() will return false and
310 // gracefully fail, destroying the callback too.
311 WorkerTaskRunner::Instance()->PostTask(
313 base::Bind(&PermissionDispatcher::RunCallbackOnWorkerThread
,
314 base::Unretained(callback
),
315 base::Passed(&status
)));
319 callback_information
->callback()->onSuccess(status
.release());
320 pending_callbacks_
.Remove(request_id
);
323 void PermissionDispatcher::OnPermissionChanged(
324 blink::WebPermissionType type
,
325 const std::string
& origin
,
326 WebPermissionObserver
* observer
,
327 PermissionStatus status
) {
328 if (!IsObserverRegistered(observer
))
331 observer
->permissionChanged(type
, GetWebPermissionStatus(status
));
333 GetNextPermissionChange(type
, origin
, observer
, status
);
336 void PermissionDispatcher::OnPermissionChangedForWorker(
337 int worker_thread_id
,
338 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
,
339 PermissionStatus status
) {
340 DCHECK(worker_thread_id
!= kNoWorkerThread
);
342 WorkerTaskRunner::Instance()->PostTask(
343 worker_thread_id
, base::Bind(callback
, GetWebPermissionStatus(status
)));
346 void PermissionDispatcher::GetNextPermissionChange(
347 blink::WebPermissionType type
,
348 const std::string
& origin
,
349 WebPermissionObserver
* observer
,
350 PermissionStatus current_status
) {
351 GetPermissionServicePtr()->GetNextPermissionChange(
352 GetPermissionName(type
),
355 base::Bind(&PermissionDispatcher::OnPermissionChanged
,
356 base::Unretained(this),
359 base::Unretained(observer
)));
362 } // namespace content