1 // Copyright 2015 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 "android_webview/browser/aw_permission_manager.h"
9 #include "android_webview/browser/aw_browser_permission_request_delegate.h"
10 #include "base/callback.h"
11 #include "base/containers/hash_tables.h"
12 #include "base/logging.h"
13 #include "base/memory/weak_ptr.h"
14 #include "content/public/browser/permission_type.h"
15 #include "content/public/browser/render_frame_host.h"
16 #include "content/public/browser/render_process_host.h"
17 #include "content/public/browser/web_contents.h"
19 using content::PermissionStatus
;
20 using content::PermissionType
;
22 namespace android_webview
{
24 class LastRequestResultCache
{
26 LastRequestResultCache() : weak_factory_(this) {}
28 void SetResult(PermissionType permission
,
29 const GURL
& requesting_origin
,
30 const GURL
& embedding_origin
,
31 PermissionStatus status
) {
32 DCHECK(status
== content::PERMISSION_STATUS_GRANTED
||
33 status
== content::PERMISSION_STATUS_DENIED
);
35 // TODO(ddorwin): We should be denying empty origins at a higher level.
36 if (requesting_origin
.is_empty() || embedding_origin
.is_empty()) {
37 DLOG(WARNING
) << "Not caching result because of empty origin.";
41 if (!requesting_origin
.is_valid()) {
42 NOTREACHED() << requesting_origin
.possibly_invalid_spec();
45 if (!embedding_origin
.is_valid()) {
46 NOTREACHED() << embedding_origin
.possibly_invalid_spec();
50 if (permission
!= PermissionType::PROTECTED_MEDIA_IDENTIFIER
) {
51 // Other permissions are not cached.
55 std::string key
= GetCacheKey(requesting_origin
, embedding_origin
);
58 // Never store an empty key because it could inadvertently be used for
59 // another combination.
62 pmi_result_cache_
[key
] = status
;
65 PermissionStatus
GetResult(PermissionType permission
,
66 const GURL
& requesting_origin
,
67 const GURL
& embedding_origin
) const {
68 // TODO(ddorwin): We should be denying empty origins at a higher level.
69 if (requesting_origin
.is_empty() || embedding_origin
.is_empty()) {
70 return content::PERMISSION_STATUS_ASK
;
73 DCHECK(requesting_origin
.is_valid())
74 << requesting_origin
.possibly_invalid_spec();
75 DCHECK(embedding_origin
.is_valid())
76 << embedding_origin
.possibly_invalid_spec();
78 if (permission
!= PermissionType::PROTECTED_MEDIA_IDENTIFIER
) {
79 NOTREACHED() << "Results are only cached for PROTECTED_MEDIA_IDENTIFIER";
80 return content::PERMISSION_STATUS_ASK
;
83 std::string key
= GetCacheKey(requesting_origin
, embedding_origin
);
84 StatusMap::const_iterator it
= pmi_result_cache_
.find(key
);
85 if (it
== pmi_result_cache_
.end()) {
86 DLOG(WARNING
) << "GetResult() called for uncached origins: " << key
;
87 return content::PERMISSION_STATUS_ASK
;
94 void ClearResult(PermissionType permission
,
95 const GURL
& requesting_origin
,
96 const GURL
& embedding_origin
) {
97 // TODO(ddorwin): We should be denying empty origins at a higher level.
98 if (requesting_origin
.is_empty() || embedding_origin
.is_empty()) {
102 DCHECK(requesting_origin
.is_valid())
103 << requesting_origin
.possibly_invalid_spec();
104 DCHECK(embedding_origin
.is_valid())
105 << embedding_origin
.possibly_invalid_spec();
108 if (permission
!= PermissionType::PROTECTED_MEDIA_IDENTIFIER
) {
109 // Other permissions are not cached, so nothing to clear.
113 std::string key
= GetCacheKey(requesting_origin
, embedding_origin
);
114 pmi_result_cache_
.erase(key
);
117 base::WeakPtr
<LastRequestResultCache
> GetWeakPtr() {
118 return weak_factory_
.GetWeakPtr();
122 // Returns a concatenation of the origins to be used as the index.
123 // Returns the empty string if either origin is invalid or empty.
124 static std::string
GetCacheKey(const GURL
& requesting_origin
,
125 const GURL
& embedding_origin
) {
126 const std::string
& requesting
= requesting_origin
.spec();
127 const std::string
& embedding
= embedding_origin
.spec();
128 if (requesting
.empty() || embedding
.empty())
129 return std::string();
130 return requesting
+ "," + embedding
;
133 using StatusMap
= base::hash_map
<std::string
, PermissionStatus
>;
134 StatusMap pmi_result_cache_
;
136 base::WeakPtrFactory
<LastRequestResultCache
> weak_factory_
;
138 DISALLOW_COPY_AND_ASSIGN(LastRequestResultCache
);
143 void CallbackPermisisonStatusWrapper(
144 const base::WeakPtr
<LastRequestResultCache
>& result_cache
,
145 const base::Callback
<void(PermissionStatus
)>& callback
,
146 PermissionType permission
,
147 const GURL
& requesting_origin
,
148 const GURL
& embedding_origin
,
150 PermissionStatus status
= allowed
? content::PERMISSION_STATUS_GRANTED
151 : content::PERMISSION_STATUS_DENIED
;
152 if (result_cache
.get()) {
153 result_cache
->SetResult(permission
, requesting_origin
, embedding_origin
,
157 callback
.Run(status
);
160 } // anonymous namespace
162 AwPermissionManager::AwPermissionManager()
163 : content::PermissionManager(), result_cache_(new LastRequestResultCache
) {
166 AwPermissionManager::~AwPermissionManager() {
169 void AwPermissionManager::RequestPermission(
170 PermissionType permission
,
171 content::RenderFrameHost
* render_frame_host
,
175 const base::Callback
<void(PermissionStatus
)>& callback
) {
176 int render_process_id
= render_frame_host
->GetProcess()->GetID();
177 int render_frame_id
= render_frame_host
->GetRoutingID();
178 AwBrowserPermissionRequestDelegate
* delegate
=
179 AwBrowserPermissionRequestDelegate::FromID(render_process_id
,
182 DVLOG(0) << "Dropping permission request for "
183 << static_cast<int>(permission
);
184 callback
.Run(content::PERMISSION_STATUS_DENIED
);
188 const GURL
& embedding_origin
=
189 content::WebContents::FromRenderFrameHost(render_frame_host
)
190 ->GetLastCommittedURL().GetOrigin();
192 switch (permission
) {
193 case PermissionType::GEOLOCATION
:
194 delegate
->RequestGeolocationPermission(
195 origin
, base::Bind(&CallbackPermisisonStatusWrapper
,
196 result_cache_
->GetWeakPtr(), callback
, permission
,
197 origin
, embedding_origin
));
199 case PermissionType::PROTECTED_MEDIA_IDENTIFIER
:
200 delegate
->RequestProtectedMediaIdentifierPermission(
201 origin
, base::Bind(&CallbackPermisisonStatusWrapper
,
202 result_cache_
->GetWeakPtr(), callback
, permission
,
203 origin
, embedding_origin
));
205 case PermissionType::MIDI_SYSEX
:
206 delegate
->RequestMIDISysexPermission(
207 origin
, base::Bind(&CallbackPermisisonStatusWrapper
,
208 result_cache_
->GetWeakPtr(), callback
, permission
,
209 origin
, embedding_origin
));
211 case PermissionType::NOTIFICATIONS
:
212 case PermissionType::PUSH_MESSAGING
:
213 case PermissionType::DURABLE_STORAGE
:
214 NOTIMPLEMENTED() << "RequestPermission is not implemented for "
215 << static_cast<int>(permission
);
216 callback
.Run(content::PERMISSION_STATUS_DENIED
);
218 case PermissionType::MIDI
:
219 callback
.Run(content::PERMISSION_STATUS_GRANTED
);
221 case PermissionType::NUM
:
222 NOTREACHED() << "PermissionType::NUM was not expected here.";
223 callback
.Run(content::PERMISSION_STATUS_DENIED
);
228 void AwPermissionManager::CancelPermissionRequest(
229 PermissionType permission
,
230 content::RenderFrameHost
* render_frame_host
,
232 const GURL
& origin
) {
233 // The caller is canceling (presumably) the most recent request. Assuming the
234 // request did not complete, the user did not respond to the requset.
235 // Thus, assume we do not know the result.
236 const GURL
& embedding_origin
=
237 content::WebContents::FromRenderFrameHost(render_frame_host
)
238 ->GetLastCommittedURL().GetOrigin();
239 result_cache_
->ClearResult(permission
, origin
, embedding_origin
);
241 int render_process_id
= render_frame_host
->GetProcess()->GetID();
242 int render_frame_id
= render_frame_host
->GetRoutingID();
243 AwBrowserPermissionRequestDelegate
* delegate
=
244 AwBrowserPermissionRequestDelegate::FromID(render_process_id
,
249 switch (permission
) {
250 case PermissionType::GEOLOCATION
:
251 delegate
->CancelGeolocationPermissionRequests(origin
);
253 case PermissionType::PROTECTED_MEDIA_IDENTIFIER
:
254 delegate
->CancelProtectedMediaIdentifierPermissionRequests(origin
);
256 case PermissionType::MIDI_SYSEX
:
257 delegate
->CancelMIDISysexPermissionRequests(origin
);
259 case PermissionType::NOTIFICATIONS
:
260 case PermissionType::PUSH_MESSAGING
:
261 case PermissionType::DURABLE_STORAGE
:
262 NOTIMPLEMENTED() << "CancelPermission not implemented for "
263 << static_cast<int>(permission
);
265 case PermissionType::MIDI
:
266 // There is nothing to cancel so this is simply ignored.
268 case PermissionType::NUM
:
269 NOTREACHED() << "PermissionType::NUM was not expected here.";
274 void AwPermissionManager::ResetPermission(PermissionType permission
,
275 const GURL
& requesting_origin
,
276 const GURL
& embedding_origin
) {
277 result_cache_
->ClearResult(permission
, requesting_origin
, embedding_origin
);
280 PermissionStatus
AwPermissionManager::GetPermissionStatus(
281 PermissionType permission
,
282 const GURL
& requesting_origin
,
283 const GURL
& embedding_origin
) {
284 // Method is called outside the Permissions API only for this permission.
285 if (permission
== PermissionType::PROTECTED_MEDIA_IDENTIFIER
) {
286 return result_cache_
->GetResult(permission
, requesting_origin
,
288 } else if (permission
== PermissionType::MIDI
) {
289 return content::PERMISSION_STATUS_GRANTED
;
292 return content::PERMISSION_STATUS_DENIED
;
295 void AwPermissionManager::RegisterPermissionUsage(
296 PermissionType permission
,
297 const GURL
& requesting_origin
,
298 const GURL
& embedding_origin
) {
301 int AwPermissionManager::SubscribePermissionStatusChange(
302 PermissionType permission
,
303 const GURL
& requesting_origin
,
304 const GURL
& embedding_origin
,
305 const base::Callback
<void(PermissionStatus
)>& callback
) {
309 void AwPermissionManager::UnsubscribePermissionStatusChange(
310 int subscription_id
) {
313 } // namespace android_webview