Make USB permissions work in the new permission message system
[chromium-blink-merge.git] / content / child / permissions / permission_dispatcher.cc
blob552d2fda35ebb1e3acd9d1e32ece4455e1fcdb8a
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;
16 namespace content {
18 namespace {
20 PermissionName GetPermissionName(blink::WebPermissionType type) {
21 switch (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;
34 default:
35 // The default statement is only there to prevent compilation failures if
36 // WebPermissionType enum gets extended.
37 NOTREACHED();
38 return PERMISSION_NAME_GEOLOCATION;
42 PermissionStatus GetPermissionStatus(blink::WebPermissionStatus status) {
43 switch (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;
52 NOTREACHED();
53 return PERMISSION_STATUS_DENIED;
56 blink::WebPermissionStatus GetWebPermissionStatus(PermissionStatus status) {
57 switch (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;
66 NOTREACHED();
67 return blink::WebPermissionStatusDenied;
70 const int kNoWorkerThread = 0;
72 } // anonymous namespace
74 // static
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,
84 int worker_thread_id)
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))
142 return;
144 RegisterObserver(observer);
146 GetNextPermissionChange(type,
147 origin.string().utf8(),
148 observer,
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),
193 origin,
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),
201 worker_thread_id,
202 callback));
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),
213 origin,
214 GetPermissionStatus(status),
215 base::Bind(&PermissionDispatcher::OnPermissionChangedForWorker,
216 base::Unretained(this),
217 worker_thread_id,
218 callback));
221 // static
222 void PermissionDispatcher::RunCallbackOnWorkerThread(
223 blink::WebPermissionCallback* callback,
224 scoped_ptr<blink::WebPermissionStatus> status) {
225 callback->onSuccess(status.release());
226 delete callback;
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
245 // not be called.
246 int request_id = pending_callbacks_.Add(
247 new CallbackInformation(callback, worker_thread_id));
248 GetPermissionServicePtr()->HasPermission(
249 GetPermissionName(type),
250 origin,
251 base::Bind(&PermissionDispatcher::OnPermissionResponse,
252 base::Unretained(this),
253 request_id));
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
264 // not be called.
265 int request_id = pending_callbacks_.Add(
266 new CallbackInformation(callback, worker_thread_id));
267 GetPermissionServicePtr()->RequestPermission(
268 GetPermissionName(type),
269 origin,
270 blink::WebUserGestureIndicator::isProcessingUserGesture(),
271 base::Bind(&PermissionDispatcher::OnPermissionResponse,
272 base::Unretained(this),
273 request_id));
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
284 // not be called.
285 int request_id = pending_callbacks_.Add(
286 new CallbackInformation(callback, worker_thread_id));
287 GetPermissionServicePtr()->RevokePermission(
288 GetPermissionName(type),
289 origin,
290 base::Bind(&PermissionDispatcher::OnPermissionResponse,
291 base::Unretained(this),
292 request_id));
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(
312 worker_thread_id,
313 base::Bind(&PermissionDispatcher::RunCallbackOnWorkerThread,
314 base::Unretained(callback),
315 base::Passed(&status)));
316 return;
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))
329 return;
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),
353 origin,
354 current_status,
355 base::Bind(&PermissionDispatcher::OnPermissionChanged,
356 base::Unretained(this),
357 type,
358 origin,
359 base::Unretained(observer)));
362 } // namespace content