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
;
35 return PermissionType::NUM
;
38 } // anonymous namespace
40 PermissionServiceImpl::PendingRequest::PendingRequest(
41 PermissionType permission
,
43 const PermissionStatusCallback
& callback
)
44 : permission(permission
),
49 PermissionServiceImpl::PendingRequest::~PendingRequest() {
50 if (!callback
.is_null())
51 callback
.Run(PERMISSION_STATUS_ASK
);
54 PermissionServiceImpl::PendingSubscription::PendingSubscription(
55 PermissionType permission
,
57 const PermissionStatusCallback
& callback
)
59 permission(permission
),
64 PermissionServiceImpl::PendingSubscription::~PendingSubscription() {
65 if (!callback
.is_null())
66 callback
.Run(PERMISSION_STATUS_ASK
);
69 PermissionServiceImpl::PermissionServiceImpl(
70 PermissionServiceContext
* context
,
71 mojo::InterfaceRequest
<PermissionService
> request
)
73 binding_(this, request
.Pass()),
75 binding_
.set_connection_error_handler(
76 base::Bind(&PermissionServiceImpl::OnConnectionError
,
77 base::Unretained(this)));
80 PermissionServiceImpl::~PermissionServiceImpl() {
81 DCHECK(pending_requests_
.IsEmpty());
84 void PermissionServiceImpl::OnConnectionError() {
85 context_
->ServiceHadConnectionError(this);
86 // After that call, |this| will be deleted.
89 void PermissionServiceImpl::RequestPermission(
90 PermissionName permission
,
91 const mojo::String
& origin
,
93 const PermissionStatusCallback
& callback
) {
94 // This condition is valid if the call is coming from a ChildThread instead of
95 // a RenderFrame. Some consumers of the service run in Workers and some in
96 // Frames. In the context of a Worker, it is not possible to show a
97 // permission prompt because there is no tab. In the context of a Frame, we
98 // can. Even if the call comes from a context where it is not possible to show
99 // any UI, we want to still return something relevant so the current
100 // permission status is returned.
101 if (!context_
->render_frame_host()) {
102 // There is no way to show a UI so the call will simply return the current
104 HasPermission(permission
, origin
, callback
);
108 BrowserContext
* browser_context
= context_
->GetBrowserContext();
109 DCHECK(browser_context
);
110 if (!browser_context
->GetPermissionManager()) {
111 callback
.Run(content::PERMISSION_STATUS_DENIED
);
115 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
116 int request_id
= pending_requests_
.Add(
117 new PendingRequest(permission_type
, GURL(origin
), callback
));
119 browser_context
->GetPermissionManager()->RequestPermission(
121 context_
->render_frame_host(),
124 user_gesture
, // TODO(mlamouri): should be removed (crbug.com/423770)
125 base::Bind(&PermissionServiceImpl::OnRequestPermissionResponse
,
126 weak_factory_
.GetWeakPtr(),
130 void PermissionServiceImpl::OnRequestPermissionResponse(
132 PermissionStatus status
) {
133 PendingRequest
* request
= pending_requests_
.Lookup(request_id
);
134 PermissionStatusCallback
callback(request
->callback
);
135 request
->callback
.reset();
136 pending_requests_
.Remove(request_id
);
137 callback
.Run(status
);
140 void PermissionServiceImpl::CancelPendingOperations() {
141 DCHECK(context_
->render_frame_host());
142 DCHECK(context_
->GetBrowserContext());
144 PermissionManager
* permission_manager
=
145 context_
->GetBrowserContext()->GetPermissionManager();
146 if (!permission_manager
)
149 // Cancel pending requests.
150 for (RequestsMap::Iterator
<PendingRequest
> it(&pending_requests_
);
151 !it
.IsAtEnd(); it
.Advance()) {
152 permission_manager
->CancelPermissionRequest(
153 it
.GetCurrentValue()->permission
,
154 context_
->render_frame_host(),
156 it
.GetCurrentValue()->origin
);
158 pending_requests_
.Clear();
160 // Cancel pending subscriptions.
161 for (SubscriptionsMap::Iterator
<PendingSubscription
>
162 it(&pending_subscriptions_
); !it
.IsAtEnd(); it
.Advance()) {
163 it
.GetCurrentValue()->callback
.Run(GetPermissionStatusFromType(
164 it
.GetCurrentValue()->permission
, it
.GetCurrentValue()->origin
));
165 it
.GetCurrentValue()->callback
.reset();
166 permission_manager
->UnsubscribePermissionStatusChange(
167 it
.GetCurrentValue()->id
);
169 pending_subscriptions_
.Clear();
172 void PermissionServiceImpl::HasPermission(
173 PermissionName permission
,
174 const mojo::String
& origin
,
175 const PermissionStatusCallback
& callback
) {
176 callback
.Run(GetPermissionStatusFromName(permission
, GURL(origin
)));
179 void PermissionServiceImpl::RevokePermission(
180 PermissionName permission
,
181 const mojo::String
& origin
,
182 const PermissionStatusCallback
& callback
) {
183 GURL
origin_url(origin
);
184 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
185 PermissionStatus status
= GetPermissionStatusFromType(permission_type
,
188 // Resetting the permission should only be possible if the permission is
190 if (status
!= PERMISSION_STATUS_GRANTED
) {
191 callback
.Run(status
);
195 ResetPermissionStatus(permission_type
, origin_url
);
197 callback
.Run(GetPermissionStatusFromType(permission_type
, origin_url
));
200 void PermissionServiceImpl::GetNextPermissionChange(
201 PermissionName permission
,
202 const mojo::String
& mojo_origin
,
203 PermissionStatus last_known_status
,
204 const PermissionStatusCallback
& callback
) {
205 GURL
origin(mojo_origin
);
206 PermissionStatus current_status
=
207 GetPermissionStatusFromName(permission
, origin
);
208 if (current_status
!= last_known_status
) {
209 callback
.Run(current_status
);
213 BrowserContext
* browser_context
= context_
->GetBrowserContext();
214 DCHECK(browser_context
);
215 if (!browser_context
->GetPermissionManager()) {
216 callback
.Run(current_status
);
220 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
222 // We need to pass the id of PendingSubscription in pending_subscriptions_
223 // to the callback but SubscribePermissionStatusChange() will also return an
224 // id which is different.
225 PendingSubscription
* subscription
=
226 new PendingSubscription(permission_type
, origin
, callback
);
227 int pending_subscription_id
= pending_subscriptions_
.Add(subscription
);
229 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
231 browser_context
->GetPermissionManager()->SubscribePermissionStatusChange(
234 // If the embedding_origin is empty, we,ll use the |origin| instead.
235 embedding_origin
.is_empty() ? origin
: embedding_origin
,
236 base::Bind(&PermissionServiceImpl::OnPermissionStatusChanged
,
237 weak_factory_
.GetWeakPtr(),
238 pending_subscription_id
));
241 PermissionStatus
PermissionServiceImpl::GetPermissionStatusFromName(
242 PermissionName permission
, const GURL
& origin
) {
243 return GetPermissionStatusFromType(PermissionNameToPermissionType(permission
),
247 PermissionStatus
PermissionServiceImpl::GetPermissionStatusFromType(
248 PermissionType type
, const GURL
& origin
) {
249 BrowserContext
* browser_context
= context_
->GetBrowserContext();
250 DCHECK(browser_context
);
251 if (!browser_context
->GetPermissionManager())
252 return PERMISSION_STATUS_DENIED
;
254 // If the embedding_origin is empty we'll use |origin| instead.
255 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
256 return browser_context
->GetPermissionManager()->GetPermissionStatus(
257 type
, origin
, embedding_origin
.is_empty() ? origin
: embedding_origin
);
260 void PermissionServiceImpl::ResetPermissionStatus(PermissionType type
,
261 const GURL
& origin
) {
262 BrowserContext
* browser_context
= context_
->GetBrowserContext();
263 DCHECK(browser_context
);
264 if (!browser_context
->GetPermissionManager())
267 // If the embedding_origin is empty we'll use |origin| instead.
268 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
269 browser_context
->GetPermissionManager()->ResetPermission(
270 type
, origin
, embedding_origin
.is_empty() ? origin
: embedding_origin
);
273 void PermissionServiceImpl::OnPermissionStatusChanged(
274 int pending_subscription_id
,
275 PermissionStatus status
) {
276 PendingSubscription
* subscription
=
277 pending_subscriptions_
.Lookup(pending_subscription_id
);
279 BrowserContext
* browser_context
= context_
->GetBrowserContext();
280 DCHECK(browser_context
);
281 if (browser_context
->GetPermissionManager()) {
282 browser_context
->GetPermissionManager()->UnsubscribePermissionStatusChange(
286 PermissionStatusCallback callback
= subscription
->callback
;
288 subscription
->callback
.reset();
289 pending_subscriptions_
.Remove(pending_subscription_id
);
291 callback
.Run(status
);
294 } // namespace content