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::OnRequestPermissionResponse(
136 PermissionStatus status
) {
137 PendingRequest
* request
= pending_requests_
.Lookup(request_id
);
138 PermissionStatusCallback
callback(request
->callback
);
139 request
->callback
.reset();
140 pending_requests_
.Remove(request_id
);
141 callback
.Run(status
);
144 void PermissionServiceImpl::CancelPendingOperations() {
145 DCHECK(context_
->render_frame_host());
146 DCHECK(context_
->GetBrowserContext());
148 PermissionManager
* permission_manager
=
149 context_
->GetBrowserContext()->GetPermissionManager();
150 if (!permission_manager
)
153 // Cancel pending requests.
154 for (RequestsMap::Iterator
<PendingRequest
> it(&pending_requests_
);
155 !it
.IsAtEnd(); it
.Advance()) {
156 permission_manager
->CancelPermissionRequest(
157 it
.GetCurrentValue()->permission
,
158 context_
->render_frame_host(),
160 it
.GetCurrentValue()->origin
);
162 pending_requests_
.Clear();
164 // Cancel pending subscriptions.
165 for (SubscriptionsMap::Iterator
<PendingSubscription
>
166 it(&pending_subscriptions_
); !it
.IsAtEnd(); it
.Advance()) {
167 it
.GetCurrentValue()->callback
.Run(GetPermissionStatusFromType(
168 it
.GetCurrentValue()->permission
, it
.GetCurrentValue()->origin
));
169 it
.GetCurrentValue()->callback
.reset();
170 permission_manager
->UnsubscribePermissionStatusChange(
171 it
.GetCurrentValue()->id
);
173 pending_subscriptions_
.Clear();
176 void PermissionServiceImpl::HasPermission(
177 PermissionName permission
,
178 const mojo::String
& origin
,
179 const PermissionStatusCallback
& callback
) {
180 callback
.Run(GetPermissionStatusFromName(permission
, GURL(origin
)));
183 void PermissionServiceImpl::RevokePermission(
184 PermissionName permission
,
185 const mojo::String
& origin
,
186 const PermissionStatusCallback
& callback
) {
187 GURL
origin_url(origin
);
188 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
189 PermissionStatus status
= GetPermissionStatusFromType(permission_type
,
192 // Resetting the permission should only be possible if the permission is
194 if (status
!= PERMISSION_STATUS_GRANTED
) {
195 callback
.Run(status
);
199 ResetPermissionStatus(permission_type
, origin_url
);
201 callback
.Run(GetPermissionStatusFromType(permission_type
, origin_url
));
204 void PermissionServiceImpl::GetNextPermissionChange(
205 PermissionName permission
,
206 const mojo::String
& mojo_origin
,
207 PermissionStatus last_known_status
,
208 const PermissionStatusCallback
& callback
) {
209 GURL
origin(mojo_origin
);
210 PermissionStatus current_status
=
211 GetPermissionStatusFromName(permission
, origin
);
212 if (current_status
!= last_known_status
) {
213 callback
.Run(current_status
);
217 BrowserContext
* browser_context
= context_
->GetBrowserContext();
218 DCHECK(browser_context
);
219 if (!browser_context
->GetPermissionManager()) {
220 callback
.Run(current_status
);
224 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
226 // We need to pass the id of PendingSubscription in pending_subscriptions_
227 // to the callback but SubscribePermissionStatusChange() will also return an
228 // id which is different.
229 PendingSubscription
* subscription
=
230 new PendingSubscription(permission_type
, origin
, callback
);
231 int pending_subscription_id
= pending_subscriptions_
.Add(subscription
);
233 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
235 browser_context
->GetPermissionManager()->SubscribePermissionStatusChange(
238 // If the embedding_origin is empty, we,ll use the |origin| instead.
239 embedding_origin
.is_empty() ? origin
: embedding_origin
,
240 base::Bind(&PermissionServiceImpl::OnPermissionStatusChanged
,
241 weak_factory_
.GetWeakPtr(),
242 pending_subscription_id
));
245 PermissionStatus
PermissionServiceImpl::GetPermissionStatusFromName(
246 PermissionName permission
, const GURL
& origin
) {
247 return GetPermissionStatusFromType(PermissionNameToPermissionType(permission
),
251 PermissionStatus
PermissionServiceImpl::GetPermissionStatusFromType(
252 PermissionType type
, const GURL
& origin
) {
253 BrowserContext
* browser_context
= context_
->GetBrowserContext();
254 DCHECK(browser_context
);
255 if (!browser_context
->GetPermissionManager())
256 return PERMISSION_STATUS_DENIED
;
258 // If the embedding_origin is empty we'll use |origin| instead.
259 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
260 return browser_context
->GetPermissionManager()->GetPermissionStatus(
261 type
, origin
, embedding_origin
.is_empty() ? origin
: embedding_origin
);
264 void PermissionServiceImpl::ResetPermissionStatus(PermissionType type
,
265 const GURL
& origin
) {
266 BrowserContext
* browser_context
= context_
->GetBrowserContext();
267 DCHECK(browser_context
);
268 if (!browser_context
->GetPermissionManager())
271 // If the embedding_origin is empty we'll use |origin| instead.
272 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
273 browser_context
->GetPermissionManager()->ResetPermission(
274 type
, origin
, embedding_origin
.is_empty() ? origin
: embedding_origin
);
277 void PermissionServiceImpl::OnPermissionStatusChanged(
278 int pending_subscription_id
,
279 PermissionStatus status
) {
280 PendingSubscription
* subscription
=
281 pending_subscriptions_
.Lookup(pending_subscription_id
);
283 BrowserContext
* browser_context
= context_
->GetBrowserContext();
284 DCHECK(browser_context
);
285 if (browser_context
->GetPermissionManager()) {
286 browser_context
->GetPermissionManager()->UnsubscribePermissionStatusChange(
290 PermissionStatusCallback callback
= subscription
->callback
;
292 subscription
->callback
.reset();
293 pending_subscriptions_
.Remove(pending_subscription_id
);
295 callback
.Run(status
);
298 } // namespace content