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 "chrome/browser/ui/website_settings/permission_bubble_manager.h"
13 #include "components/content_settings/core/browser/host_content_settings_map.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
;
24 // Helper method to convert ContentSetting to PermissionStatus.
25 PermissionStatus
ContentSettingToPermissionStatus(ContentSetting setting
) {
27 case CONTENT_SETTING_ALLOW
:
28 case CONTENT_SETTING_SESSION_ONLY
:
29 return content::PERMISSION_STATUS_GRANTED
;
30 case CONTENT_SETTING_BLOCK
:
31 return content::PERMISSION_STATUS_DENIED
;
32 case CONTENT_SETTING_ASK
:
33 return content::PERMISSION_STATUS_ASK
;
34 case CONTENT_SETTING_DETECT_IMPORTANT_CONTENT
:
35 case CONTENT_SETTING_DEFAULT
:
36 case CONTENT_SETTING_NUM_SETTINGS
:
41 return content::PERMISSION_STATUS_DENIED
;
44 // Helper method to convert PermissionType to ContentSettingType.
45 ContentSettingsType
PermissionTypeToContentSetting(PermissionType permission
) {
47 case PermissionType::MIDI_SYSEX
:
48 return CONTENT_SETTINGS_TYPE_MIDI_SYSEX
;
49 case PermissionType::PUSH_MESSAGING
:
50 return CONTENT_SETTINGS_TYPE_PUSH_MESSAGING
;
51 case PermissionType::NOTIFICATIONS
:
52 return CONTENT_SETTINGS_TYPE_NOTIFICATIONS
;
53 case PermissionType::GEOLOCATION
:
54 return CONTENT_SETTINGS_TYPE_GEOLOCATION
;
55 case PermissionType::PROTECTED_MEDIA_IDENTIFIER
:
56 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
57 return CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER
;
62 case PermissionType::DURABLE_STORAGE
:
63 return CONTENT_SETTINGS_TYPE_DURABLE_STORAGE
;
64 case PermissionType::MIDI
:
65 // This will hit the NOTREACHED below.
67 case PermissionType::AUDIO_CAPTURE
:
68 return CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
;
69 case PermissionType::VIDEO_CAPTURE
:
70 return CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
;
71 case PermissionType::NUM
:
72 // This will hit the NOTREACHED below.
76 NOTREACHED() << "Unknown content setting for permission "
77 << static_cast<int>(permission
);
78 return CONTENT_SETTINGS_TYPE_DEFAULT
;
81 // Helper method that wraps a callback a void(PermissionStatus)
82 // callback into a void(ContentSetting) callback.
83 void PermissionStatusCallbackWrapper(
84 const base::Callback
<void(PermissionStatus
)>& callback
,
85 ContentSetting content_setting
) {
86 callback
.Run(ContentSettingToPermissionStatus(content_setting
));
89 // Returns whether the permission has a constant PermissionStatus value (i.e.
90 // always approved or always denied)
91 // The PermissionTypes for which true is returned should be exactly those which
92 // return nullptr in PermissionContext::Get since they don't have a context.
93 bool IsConstantPermission(PermissionType type
) {
95 case PermissionType::MIDI
:
102 // Function used for handling permission types which do not change their
103 // value i.e. they are always approved or always denied etc.
104 // CONTENT_SETTING_DEFAULT is returned if the permission needs further handling.
105 // This function should only be called when IsConstantPermission has returned
106 // true for the PermissionType.
107 ContentSetting
GetContentSettingForConstantPermission(PermissionType type
) {
108 DCHECK(IsConstantPermission(type
));
110 case PermissionType::MIDI
:
111 return CONTENT_SETTING_ALLOW
;
113 return CONTENT_SETTING_DEFAULT
;
117 PermissionStatus
GetPermissionStatusForConstantPermission(PermissionType type
) {
118 return ContentSettingToPermissionStatus(
119 GetContentSettingForConstantPermission(type
));
122 } // anonymous namespace
124 struct PermissionManager::Subscription
{
125 PermissionType permission
;
126 GURL requesting_origin
;
127 GURL embedding_origin
;
128 base::Callback
<void(PermissionStatus
)> callback
;
129 ContentSetting current_value
;
132 PermissionManager::PermissionManager(Profile
* profile
)
133 : profile_(profile
) {
136 PermissionManager::~PermissionManager() {
137 if (!subscriptions_
.IsEmpty())
138 profile_
->GetHostContentSettingsMap()->RemoveObserver(this);
141 void PermissionManager::RequestPermission(
142 PermissionType permission
,
143 content::RenderFrameHost
* render_frame_host
,
145 const GURL
& requesting_origin
,
147 const base::Callback
<void(PermissionStatus
)>& callback
) {
148 if (IsConstantPermission(permission
)) {
149 callback
.Run(GetPermissionStatusForConstantPermission(permission
));
153 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
155 callback
.Run(content::PERMISSION_STATUS_DENIED
);
159 content::WebContents
* web_contents
=
160 content::WebContents::FromRenderFrameHost(render_frame_host
);
161 if (IsPermissionBubbleManagerMissing(web_contents
)) {
163 GetPermissionStatus(permission
, requesting_origin
,
164 web_contents
->GetLastCommittedURL().GetOrigin()));
168 int render_process_id
= render_frame_host
->GetProcess()->GetID();
169 int render_frame_id
= render_frame_host
->GetRoutingID();
170 const PermissionRequestID
request(render_process_id
,
174 context
->RequestPermission(
175 web_contents
, request
, requesting_origin
, user_gesture
,
176 base::Bind(&PermissionStatusCallbackWrapper
, callback
));
179 void PermissionManager::CancelPermissionRequest(
180 PermissionType permission
,
181 content::RenderFrameHost
* render_frame_host
,
183 const GURL
& requesting_origin
) {
184 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
188 content::WebContents
* web_contents
=
189 content::WebContents::FromRenderFrameHost(render_frame_host
);
190 if (IsPermissionBubbleManagerMissing(web_contents
))
193 int render_process_id
= render_frame_host
->GetProcess()->GetID();
194 int render_frame_id
= render_frame_host
->GetRoutingID();
195 const PermissionRequestID
request(render_process_id
,
199 context
->CancelPermissionRequest(web_contents
, request
);
202 void PermissionManager::ResetPermission(PermissionType permission
,
203 const GURL
& requesting_origin
,
204 const GURL
& embedding_origin
) {
205 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
209 context
->ResetPermission(requesting_origin
.GetOrigin(),
210 embedding_origin
.GetOrigin());
213 PermissionStatus
PermissionManager::GetPermissionStatus(
214 PermissionType permission
,
215 const GURL
& requesting_origin
,
216 const GURL
& embedding_origin
) {
217 if (IsConstantPermission(permission
))
218 return GetPermissionStatusForConstantPermission(permission
);
220 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
222 return content::PERMISSION_STATUS_DENIED
;
224 return ContentSettingToPermissionStatus(context
->GetPermissionStatus(
225 requesting_origin
.GetOrigin(), embedding_origin
.GetOrigin()));
228 void PermissionManager::RegisterPermissionUsage(PermissionType permission
,
229 const GURL
& requesting_origin
,
230 const GURL
& embedding_origin
) {
231 // This is required because constant permissions don't have a
232 // ContentSettingsType.
233 if (IsConstantPermission(permission
))
236 profile_
->GetHostContentSettingsMap()->UpdateLastUsage(
239 PermissionTypeToContentSetting(permission
));
242 int PermissionManager::SubscribePermissionStatusChange(
243 PermissionType permission
,
244 const GURL
& requesting_origin
,
245 const GURL
& embedding_origin
,
246 const base::Callback
<void(PermissionStatus
)>& callback
) {
247 if (subscriptions_
.IsEmpty())
248 profile_
->GetHostContentSettingsMap()->AddObserver(this);
250 Subscription
* subscription
= new Subscription();
251 subscription
->permission
= permission
;
252 subscription
->requesting_origin
= requesting_origin
;
253 subscription
->embedding_origin
= embedding_origin
;
254 subscription
->callback
= callback
;
256 if (IsConstantPermission(permission
)) {
257 subscription
->current_value
= GetContentSettingForConstantPermission(
260 subscription
->current_value
= PermissionContext::Get(profile_
, permission
)
261 ->GetPermissionStatus(subscription
->requesting_origin
,
262 subscription
->embedding_origin
);
265 return subscriptions_
.Add(subscription
);
268 void PermissionManager::UnsubscribePermissionStatusChange(int subscription_id
) {
269 // Whether |subscription_id| is known will be checked by the Remove() call.
270 subscriptions_
.Remove(subscription_id
);
272 if (subscriptions_
.IsEmpty())
273 profile_
->GetHostContentSettingsMap()->RemoveObserver(this);
276 bool PermissionManager::IsPermissionBubbleManagerMissing(
277 content::WebContents
* web_contents
) {
278 // Can't be missing if it isn't needed to begin with.
279 if (!PermissionBubbleManager::Enabled())
282 return PermissionBubbleManager::FromWebContents(web_contents
) == nullptr;
285 void PermissionManager::OnContentSettingChanged(
286 const ContentSettingsPattern
& primary_pattern
,
287 const ContentSettingsPattern
& secondary_pattern
,
288 ContentSettingsType content_type
,
289 std::string resource_identifier
) {
290 std::list
<base::Closure
> callbacks
;
292 for (SubscriptionsMap::iterator
iter(&subscriptions_
);
293 !iter
.IsAtEnd(); iter
.Advance()) {
294 Subscription
* subscription
= iter
.GetCurrentValue();
295 if (PermissionTypeToContentSetting(subscription
->permission
) !=
300 if (primary_pattern
.IsValid() &&
301 !primary_pattern
.Matches(subscription
->requesting_origin
))
303 if (secondary_pattern
.IsValid() &&
304 !secondary_pattern
.Matches(subscription
->embedding_origin
))
307 ContentSetting new_value
=
308 PermissionContext::Get(profile_
, subscription
->permission
)
309 ->GetPermissionStatus(subscription
->requesting_origin
,
310 subscription
->embedding_origin
);
311 if (subscription
->current_value
== new_value
)
314 subscription
->current_value
= new_value
;
316 // Add the callback to |callbacks| which will be run after the loop to
317 // prevent re-entrance issues.
319 base::Bind(subscription
->callback
,
320 ContentSettingToPermissionStatus(new_value
)));
323 for (const auto& callback
: callbacks
)