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 "chrome/browser/permissions/permission_manager.h"
7 #include "base/callback.h"
8 #include "chrome/browser/permissions/permission_context.h"
9 #include "chrome/browser/permissions/permission_context_base.h"
10 #include "chrome/browser/permissions/permission_request_id.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "components/content_settings/core/browser/host_content_settings_map.h"
13 #include "content/public/browser/permission_type.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/web_contents.h"
18 using content::PermissionStatus
;
19 using content::PermissionType
;
23 // Helper method to convert ContentSetting to PermissionStatus.
24 PermissionStatus
ContentSettingToPermissionStatus(ContentSetting setting
) {
26 case CONTENT_SETTING_ALLOW
:
27 case CONTENT_SETTING_SESSION_ONLY
:
28 return content::PERMISSION_STATUS_GRANTED
;
29 case CONTENT_SETTING_BLOCK
:
30 return content::PERMISSION_STATUS_DENIED
;
31 case CONTENT_SETTING_ASK
:
32 return content::PERMISSION_STATUS_ASK
;
33 case CONTENT_SETTING_DETECT_IMPORTANT_CONTENT
:
34 case CONTENT_SETTING_DEFAULT
:
35 case CONTENT_SETTING_NUM_SETTINGS
:
40 return content::PERMISSION_STATUS_DENIED
;
43 // Helper method to convert PermissionType to ContentSettingType.
44 ContentSettingsType
PermissionTypeToContentSetting(PermissionType permission
) {
46 case PermissionType::MIDI_SYSEX
:
47 return CONTENT_SETTINGS_TYPE_MIDI_SYSEX
;
48 case PermissionType::PUSH_MESSAGING
:
49 return CONTENT_SETTINGS_TYPE_PUSH_MESSAGING
;
50 case PermissionType::NOTIFICATIONS
:
51 return CONTENT_SETTINGS_TYPE_NOTIFICATIONS
;
52 case PermissionType::GEOLOCATION
:
53 return CONTENT_SETTINGS_TYPE_GEOLOCATION
;
54 case PermissionType::PROTECTED_MEDIA_IDENTIFIER
:
55 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
56 return CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER
;
61 case PermissionType::DURABLE_STORAGE
:
62 return CONTENT_SETTINGS_TYPE_DURABLE_STORAGE
;
63 case PermissionType::MIDI
:
64 // This will hit the NOTREACHED below.
66 case PermissionType::NUM
:
67 // This will hit the NOTREACHED below.
71 NOTREACHED() << "Unknown content setting for permission "
72 << static_cast<int>(permission
);
73 return CONTENT_SETTINGS_TYPE_DEFAULT
;
76 // Helper method that wraps a callback a void(PermissionStatus)
77 // callback into a void(ContentSetting) callback.
78 void PermissionStatusCallbackWrapper(
79 const base::Callback
<void(PermissionStatus
)>& callback
,
80 ContentSetting content_setting
) {
81 callback
.Run(ContentSettingToPermissionStatus(content_setting
));
84 // Returns whether the permission has a constant PermissionStatus value (i.e.
85 // always approved or always denied)
86 // The PermissionTypes for which true is returned should be exactly those which
87 // return nullptr in PermissionContext::Get since they don't have a context.
88 bool IsConstantPermission(PermissionType type
) {
90 case PermissionType::MIDI
:
97 // Function used for handling permission types which do not change their
98 // value i.e. they are always approved or always denied etc.
99 // CONTENT_SETTING_DEFAULT is returned if the permission needs further handling.
100 // This function should only be called when IsConstantPermission has returned
101 // true for the PermissionType.
102 ContentSetting
GetContentSettingForConstantPermission(PermissionType type
) {
103 DCHECK(IsConstantPermission(type
));
105 case PermissionType::MIDI
:
106 return CONTENT_SETTING_ALLOW
;
108 return CONTENT_SETTING_DEFAULT
;
112 PermissionStatus
GetPermissionStatusForConstantPermission(PermissionType type
) {
113 return ContentSettingToPermissionStatus(
114 GetContentSettingForConstantPermission(type
));
117 } // anonymous namespace
119 struct PermissionManager::Subscription
{
120 PermissionType permission
;
121 GURL requesting_origin
;
122 GURL embedding_origin
;
123 base::Callback
<void(PermissionStatus
)> callback
;
124 ContentSetting current_value
;
127 PermissionManager::PermissionManager(Profile
* profile
)
128 : profile_(profile
) {
131 PermissionManager::~PermissionManager() {
132 if (!subscriptions_
.IsEmpty())
133 profile_
->GetHostContentSettingsMap()->RemoveObserver(this);
136 void PermissionManager::RequestPermission(
137 PermissionType permission
,
138 content::RenderFrameHost
* render_frame_host
,
140 const GURL
& requesting_origin
,
142 const base::Callback
<void(PermissionStatus
)>& callback
) {
143 if (IsConstantPermission(permission
)) {
144 callback
.Run(GetPermissionStatusForConstantPermission(permission
));
148 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
150 callback
.Run(content::PERMISSION_STATUS_DENIED
);
154 int render_process_id
= render_frame_host
->GetProcess()->GetID();
155 int render_frame_id
= render_frame_host
->GetRoutingID();
156 const PermissionRequestID
request(render_process_id
,
161 context
->RequestPermission(
162 content::WebContents::FromRenderFrameHost(render_frame_host
),
163 request
, requesting_origin
, user_gesture
,
164 base::Bind(&PermissionStatusCallbackWrapper
,
168 void PermissionManager::CancelPermissionRequest(
169 PermissionType permission
,
170 content::RenderFrameHost
* render_frame_host
,
172 const GURL
& requesting_origin
) {
173 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
177 int render_process_id
= render_frame_host
->GetProcess()->GetID();
178 int render_frame_id
= render_frame_host
->GetRoutingID();
179 const PermissionRequestID
request(render_process_id
,
184 context
->CancelPermissionRequest(
185 content::WebContents::FromRenderFrameHost(render_frame_host
), request
);
188 void PermissionManager::ResetPermission(PermissionType permission
,
189 const GURL
& requesting_origin
,
190 const GURL
& embedding_origin
) {
191 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
195 context
->ResetPermission(requesting_origin
.GetOrigin(),
196 embedding_origin
.GetOrigin());
199 PermissionStatus
PermissionManager::GetPermissionStatus(
200 PermissionType permission
,
201 const GURL
& requesting_origin
,
202 const GURL
& embedding_origin
) {
203 if (IsConstantPermission(permission
))
204 return GetPermissionStatusForConstantPermission(permission
);
206 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
208 return content::PERMISSION_STATUS_DENIED
;
210 return ContentSettingToPermissionStatus(
211 context
->GetPermissionStatus(requesting_origin
.GetOrigin(),
212 embedding_origin
.GetOrigin()));
215 void PermissionManager::RegisterPermissionUsage(PermissionType permission
,
216 const GURL
& requesting_origin
,
217 const GURL
& embedding_origin
) {
218 // This is required because constant permissions don't have a
219 // ContentSettingsType.
220 if (IsConstantPermission(permission
))
223 profile_
->GetHostContentSettingsMap()->UpdateLastUsage(
226 PermissionTypeToContentSetting(permission
));
229 int PermissionManager::SubscribePermissionStatusChange(
230 PermissionType permission
,
231 const GURL
& requesting_origin
,
232 const GURL
& embedding_origin
,
233 const base::Callback
<void(PermissionStatus
)>& callback
) {
234 if (subscriptions_
.IsEmpty())
235 profile_
->GetHostContentSettingsMap()->AddObserver(this);
237 Subscription
* subscription
= new Subscription();
238 subscription
->permission
= permission
;
239 subscription
->requesting_origin
= requesting_origin
;
240 subscription
->embedding_origin
= embedding_origin
;
241 subscription
->callback
= callback
;
243 if (IsConstantPermission(permission
)) {
244 subscription
->current_value
= GetContentSettingForConstantPermission(
247 subscription
->current_value
= PermissionContext::Get(profile_
, permission
)
248 ->GetPermissionStatus(subscription
->requesting_origin
,
249 subscription
->embedding_origin
);
252 return subscriptions_
.Add(subscription
);
255 void PermissionManager::UnsubscribePermissionStatusChange(int subscription_id
) {
256 // Whether |subscription_id| is known will be checked by the Remove() call.
257 subscriptions_
.Remove(subscription_id
);
259 if (subscriptions_
.IsEmpty())
260 profile_
->GetHostContentSettingsMap()->RemoveObserver(this);
263 void PermissionManager::OnContentSettingChanged(
264 const ContentSettingsPattern
& primary_pattern
,
265 const ContentSettingsPattern
& secondary_pattern
,
266 ContentSettingsType content_type
,
267 std::string resource_identifier
) {
268 std::list
<base::Closure
> callbacks
;
270 for (SubscriptionsMap::iterator
iter(&subscriptions_
);
271 !iter
.IsAtEnd(); iter
.Advance()) {
272 Subscription
* subscription
= iter
.GetCurrentValue();
273 if (PermissionTypeToContentSetting(subscription
->permission
) !=
278 if (primary_pattern
.IsValid() &&
279 !primary_pattern
.Matches(subscription
->requesting_origin
))
281 if (secondary_pattern
.IsValid() &&
282 !secondary_pattern
.Matches(subscription
->embedding_origin
))
285 ContentSetting new_value
=
286 PermissionContext::Get(profile_
, subscription
->permission
)
287 ->GetPermissionStatus(subscription
->requesting_origin
,
288 subscription
->embedding_origin
);
289 if (subscription
->current_value
== new_value
)
292 subscription
->current_value
= new_value
;
294 // Add the callback to |callbacks| which will be run after the loop to
295 // prevent re-entrance issues.
297 base::Bind(subscription
->callback
,
298 ContentSettingToPermissionStatus(new_value
)));
301 for (const auto& callback
: callbacks
)