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::WebPermissionQueryCallback
* callback
,
80 : callback_(callback
),
81 worker_thread_id_(worker_thread_id
) {
84 blink::WebPermissionQueryCallback
*
85 PermissionDispatcher::CallbackInformation::callback() const {
86 return callback_
.get();
89 int PermissionDispatcher::CallbackInformation::worker_thread_id() const {
90 return worker_thread_id_
;
93 blink::WebPermissionQueryCallback
*
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::WebPermissionQueryCallback
* callback
) {
112 QueryPermissionInternal(
113 type
, origin
.string().utf8(), callback
, kNoWorkerThread
);
116 void PermissionDispatcher::startListening(
117 blink::WebPermissionType type
,
118 const blink::WebURL
& origin
,
119 WebPermissionObserver
* observer
) {
120 if (!IsObservable(type
))
123 RegisterObserver(observer
);
125 GetNextPermissionChange(type
,
126 origin
.string().utf8(),
128 // We initialize with an arbitrary value because the
129 // mojo service wants a value. Worst case, the
130 // observer will get notified about a non-change which
131 // should be a no-op. After the first notification,
132 // GetNextPermissionChange will be called with the
133 // latest known value.
134 PERMISSION_STATUS_ASK
);
137 void PermissionDispatcher::stopListening(WebPermissionObserver
* observer
) {
138 UnregisterObserver(observer
);
141 void PermissionDispatcher::QueryPermissionForWorker(
142 blink::WebPermissionType type
,
143 const std::string
& origin
,
144 blink::WebPermissionQueryCallback
* callback
,
145 int worker_thread_id
) {
146 QueryPermissionInternal(type
, origin
, callback
, worker_thread_id
);
149 void PermissionDispatcher::StartListeningForWorker(
150 blink::WebPermissionType type
,
151 const std::string
& origin
,
152 int worker_thread_id
,
153 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
) {
154 GetPermissionServicePtr()->GetNextPermissionChange(
155 GetPermissionName(type
),
157 // We initialize with an arbitrary value because the mojo service wants a
158 // value. Worst case, the observer will get notified about a non-change
159 // which should be a no-op. After the first notification,
160 // GetNextPermissionChange will be called with the latest known value.
161 PERMISSION_STATUS_ASK
,
162 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker
,
163 base::Unretained(this),
168 void PermissionDispatcher::GetNextPermissionChangeForWorker(
169 blink::WebPermissionType type
,
170 const std::string
& origin
,
171 blink::WebPermissionStatus status
,
172 int worker_thread_id
,
173 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
) {
174 GetPermissionServicePtr()->GetNextPermissionChange(
175 GetPermissionName(type
),
177 GetPermissionStatus(status
),
178 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker
,
179 base::Unretained(this),
185 void PermissionDispatcher::RunCallbackOnWorkerThread(
186 blink::WebPermissionQueryCallback
* callback
,
187 scoped_ptr
<blink::WebPermissionStatus
> status
) {
188 callback
->onSuccess(status
.release());
192 PermissionServicePtr
& PermissionDispatcher::GetPermissionServicePtr() {
193 if (!permission_service_
.get())
194 service_registry_
->ConnectToRemoteService(&permission_service_
);
195 return permission_service_
;
198 void PermissionDispatcher::QueryPermissionInternal(
199 blink::WebPermissionType type
,
200 const std::string
& origin
,
201 blink::WebPermissionQueryCallback
* callback
,
202 int worker_thread_id
) {
203 // We need to save the |callback| in an IDMap so if |this| gets deleted, the
204 // callback will not leak. In the case of |this| gets deleted, the
205 // |permission_service_| pipe will be destroyed too so OnQueryPermission will
207 int request_id
= pending_callbacks_
.Add(
208 new CallbackInformation(callback
, worker_thread_id
));
209 GetPermissionServicePtr()->HasPermission(
210 GetPermissionName(type
),
212 base::Bind(&PermissionDispatcher::OnQueryPermission
,
213 base::Unretained(this),
217 void PermissionDispatcher::OnQueryPermission(int request_id
,
218 PermissionStatus result
) {
219 CallbackInformation
* callback_information
=
220 pending_callbacks_
.Lookup(request_id
);
221 DCHECK(callback_information
&& callback_information
->callback());
222 scoped_ptr
<blink::WebPermissionStatus
> status(
223 new blink::WebPermissionStatus(GetWebPermissionStatus(result
)));
225 if (callback_information
->worker_thread_id() != kNoWorkerThread
) {
226 blink::WebPermissionQueryCallback
* callback
=
227 callback_information
->ReleaseCallback();
228 int worker_thread_id
= callback_information
->worker_thread_id();
229 pending_callbacks_
.Remove(request_id
);
231 // If the worker is no longer running, ::PostTask() will return false and
232 // gracefully fail, destroying the callback too.
233 WorkerTaskRunner::Instance()->PostTask(
235 base::Bind(&PermissionDispatcher::RunCallbackOnWorkerThread
,
236 base::Unretained(callback
),
237 base::Passed(&status
)));
241 callback_information
->callback()->onSuccess(status
.release());
242 pending_callbacks_
.Remove(request_id
);
245 void PermissionDispatcher::OnPermissionChanged(
246 blink::WebPermissionType type
,
247 const std::string
& origin
,
248 WebPermissionObserver
* observer
,
249 PermissionStatus status
) {
250 if (!IsObserverRegistered(observer
))
253 observer
->permissionChanged(type
, GetWebPermissionStatus(status
));
255 GetNextPermissionChange(type
, origin
, observer
, status
);
258 void PermissionDispatcher::OnPermissionChangedForWorker(
259 int worker_thread_id
,
260 const base::Callback
<void(blink::WebPermissionStatus
)>& callback
,
261 PermissionStatus status
) {
262 DCHECK(worker_thread_id
!= kNoWorkerThread
);
264 WorkerTaskRunner::Instance()->PostTask(
265 worker_thread_id
, base::Bind(callback
, GetWebPermissionStatus(status
)));
268 void PermissionDispatcher::GetNextPermissionChange(
269 blink::WebPermissionType type
,
270 const std::string
& origin
,
271 WebPermissionObserver
* observer
,
272 PermissionStatus current_status
) {
273 GetPermissionServicePtr()->GetNextPermissionChange(
274 GetPermissionName(type
),
277 base::Bind(&PermissionDispatcher::OnPermissionChanged
,
278 base::Unretained(this),
281 base::Unretained(observer
)));
284 } // namespace content