1 // Copyright 2014 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/browser/permissions/permission_service_impl.h"
8 #include "content/public/browser/browser_context.h"
9 #include "content/public/browser/permission_manager.h"
10 #include "content/public/browser/permission_type.h"
16 PermissionType
PermissionNameToPermissionType(PermissionName name
) {
18 case PERMISSION_NAME_GEOLOCATION
:
19 return PermissionType::GEOLOCATION
;
20 case PERMISSION_NAME_NOTIFICATIONS
:
21 return PermissionType::NOTIFICATIONS
;
22 case PERMISSION_NAME_PUSH_NOTIFICATIONS
:
23 return PermissionType::PUSH_MESSAGING
;
24 case PERMISSION_NAME_MIDI
:
25 return PermissionType::MIDI
;
26 case PERMISSION_NAME_MIDI_SYSEX
:
27 return PermissionType::MIDI_SYSEX
;
28 case PERMISSION_NAME_PROTECTED_MEDIA_IDENTIFIER
:
29 return PermissionType::PROTECTED_MEDIA_IDENTIFIER
;
30 case PERMISSION_NAME_DURABLE_STORAGE
:
31 return PermissionType::DURABLE_STORAGE
;
32 case PERMISSION_NAME_AUDIO_CAPTURE
:
33 return PermissionType::AUDIO_CAPTURE
;
34 case PERMISSION_NAME_VIDEO_CAPTURE
:
35 return PermissionType::VIDEO_CAPTURE
;
39 return PermissionType::NUM
;
42 } // anonymous namespace
44 PermissionServiceImpl::PendingRequest::PendingRequest(
45 PermissionType permission
,
47 const PermissionStatusCallback
& callback
)
48 : permission(permission
),
53 PermissionServiceImpl::PendingRequest::~PendingRequest() {
54 if (!callback
.is_null())
55 callback
.Run(PERMISSION_STATUS_ASK
);
58 PermissionServiceImpl::PendingSubscription::PendingSubscription(
59 PermissionType permission
,
61 const PermissionStatusCallback
& callback
)
63 permission(permission
),
68 PermissionServiceImpl::PendingSubscription::~PendingSubscription() {
69 if (!callback
.is_null())
70 callback
.Run(PERMISSION_STATUS_ASK
);
73 PermissionServiceImpl::PermissionServiceImpl(
74 PermissionServiceContext
* context
,
75 mojo::InterfaceRequest
<PermissionService
> request
)
77 binding_(this, request
.Pass()),
79 binding_
.set_connection_error_handler(
80 base::Bind(&PermissionServiceImpl::OnConnectionError
,
81 base::Unretained(this)));
84 PermissionServiceImpl::~PermissionServiceImpl() {
85 DCHECK(pending_requests_
.IsEmpty());
88 void PermissionServiceImpl::OnConnectionError() {
89 context_
->ServiceHadConnectionError(this);
90 // After that call, |this| will be deleted.
93 void PermissionServiceImpl::RequestPermission(
94 PermissionName permission
,
95 const mojo::String
& origin
,
97 const PermissionStatusCallback
& callback
) {
98 // This condition is valid if the call is coming from a ChildThread instead of
99 // a RenderFrame. Some consumers of the service run in Workers and some in
100 // Frames. In the context of a Worker, it is not possible to show a
101 // permission prompt because there is no tab. In the context of a Frame, we
102 // can. Even if the call comes from a context where it is not possible to show
103 // any UI, we want to still return something relevant so the current
104 // permission status is returned.
105 if (!context_
->render_frame_host()) {
106 // There is no way to show a UI so the call will simply return the current
108 HasPermission(permission
, origin
, callback
);
112 BrowserContext
* browser_context
= context_
->GetBrowserContext();
113 DCHECK(browser_context
);
114 if (!browser_context
->GetPermissionManager()) {
115 callback
.Run(content::PERMISSION_STATUS_DENIED
);
119 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
120 int request_id
= pending_requests_
.Add(
121 new PendingRequest(permission_type
, GURL(origin
), callback
));
123 browser_context
->GetPermissionManager()->RequestPermission(
125 context_
->render_frame_host(),
128 user_gesture
, // TODO(mlamouri): should be removed (crbug.com/423770)
129 base::Bind(&PermissionServiceImpl::OnRequestPermissionResponse
,
130 weak_factory_
.GetWeakPtr(),
134 void PermissionServiceImpl::RequestPermissions(
135 mojo::Array
<PermissionName
> permissions
,
136 const mojo::String
& origin
,
138 const PermissionsStatusCallback
& callback
) {
141 // TODO(lalitm,mlamouri): this is returning the current permission statuses
142 // in order for the call to successfully return. It will be changed later.
143 // See https://crbug.com/516626
144 mojo::Array
<PermissionStatus
> result(permissions
.size());
145 for (size_t i
= 0; i
< permissions
.size(); ++i
)
146 result
[i
] = GetPermissionStatusFromName(permissions
[i
], GURL(origin
));
147 callback
.Run(result
.Pass());
150 void PermissionServiceImpl::OnRequestPermissionResponse(
152 PermissionStatus status
) {
153 PendingRequest
* request
= pending_requests_
.Lookup(request_id
);
154 PermissionStatusCallback
callback(request
->callback
);
155 request
->callback
.reset();
156 pending_requests_
.Remove(request_id
);
157 callback
.Run(status
);
160 void PermissionServiceImpl::CancelPendingOperations() {
161 DCHECK(context_
->render_frame_host());
162 DCHECK(context_
->GetBrowserContext());
164 PermissionManager
* permission_manager
=
165 context_
->GetBrowserContext()->GetPermissionManager();
166 if (!permission_manager
)
169 // Cancel pending requests.
170 for (RequestsMap::Iterator
<PendingRequest
> it(&pending_requests_
);
171 !it
.IsAtEnd(); it
.Advance()) {
172 permission_manager
->CancelPermissionRequest(
173 it
.GetCurrentValue()->permission
,
174 context_
->render_frame_host(),
176 it
.GetCurrentValue()->origin
);
178 pending_requests_
.Clear();
180 // Cancel pending subscriptions.
181 for (SubscriptionsMap::Iterator
<PendingSubscription
>
182 it(&pending_subscriptions_
); !it
.IsAtEnd(); it
.Advance()) {
183 it
.GetCurrentValue()->callback
.Run(GetPermissionStatusFromType(
184 it
.GetCurrentValue()->permission
, it
.GetCurrentValue()->origin
));
185 it
.GetCurrentValue()->callback
.reset();
186 permission_manager
->UnsubscribePermissionStatusChange(
187 it
.GetCurrentValue()->id
);
189 pending_subscriptions_
.Clear();
192 void PermissionServiceImpl::HasPermission(
193 PermissionName permission
,
194 const mojo::String
& origin
,
195 const PermissionStatusCallback
& callback
) {
196 callback
.Run(GetPermissionStatusFromName(permission
, GURL(origin
)));
199 void PermissionServiceImpl::RevokePermission(
200 PermissionName permission
,
201 const mojo::String
& origin
,
202 const PermissionStatusCallback
& callback
) {
203 GURL
origin_url(origin
);
204 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
205 PermissionStatus status
= GetPermissionStatusFromType(permission_type
,
208 // Resetting the permission should only be possible if the permission is
210 if (status
!= PERMISSION_STATUS_GRANTED
) {
211 callback
.Run(status
);
215 ResetPermissionStatus(permission_type
, origin_url
);
217 callback
.Run(GetPermissionStatusFromType(permission_type
, origin_url
));
220 void PermissionServiceImpl::GetNextPermissionChange(
221 PermissionName permission
,
222 const mojo::String
& mojo_origin
,
223 PermissionStatus last_known_status
,
224 const PermissionStatusCallback
& callback
) {
225 GURL
origin(mojo_origin
);
226 PermissionStatus current_status
=
227 GetPermissionStatusFromName(permission
, origin
);
228 if (current_status
!= last_known_status
) {
229 callback
.Run(current_status
);
233 BrowserContext
* browser_context
= context_
->GetBrowserContext();
234 DCHECK(browser_context
);
235 if (!browser_context
->GetPermissionManager()) {
236 callback
.Run(current_status
);
240 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
242 // We need to pass the id of PendingSubscription in pending_subscriptions_
243 // to the callback but SubscribePermissionStatusChange() will also return an
244 // id which is different.
245 PendingSubscription
* subscription
=
246 new PendingSubscription(permission_type
, origin
, callback
);
247 int pending_subscription_id
= pending_subscriptions_
.Add(subscription
);
249 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
251 browser_context
->GetPermissionManager()->SubscribePermissionStatusChange(
254 // If the embedding_origin is empty, we,ll use the |origin| instead.
255 embedding_origin
.is_empty() ? origin
: embedding_origin
,
256 base::Bind(&PermissionServiceImpl::OnPermissionStatusChanged
,
257 weak_factory_
.GetWeakPtr(),
258 pending_subscription_id
));
261 PermissionStatus
PermissionServiceImpl::GetPermissionStatusFromName(
262 PermissionName permission
, const GURL
& origin
) {
263 return GetPermissionStatusFromType(PermissionNameToPermissionType(permission
),
267 PermissionStatus
PermissionServiceImpl::GetPermissionStatusFromType(
268 PermissionType type
, const GURL
& origin
) {
269 BrowserContext
* browser_context
= context_
->GetBrowserContext();
270 DCHECK(browser_context
);
271 if (!browser_context
->GetPermissionManager())
272 return PERMISSION_STATUS_DENIED
;
274 // If the embedding_origin is empty we'll use |origin| instead.
275 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
276 return browser_context
->GetPermissionManager()->GetPermissionStatus(
277 type
, origin
, embedding_origin
.is_empty() ? origin
: embedding_origin
);
280 void PermissionServiceImpl::ResetPermissionStatus(PermissionType type
,
281 const GURL
& origin
) {
282 BrowserContext
* browser_context
= context_
->GetBrowserContext();
283 DCHECK(browser_context
);
284 if (!browser_context
->GetPermissionManager())
287 // If the embedding_origin is empty we'll use |origin| instead.
288 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
289 browser_context
->GetPermissionManager()->ResetPermission(
290 type
, origin
, embedding_origin
.is_empty() ? origin
: embedding_origin
);
293 void PermissionServiceImpl::OnPermissionStatusChanged(
294 int pending_subscription_id
,
295 PermissionStatus status
) {
296 PendingSubscription
* subscription
=
297 pending_subscriptions_
.Lookup(pending_subscription_id
);
299 BrowserContext
* browser_context
= context_
->GetBrowserContext();
300 DCHECK(browser_context
);
301 if (browser_context
->GetPermissionManager()) {
302 browser_context
->GetPermissionManager()->UnsubscribePermissionStatusChange(
306 PermissionStatusCallback callback
= subscription
->callback
;
308 subscription
->callback
.reset();
309 pending_subscriptions_
.Remove(pending_subscription_id
);
311 callback
.Run(status
);
314 } // namespace content