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/content_settings/host_content_settings_map_factory.h"
9 #include "chrome/browser/permissions/permission_context.h"
10 #include "chrome/browser/permissions/permission_context_base.h"
11 #include "chrome/browser/permissions/permission_request_id.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
14 #include "components/content_settings/core/browser/host_content_settings_map.h"
15 #include "content/public/browser/permission_type.h"
16 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/browser/render_process_host.h"
18 #include "content/public/browser/web_contents.h"
20 using content::PermissionStatus
;
21 using content::PermissionType
;
25 // Helper method to convert ContentSetting to PermissionStatus.
26 PermissionStatus
ContentSettingToPermissionStatus(ContentSetting setting
) {
28 case CONTENT_SETTING_ALLOW
:
29 case CONTENT_SETTING_SESSION_ONLY
:
30 return content::PERMISSION_STATUS_GRANTED
;
31 case CONTENT_SETTING_BLOCK
:
32 return content::PERMISSION_STATUS_DENIED
;
33 case CONTENT_SETTING_ASK
:
34 return content::PERMISSION_STATUS_ASK
;
35 case CONTENT_SETTING_DETECT_IMPORTANT_CONTENT
:
36 case CONTENT_SETTING_DEFAULT
:
37 case CONTENT_SETTING_NUM_SETTINGS
:
42 return content::PERMISSION_STATUS_DENIED
;
45 // Helper method to convert PermissionType to ContentSettingType.
46 ContentSettingsType
PermissionTypeToContentSetting(PermissionType permission
) {
48 case PermissionType::MIDI_SYSEX
:
49 return CONTENT_SETTINGS_TYPE_MIDI_SYSEX
;
50 case PermissionType::PUSH_MESSAGING
:
51 return CONTENT_SETTINGS_TYPE_PUSH_MESSAGING
;
52 case PermissionType::NOTIFICATIONS
:
53 return CONTENT_SETTINGS_TYPE_NOTIFICATIONS
;
54 case PermissionType::GEOLOCATION
:
55 return CONTENT_SETTINGS_TYPE_GEOLOCATION
;
56 case PermissionType::PROTECTED_MEDIA_IDENTIFIER
:
57 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
58 return CONTENT_SETTINGS_TYPE_PROTECTED_MEDIA_IDENTIFIER
;
63 case PermissionType::DURABLE_STORAGE
:
64 return CONTENT_SETTINGS_TYPE_DURABLE_STORAGE
;
65 case PermissionType::MIDI
:
66 // This will hit the NOTREACHED below.
68 case PermissionType::AUDIO_CAPTURE
:
69 return CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
;
70 case PermissionType::VIDEO_CAPTURE
:
71 return CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
;
72 case PermissionType::NUM
:
73 // This will hit the NOTREACHED below.
77 NOTREACHED() << "Unknown content setting for permission "
78 << static_cast<int>(permission
);
79 return CONTENT_SETTINGS_TYPE_DEFAULT
;
82 // Helper method that wraps a callback a void(PermissionStatus)
83 // callback into a void(ContentSetting) callback.
84 void PermissionStatusCallbackWrapper(
85 const base::Callback
<void(PermissionStatus
)>& callback
,
86 ContentSetting content_setting
) {
87 callback
.Run(ContentSettingToPermissionStatus(content_setting
));
90 // Returns whether the permission has a constant PermissionStatus value (i.e.
91 // always approved or always denied)
92 // The PermissionTypes for which true is returned should be exactly those which
93 // return nullptr in PermissionContext::Get since they don't have a context.
94 bool IsConstantPermission(PermissionType type
) {
96 case PermissionType::MIDI
:
103 // Function used for handling permission types which do not change their
104 // value i.e. they are always approved or always denied etc.
105 // CONTENT_SETTING_DEFAULT is returned if the permission needs further handling.
106 // This function should only be called when IsConstantPermission has returned
107 // true for the PermissionType.
108 ContentSetting
GetContentSettingForConstantPermission(PermissionType type
) {
109 DCHECK(IsConstantPermission(type
));
111 case PermissionType::MIDI
:
112 return CONTENT_SETTING_ALLOW
;
114 return CONTENT_SETTING_DEFAULT
;
118 PermissionStatus
GetPermissionStatusForConstantPermission(PermissionType type
) {
119 return ContentSettingToPermissionStatus(
120 GetContentSettingForConstantPermission(type
));
123 } // anonymous namespace
125 struct PermissionManager::Subscription
{
126 PermissionType permission
;
127 GURL requesting_origin
;
128 GURL embedding_origin
;
129 base::Callback
<void(PermissionStatus
)> callback
;
130 ContentSetting current_value
;
133 PermissionManager::PermissionManager(Profile
* profile
)
134 : profile_(profile
) {
137 PermissionManager::~PermissionManager() {
138 if (!subscriptions_
.IsEmpty())
139 HostContentSettingsMapFactory::GetForProfile(profile_
)
140 ->RemoveObserver(this);
143 void PermissionManager::RequestPermission(
144 PermissionType permission
,
145 content::RenderFrameHost
* render_frame_host
,
147 const GURL
& requesting_origin
,
149 const base::Callback
<void(PermissionStatus
)>& callback
) {
150 if (IsConstantPermission(permission
)) {
151 callback
.Run(GetPermissionStatusForConstantPermission(permission
));
155 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
157 callback
.Run(content::PERMISSION_STATUS_DENIED
);
161 content::WebContents
* web_contents
=
162 content::WebContents::FromRenderFrameHost(render_frame_host
);
163 if (IsPermissionBubbleManagerMissing(web_contents
)) {
165 GetPermissionStatus(permission
, requesting_origin
,
166 web_contents
->GetLastCommittedURL().GetOrigin()));
170 int render_process_id
= render_frame_host
->GetProcess()->GetID();
171 int render_frame_id
= render_frame_host
->GetRoutingID();
172 const PermissionRequestID
request(render_process_id
,
176 context
->RequestPermission(
177 web_contents
, request
, requesting_origin
, user_gesture
,
178 base::Bind(&PermissionStatusCallbackWrapper
, callback
));
181 void PermissionManager::CancelPermissionRequest(
182 PermissionType permission
,
183 content::RenderFrameHost
* render_frame_host
,
185 const GURL
& requesting_origin
) {
186 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
190 content::WebContents
* web_contents
=
191 content::WebContents::FromRenderFrameHost(render_frame_host
);
192 if (IsPermissionBubbleManagerMissing(web_contents
))
195 int render_process_id
= render_frame_host
->GetProcess()->GetID();
196 int render_frame_id
= render_frame_host
->GetRoutingID();
197 const PermissionRequestID
request(render_process_id
,
201 context
->CancelPermissionRequest(web_contents
, request
);
204 void PermissionManager::ResetPermission(PermissionType permission
,
205 const GURL
& requesting_origin
,
206 const GURL
& embedding_origin
) {
207 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
211 context
->ResetPermission(requesting_origin
.GetOrigin(),
212 embedding_origin
.GetOrigin());
215 PermissionStatus
PermissionManager::GetPermissionStatus(
216 PermissionType permission
,
217 const GURL
& requesting_origin
,
218 const GURL
& embedding_origin
) {
219 if (IsConstantPermission(permission
))
220 return GetPermissionStatusForConstantPermission(permission
);
222 PermissionContextBase
* context
= PermissionContext::Get(profile_
, permission
);
224 return content::PERMISSION_STATUS_DENIED
;
226 return ContentSettingToPermissionStatus(context
->GetPermissionStatus(
227 requesting_origin
.GetOrigin(), embedding_origin
.GetOrigin()));
230 void PermissionManager::RegisterPermissionUsage(PermissionType permission
,
231 const GURL
& requesting_origin
,
232 const GURL
& embedding_origin
) {
233 // This is required because constant permissions don't have a
234 // ContentSettingsType.
235 if (IsConstantPermission(permission
))
238 HostContentSettingsMapFactory::GetForProfile(profile_
)->UpdateLastUsage(
241 PermissionTypeToContentSetting(permission
));
244 int PermissionManager::SubscribePermissionStatusChange(
245 PermissionType permission
,
246 const GURL
& requesting_origin
,
247 const GURL
& embedding_origin
,
248 const base::Callback
<void(PermissionStatus
)>& callback
) {
249 if (subscriptions_
.IsEmpty())
250 HostContentSettingsMapFactory::GetForProfile(profile_
)->AddObserver(this);
252 Subscription
* subscription
= new Subscription();
253 subscription
->permission
= permission
;
254 subscription
->requesting_origin
= requesting_origin
;
255 subscription
->embedding_origin
= embedding_origin
;
256 subscription
->callback
= callback
;
258 if (IsConstantPermission(permission
)) {
259 subscription
->current_value
= GetContentSettingForConstantPermission(
262 subscription
->current_value
= PermissionContext::Get(profile_
, permission
)
263 ->GetPermissionStatus(subscription
->requesting_origin
,
264 subscription
->embedding_origin
);
267 return subscriptions_
.Add(subscription
);
270 void PermissionManager::UnsubscribePermissionStatusChange(int subscription_id
) {
271 // Whether |subscription_id| is known will be checked by the Remove() call.
272 subscriptions_
.Remove(subscription_id
);
274 if (subscriptions_
.IsEmpty())
275 HostContentSettingsMapFactory::GetForProfile(profile_
)
276 ->RemoveObserver(this);
279 bool PermissionManager::IsPermissionBubbleManagerMissing(
280 content::WebContents
* web_contents
) {
281 // Can't be missing if it isn't needed to begin with.
282 if (!PermissionBubbleManager::Enabled())
285 return PermissionBubbleManager::FromWebContents(web_contents
) == nullptr;
288 void PermissionManager::OnContentSettingChanged(
289 const ContentSettingsPattern
& primary_pattern
,
290 const ContentSettingsPattern
& secondary_pattern
,
291 ContentSettingsType content_type
,
292 std::string resource_identifier
) {
293 std::list
<base::Closure
> callbacks
;
295 for (SubscriptionsMap::iterator
iter(&subscriptions_
);
296 !iter
.IsAtEnd(); iter
.Advance()) {
297 Subscription
* subscription
= iter
.GetCurrentValue();
298 if (PermissionTypeToContentSetting(subscription
->permission
) !=
303 if (primary_pattern
.IsValid() &&
304 !primary_pattern
.Matches(subscription
->requesting_origin
))
306 if (secondary_pattern
.IsValid() &&
307 !secondary_pattern
.Matches(subscription
->embedding_origin
))
310 ContentSetting new_value
=
311 PermissionContext::Get(profile_
, subscription
->permission
)
312 ->GetPermissionStatus(subscription
->requesting_origin
,
313 subscription
->embedding_origin
);
314 if (subscription
->current_value
== new_value
)
317 subscription
->current_value
= new_value
;
319 // Add the callback to |callbacks| which will be run after the loop to
320 // prevent re-entrance issues.
322 base::Bind(subscription
->callback
,
323 ContentSettingToPermissionStatus(new_value
)));
326 for (const auto& callback
: callbacks
)