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_SYSEX
:
25 return PermissionType::MIDI_SYSEX
;
26 case PERMISSION_NAME_PROTECTED_MEDIA_IDENTIFIER
:
27 return PermissionType::PROTECTED_MEDIA_IDENTIFIER
;
31 return PermissionType::NUM
;
34 } // anonymous namespace
36 PermissionServiceImpl::PendingRequest::PendingRequest(
37 PermissionType permission
,
39 const PermissionStatusCallback
& callback
)
40 : permission(permission
),
45 PermissionServiceImpl::PendingRequest::~PendingRequest() {
46 if (!callback
.is_null())
47 callback
.Run(PERMISSION_STATUS_ASK
);
50 PermissionServiceImpl::PendingSubscription::PendingSubscription(
51 PermissionType permission
,
53 const PermissionStatusCallback
& callback
)
55 permission(permission
),
60 PermissionServiceImpl::PendingSubscription::~PendingSubscription() {
61 if (!callback
.is_null())
62 callback
.Run(PERMISSION_STATUS_ASK
);
65 PermissionServiceImpl::PermissionServiceImpl(
66 PermissionServiceContext
* context
,
67 mojo::InterfaceRequest
<PermissionService
> request
)
69 binding_(this, request
.Pass()),
71 binding_
.set_connection_error_handler(
72 base::Bind(&PermissionServiceImpl::OnConnectionError
,
73 base::Unretained(this)));
76 PermissionServiceImpl::~PermissionServiceImpl() {
77 DCHECK(pending_requests_
.IsEmpty());
80 void PermissionServiceImpl::OnConnectionError() {
81 context_
->ServiceHadConnectionError(this);
82 // After that call, |this| will be deleted.
85 void PermissionServiceImpl::RequestPermission(
86 PermissionName permission
,
87 const mojo::String
& origin
,
89 const PermissionStatusCallback
& callback
) {
90 // This condition is valid if the call is coming from a ChildThread instead of
91 // a RenderFrame. Some consumers of the service run in Workers and some in
92 // Frames. In the context of a Worker, it is not possible to show a
93 // permission prompt because there is no tab. In the context of a Frame, we
94 // can. Even if the call comes from a context where it is not possible to show
95 // any UI, we want to still return something relevant so the current
96 // permission status is returned.
97 if (!context_
->render_frame_host()) {
98 // There is no way to show a UI so the call will simply return the current
100 HasPermission(permission
, origin
, callback
);
104 BrowserContext
* browser_context
= context_
->GetBrowserContext();
105 DCHECK(browser_context
);
106 if (!browser_context
->GetPermissionManager()) {
107 callback
.Run(content::PERMISSION_STATUS_DENIED
);
111 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
112 int request_id
= pending_requests_
.Add(
113 new PendingRequest(permission_type
, GURL(origin
), callback
));
115 browser_context
->GetPermissionManager()->RequestPermission(
117 context_
->render_frame_host(),
120 user_gesture
, // TODO(mlamouri): should be removed (crbug.com/423770)
121 base::Bind(&PermissionServiceImpl::OnRequestPermissionResponse
,
122 weak_factory_
.GetWeakPtr(),
126 void PermissionServiceImpl::OnRequestPermissionResponse(
128 PermissionStatus status
) {
129 PendingRequest
* request
= pending_requests_
.Lookup(request_id
);
130 PermissionStatusCallback
callback(request
->callback
);
131 request
->callback
.reset();
132 pending_requests_
.Remove(request_id
);
133 callback
.Run(status
);
136 void PermissionServiceImpl::CancelPendingOperations() {
137 DCHECK(context_
->render_frame_host());
138 DCHECK(context_
->GetBrowserContext());
140 PermissionManager
* permission_manager
=
141 context_
->GetBrowserContext()->GetPermissionManager();
142 if (!permission_manager
)
145 // Cancel pending requests.
146 for (RequestsMap::Iterator
<PendingRequest
> it(&pending_requests_
);
147 !it
.IsAtEnd(); it
.Advance()) {
148 permission_manager
->CancelPermissionRequest(
149 it
.GetCurrentValue()->permission
,
150 context_
->render_frame_host(),
152 it
.GetCurrentValue()->origin
);
154 pending_requests_
.Clear();
156 // Cancel pending subscriptions.
157 for (SubscriptionsMap::Iterator
<PendingSubscription
>
158 it(&pending_subscriptions_
); !it
.IsAtEnd(); it
.Advance()) {
159 it
.GetCurrentValue()->callback
.Run(GetPermissionStatusFromType(
160 it
.GetCurrentValue()->permission
, it
.GetCurrentValue()->origin
));
161 it
.GetCurrentValue()->callback
.reset();
162 permission_manager
->UnsubscribePermissionStatusChange(
163 it
.GetCurrentValue()->id
);
165 pending_subscriptions_
.Clear();
168 void PermissionServiceImpl::HasPermission(
169 PermissionName permission
,
170 const mojo::String
& origin
,
171 const PermissionStatusCallback
& callback
) {
172 callback
.Run(GetPermissionStatusFromName(permission
, GURL(origin
)));
175 void PermissionServiceImpl::RevokePermission(
176 PermissionName permission
,
177 const mojo::String
& origin
,
178 const PermissionStatusCallback
& callback
) {
179 GURL
origin_url(origin
);
180 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
181 PermissionStatus status
= GetPermissionStatusFromType(permission_type
,
184 // Resetting the permission should only be possible if the permission is
186 if (status
!= PERMISSION_STATUS_GRANTED
) {
187 callback
.Run(status
);
191 ResetPermissionStatus(permission_type
, origin_url
);
193 callback
.Run(GetPermissionStatusFromType(permission_type
, origin_url
));
196 void PermissionServiceImpl::GetNextPermissionChange(
197 PermissionName permission
,
198 const mojo::String
& mojo_origin
,
199 PermissionStatus last_known_status
,
200 const PermissionStatusCallback
& callback
) {
201 GURL
origin(mojo_origin
);
202 PermissionStatus current_status
=
203 GetPermissionStatusFromName(permission
, origin
);
204 if (current_status
!= last_known_status
) {
205 callback
.Run(current_status
);
209 BrowserContext
* browser_context
= context_
->GetBrowserContext();
210 DCHECK(browser_context
);
211 if (!browser_context
->GetPermissionManager()) {
212 callback
.Run(current_status
);
216 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
218 // We need to pass the id of PendingSubscription in pending_subscriptions_
219 // to the callback but SubscribePermissionStatusChange() will also return an
220 // id which is different.
221 PendingSubscription
* subscription
=
222 new PendingSubscription(permission_type
, origin
, callback
);
223 int pending_subscription_id
= pending_subscriptions_
.Add(subscription
);
225 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
227 browser_context
->GetPermissionManager()->SubscribePermissionStatusChange(
230 // If the embedding_origin is empty, we,ll use the |origin| instead.
231 embedding_origin
.is_empty() ? origin
: embedding_origin
,
232 base::Bind(&PermissionServiceImpl::OnPermissionStatusChanged
,
233 weak_factory_
.GetWeakPtr(),
234 pending_subscription_id
));
237 PermissionStatus
PermissionServiceImpl::GetPermissionStatusFromName(
238 PermissionName permission
, const GURL
& origin
) {
239 return GetPermissionStatusFromType(PermissionNameToPermissionType(permission
),
243 PermissionStatus
PermissionServiceImpl::GetPermissionStatusFromType(
244 PermissionType type
, const GURL
& origin
) {
245 BrowserContext
* browser_context
= context_
->GetBrowserContext();
246 DCHECK(browser_context
);
247 if (!browser_context
->GetPermissionManager())
248 return PERMISSION_STATUS_DENIED
;
250 // If the embedding_origin is empty we'll use |origin| instead.
251 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
252 return browser_context
->GetPermissionManager()->GetPermissionStatus(
253 type
, origin
, embedding_origin
.is_empty() ? origin
: embedding_origin
);
256 void PermissionServiceImpl::ResetPermissionStatus(PermissionType type
,
257 const GURL
& origin
) {
258 BrowserContext
* browser_context
= context_
->GetBrowserContext();
259 DCHECK(browser_context
);
260 if (!browser_context
->GetPermissionManager())
263 // If the embedding_origin is empty we'll use |origin| instead.
264 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
265 browser_context
->GetPermissionManager()->ResetPermission(
266 type
, origin
, embedding_origin
.is_empty() ? origin
: embedding_origin
);
269 void PermissionServiceImpl::OnPermissionStatusChanged(
270 int pending_subscription_id
,
271 PermissionStatus status
) {
272 PendingSubscription
* subscription
=
273 pending_subscriptions_
.Lookup(pending_subscription_id
);
275 BrowserContext
* browser_context
= context_
->GetBrowserContext();
276 DCHECK(browser_context
);
277 if (browser_context
->GetPermissionManager()) {
278 browser_context
->GetPermissionManager()->UnsubscribePermissionStatusChange(
282 PermissionStatusCallback callback
= subscription
->callback
;
284 subscription
->callback
.reset();
285 pending_subscriptions_
.Remove(pending_subscription_id
);
287 callback
.Run(status
);
290 } // namespace content