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(PermissionServiceContext
* context
)
70 PermissionServiceImpl::~PermissionServiceImpl() {
71 DCHECK(pending_requests_
.IsEmpty());
74 void PermissionServiceImpl::OnConnectionError() {
75 context_
->ServiceHadConnectionError(this);
76 // After that call, |this| will be deleted.
79 void PermissionServiceImpl::RequestPermission(
80 PermissionName permission
,
81 const mojo::String
& origin
,
83 const PermissionStatusCallback
& callback
) {
84 // This condition is valid if the call is coming from a ChildThread instead of
85 // a RenderFrame. Some consumers of the service run in Workers and some in
86 // Frames. In the context of a Worker, it is not possible to show a
87 // permission prompt because there is no tab. In the context of a Frame, we
88 // can. Even if the call comes from a context where it is not possible to show
89 // any UI, we want to still return something relevant so the current
90 // permission status is returned.
91 if (!context_
->web_contents()) {
92 // There is no way to show a UI so the call will simply return the current
94 HasPermission(permission
, origin
, callback
);
98 BrowserContext
* browser_context
= context_
->GetBrowserContext();
99 DCHECK(browser_context
);
100 if (!browser_context
->GetPermissionManager()) {
101 callback
.Run(content::PERMISSION_STATUS_DENIED
);
105 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
106 int request_id
= pending_requests_
.Add(
107 new PendingRequest(permission_type
, GURL(origin
), callback
));
109 browser_context
->GetPermissionManager()->RequestPermission(
111 context_
->web_contents(),
114 user_gesture
, // TODO(mlamouri): should be removed (crbug.com/423770)
115 base::Bind(&PermissionServiceImpl::OnRequestPermissionResponse
,
116 weak_factory_
.GetWeakPtr(),
120 void PermissionServiceImpl::OnRequestPermissionResponse(
122 PermissionStatus status
) {
123 PendingRequest
* request
= pending_requests_
.Lookup(request_id
);
124 PermissionStatusCallback
callback(request
->callback
);
125 request
->callback
.reset();
126 pending_requests_
.Remove(request_id
);
127 callback
.Run(status
);
130 void PermissionServiceImpl::CancelPendingOperations() {
131 DCHECK(context_
->web_contents());
132 DCHECK(context_
->GetBrowserContext());
134 PermissionManager
* permission_manager
=
135 context_
->GetBrowserContext()->GetPermissionManager();
136 if (!permission_manager
)
139 // Cancel pending requests.
140 for (RequestsMap::Iterator
<PendingRequest
> it(&pending_requests_
);
141 !it
.IsAtEnd(); it
.Advance()) {
142 permission_manager
->CancelPermissionRequest(
143 it
.GetCurrentValue()->permission
,
144 context_
->web_contents(),
146 it
.GetCurrentValue()->origin
);
148 pending_requests_
.Clear();
150 // Cancel pending subscriptions.
151 for (SubscriptionsMap::Iterator
<PendingSubscription
>
152 it(&pending_subscriptions_
); !it
.IsAtEnd(); it
.Advance()) {
153 it
.GetCurrentValue()->callback
.Run(GetPermissionStatusFromType(
154 it
.GetCurrentValue()->permission
, it
.GetCurrentValue()->origin
));
155 it
.GetCurrentValue()->callback
.reset();
156 permission_manager
->UnsubscribePermissionStatusChange(
157 it
.GetCurrentValue()->id
);
159 pending_subscriptions_
.Clear();
162 void PermissionServiceImpl::HasPermission(
163 PermissionName permission
,
164 const mojo::String
& origin
,
165 const PermissionStatusCallback
& callback
) {
166 callback
.Run(GetPermissionStatusFromName(permission
, GURL(origin
)));
169 void PermissionServiceImpl::RevokePermission(
170 PermissionName permission
,
171 const mojo::String
& origin
,
172 const PermissionStatusCallback
& callback
) {
173 GURL
origin_url(origin
);
174 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
175 PermissionStatus status
= GetPermissionStatusFromType(permission_type
,
178 // Resetting the permission should only be possible if the permission is
180 if (status
!= PERMISSION_STATUS_GRANTED
) {
181 callback
.Run(status
);
185 ResetPermissionStatus(permission_type
, origin_url
);
187 callback
.Run(GetPermissionStatusFromType(permission_type
, origin_url
));
190 void PermissionServiceImpl::GetNextPermissionChange(
191 PermissionName permission
,
192 const mojo::String
& mojo_origin
,
193 PermissionStatus last_known_status
,
194 const PermissionStatusCallback
& callback
) {
195 GURL
origin(mojo_origin
);
196 PermissionStatus current_status
=
197 GetPermissionStatusFromName(permission
, origin
);
198 if (current_status
!= last_known_status
) {
199 callback
.Run(current_status
);
203 BrowserContext
* browser_context
= context_
->GetBrowserContext();
204 DCHECK(browser_context
);
205 if (!browser_context
->GetPermissionManager()) {
206 callback
.Run(current_status
);
210 PermissionType permission_type
= PermissionNameToPermissionType(permission
);
212 // We need to pass the id of PendingSubscription in pending_subscriptions_
213 // to the callback but SubscribePermissionStatusChange() will also return an
214 // id which is different.
215 PendingSubscription
* subscription
=
216 new PendingSubscription(permission_type
, origin
, callback
);
217 int pending_subscription_id
= pending_subscriptions_
.Add(subscription
);
219 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
221 browser_context
->GetPermissionManager()->SubscribePermissionStatusChange(
224 // If the embedding_origin is empty, we,ll use the |origin| instead.
225 embedding_origin
.is_empty() ? origin
: embedding_origin
,
226 base::Bind(&PermissionServiceImpl::OnPermissionStatusChanged
,
227 weak_factory_
.GetWeakPtr(),
228 pending_subscription_id
));
231 PermissionStatus
PermissionServiceImpl::GetPermissionStatusFromName(
232 PermissionName permission
, const GURL
& origin
) {
233 return GetPermissionStatusFromType(PermissionNameToPermissionType(permission
),
237 PermissionStatus
PermissionServiceImpl::GetPermissionStatusFromType(
238 PermissionType type
, const GURL
& origin
) {
239 BrowserContext
* browser_context
= context_
->GetBrowserContext();
240 DCHECK(browser_context
);
241 if (!browser_context
->GetPermissionManager())
242 return PERMISSION_STATUS_DENIED
;
244 // If the embedding_origin is empty we'll use |origin| instead.
245 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
246 return browser_context
->GetPermissionManager()->GetPermissionStatus(
247 type
, origin
, embedding_origin
.is_empty() ? origin
: embedding_origin
);
250 void PermissionServiceImpl::ResetPermissionStatus(PermissionType type
,
251 const GURL
& origin
) {
252 BrowserContext
* browser_context
= context_
->GetBrowserContext();
253 DCHECK(browser_context
);
254 if (!browser_context
->GetPermissionManager())
257 // If the embedding_origin is empty we'll use |origin| instead.
258 GURL embedding_origin
= context_
->GetEmbeddingOrigin();
259 browser_context
->GetPermissionManager()->ResetPermission(
260 type
, origin
, embedding_origin
.is_empty() ? origin
: embedding_origin
);
263 void PermissionServiceImpl::OnPermissionStatusChanged(
264 int pending_subscription_id
,
265 PermissionStatus status
) {
266 PendingSubscription
* subscription
=
267 pending_subscriptions_
.Lookup(pending_subscription_id
);
269 BrowserContext
* browser_context
= context_
->GetBrowserContext();
270 DCHECK(browser_context
);
271 if (browser_context
->GetPermissionManager()) {
272 browser_context
->GetPermissionManager()->UnsubscribePermissionStatusChange(
276 PermissionStatusCallback callback
= subscription
->callback
;
278 subscription
->callback
.reset();
279 pending_subscriptions_
.Remove(pending_subscription_id
);
281 callback
.Run(status
);
284 } // namespace content