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"
13 using blink::WebPermissionObserver
;
19 PermissionName
GetPermissionName(blink::WebPermissionType type
) {
21 case blink::WebPermissionTypeGeolocation
:
22 return PERMISSION_NAME_GEOLOCATION
;
23 case blink::WebPermissionTypeNotifications
:
24 return PERMISSION_NAME_NOTIFICATIONS
;
25 case blink::WebPermissionTypePushNotifications
:
26 return PERMISSION_NAME_PUSH_NOTIFICATIONS
;
27 case blink::WebPermissionTypeMidiSysEx
:
28 return PERMISSION_NAME_MIDI_SYSEX
;
30 // The default statement is only there to prevent compilation failures if
31 // WebPermissionType enum gets extended.
33 return PERMISSION_NAME_GEOLOCATION
;
37 PermissionStatus
GetPermissionStatus(blink::WebPermissionStatus status
) {
39 case blink::WebPermissionStatusGranted
:
40 return PERMISSION_STATUS_GRANTED
;
41 case blink::WebPermissionStatusDenied
:
42 return PERMISSION_STATUS_DENIED
;
43 case blink::WebPermissionStatusPrompt
:
44 return PERMISSION_STATUS_ASK
;
48 return PERMISSION_STATUS_DENIED
;
51 blink::WebPermissionStatus
GetWebPermissionStatus(PermissionStatus status
) {
53 case PERMISSION_STATUS_GRANTED
:
54 return blink::WebPermissionStatusGranted
;
55 case PERMISSION_STATUS_DENIED
:
56 return blink::WebPermissionStatusDenied
;
57 case PERMISSION_STATUS_ASK
:
58 return blink::WebPermissionStatusPrompt
;
62 return blink::WebPermissionStatusDenied
;
65 const int kNoWorkerThread
= 0;
67 } // anonymous namespace
70 bool PermissionDispatcher::IsObservable(blink::WebPermissionType type
) {
71 return type
== blink::WebPermissionTypeGeolocation
||
72 type
== blink::WebPermissionTypeNotifications
||
73 type
== blink::WebPermissionTypePushNotifications
||
74 type
== blink::WebPermissionTypeMidiSysEx
;
77 PermissionDispatcher::CallbackInformation::CallbackInformation(
78 blink::WebPermissionCallback
* callback
,
80 : callback_(callback
),
81 worker_thread_id_(worker_thread_id
) {
84 blink::WebPermissionCallback
*
85 PermissionDispatcher::CallbackInformation::callback() const {
86 return callback_
.get();
89 int PermissionDispatcher::CallbackInformation::worker_thread_id() const {
90 return worker_thread_id_
;
93 blink::WebPermissionCallback
*
94 PermissionDispatcher::CallbackInformation::ReleaseCallback() {
95 return callback_
.release();
98 PermissionDispatcher::CallbackInformation::~CallbackInformation() {
101 PermissionDispatcher::PermissionDispatcher(ServiceRegistry
* service_registry
)
102 : service_registry_(service_registry
) {
105 PermissionDispatcher::~PermissionDispatcher() {
108 void PermissionDispatcher::queryPermission(
109 blink::WebPermissionType type
,
110 const blink::WebURL
& origin
,
111 blink::WebPermissionCallback
* callback
) {
112 QueryPermissionInternal(
113 type
, origin
.string().utf8(), callback
, kNoWorkerThread
);
116 void PermissionDispatcher::revokePermission(
117 blink::WebPermissionType type
,
118 const blink::WebURL
& origin
,
119 blink::WebPermissionCallback
* callback
) {
120 RevokePermissionInternal(
121 type
, origin
.string().utf8(), callback
, kNoWorkerThread
);
124 void PermissionDispatcher::startListening(
125 blink::WebPermissionType type
,
126 const blink::WebURL
& origin
,
127 WebPermissionObserver
* observer
) {
128 if (!IsObservable(type
))
131 RegisterObserver(observer
);
133 GetNextPermissionChange(type
,
134 origin
.string().utf8(),
136 // We initialize with an arbitrary value because the
137 // mojo service wants a value. Worst case, the
138 // observer will get notified about a non-change which
139 // should be a no-op. After the first notification,
140 // GetNextPermissionChange will be called with the
141 // latest known value.
142 PERMISSION_STATUS_ASK
);
145 void PermissionDispatcher::stopListening(WebPermissionObserver
* observer
) {
146 UnregisterObserver(observer
);
149 void PermissionDispatcher::QueryPermissionForWorker(
150 blink::WebPermissionType type
,
151 const std::string
& origin
,
152 blink::WebPermissionCallback
* callback
,
153 int worker_thread_id
) {
154 QueryPermissionInternal(type
, origin
, callback
, worker_thread_id
);
157 void PermissionDispatcher::RevokePermissionForWorker(
158 blink::WebPermissionType type
,
159 const std::string
& origin
,
160 blink::WebPermissionCallback
* callback
,
161 int worker_thread_id
) {
162 RevokePermissionInternal(type
, origin
, callback
, worker_thread_id
);
165 void PermissionDispatcher::StartListeningForWorker(
166 blink::WebPermissionType type
,
167 const std::string
& origin
,
168 int worker_thread_id
,
169 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
) {
170 GetPermissionServicePtr()->GetNextPermissionChange(
171 GetPermissionName(type
),
173 // We initialize with an arbitrary value because the mojo service wants a
174 // value. Worst case, the observer will get notified about a non-change
175 // which should be a no-op. After the first notification,
176 // GetNextPermissionChange will be called with the latest known value.
177 PERMISSION_STATUS_ASK
,
178 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker
,
179 base::Unretained(this),
184 void PermissionDispatcher::GetNextPermissionChangeForWorker(
185 blink::WebPermissionType type
,
186 const std::string
& origin
,
187 blink::WebPermissionStatus status
,
188 int worker_thread_id
,
189 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
) {
190 GetPermissionServicePtr()->GetNextPermissionChange(
191 GetPermissionName(type
),
193 GetPermissionStatus(status
),
194 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker
,
195 base::Unretained(this),
201 void PermissionDispatcher::RunCallbackOnWorkerThread(
202 blink::WebPermissionCallback
* callback
,
203 scoped_ptr
<blink::WebPermissionStatus
> status
) {
204 callback
->onSuccess(status
.release());
208 PermissionServicePtr
& PermissionDispatcher::GetPermissionServicePtr() {
209 if (!permission_service_
.get()) {
210 service_registry_
->ConnectToRemoteService(
211 mojo::GetProxy(&permission_service_
));
213 return permission_service_
;
216 void PermissionDispatcher::QueryPermissionInternal(
217 blink::WebPermissionType type
,
218 const std::string
& origin
,
219 blink::WebPermissionCallback
* callback
,
220 int worker_thread_id
) {
221 // We need to save the |callback| in an IDMap so if |this| gets deleted, the
222 // callback will not leak. In the case of |this| gets deleted, the
223 // |permission_service_| pipe will be destroyed too so OnQueryPermission will
225 int request_id
= pending_callbacks_
.Add(
226 new CallbackInformation(callback
, worker_thread_id
));
227 GetPermissionServicePtr()->HasPermission(
228 GetPermissionName(type
),
230 base::Bind(&PermissionDispatcher::OnPermissionResponse
,
231 base::Unretained(this),
235 void PermissionDispatcher::RevokePermissionInternal(
236 blink::WebPermissionType type
,
237 const std::string
& origin
,
238 blink::WebPermissionCallback
* callback
,
239 int worker_thread_id
) {
240 // We need to save the |callback| in an IDMap so if |this| gets deleted, the
241 // callback will not leak. In the case of |this| gets deleted, the
242 // |permission_service_| pipe will be destroyed too so OnQueryPermission will
244 int request_id
= pending_callbacks_
.Add(
245 new CallbackInformation(callback
, worker_thread_id
));
246 GetPermissionServicePtr()->RevokePermission(
247 GetPermissionName(type
),
249 base::Bind(&PermissionDispatcher::OnPermissionResponse
,
250 base::Unretained(this),
254 void PermissionDispatcher::OnPermissionResponse(int request_id
,
255 PermissionStatus result
) {
256 CallbackInformation
* callback_information
=
257 pending_callbacks_
.Lookup(request_id
);
258 DCHECK(callback_information
&& callback_information
->callback());
259 scoped_ptr
<blink::WebPermissionStatus
> status(
260 new blink::WebPermissionStatus(GetWebPermissionStatus(result
)));
262 if (callback_information
->worker_thread_id() != kNoWorkerThread
) {
263 blink::WebPermissionCallback
* callback
=
264 callback_information
->ReleaseCallback();
265 int worker_thread_id
= callback_information
->worker_thread_id();
266 pending_callbacks_
.Remove(request_id
);
268 // If the worker is no longer running, ::PostTask() will return false and
269 // gracefully fail, destroying the callback too.
270 WorkerTaskRunner::Instance()->PostTask(
272 base::Bind(&PermissionDispatcher::RunCallbackOnWorkerThread
,
273 base::Unretained(callback
),
274 base::Passed(&status
)));
278 callback_information
->callback()->onSuccess(status
.release());
279 pending_callbacks_
.Remove(request_id
);
282 void PermissionDispatcher::OnPermissionChanged(
283 blink::WebPermissionType type
,
284 const std::string
& origin
,
285 WebPermissionObserver
* observer
,
286 PermissionStatus status
) {
287 if (!IsObserverRegistered(observer
))
290 observer
->permissionChanged(type
, GetWebPermissionStatus(status
));
292 GetNextPermissionChange(type
, origin
, observer
, status
);
295 void PermissionDispatcher::OnPermissionChangedForWorker(
296 int worker_thread_id
,
297 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
,
298 PermissionStatus status
) {
299 DCHECK(worker_thread_id
!= kNoWorkerThread
);
301 WorkerTaskRunner::Instance()->PostTask(
302 worker_thread_id
, base::Bind(callback
, GetWebPermissionStatus(status
)));
305 void PermissionDispatcher::GetNextPermissionChange(
306 blink::WebPermissionType type
,
307 const std::string
& origin
,
308 WebPermissionObserver
* observer
,
309 PermissionStatus current_status
) {
310 GetPermissionServicePtr()->GetNextPermissionChange(
311 GetPermissionName(type
),
314 base::Bind(&PermissionDispatcher::OnPermissionChanged
,
315 base::Unretained(this),
318 base::Unretained(observer
)));
321 } // namespace content