Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / permissions / permission_manager.cc
blob6aa0728dc9bd125e6a6276451fee58a22201bb8a
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;
23 namespace {
25 // Helper method to convert ContentSetting to PermissionStatus.
26 PermissionStatus ContentSettingToPermissionStatus(ContentSetting setting) {
27 switch (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:
38 break;
41 NOTREACHED();
42 return content::PERMISSION_STATUS_DENIED;
45 // Helper method to convert PermissionType to ContentSettingType.
46 ContentSettingsType PermissionTypeToContentSetting(PermissionType permission) {
47 switch (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;
59 #else
60 NOTIMPLEMENTED();
61 break;
62 #endif
63 case PermissionType::DURABLE_STORAGE:
64 return CONTENT_SETTINGS_TYPE_DURABLE_STORAGE;
65 case PermissionType::MIDI:
66 // This will hit the NOTREACHED below.
67 break;
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.
74 break;
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) {
95 switch (type) {
96 case PermissionType::MIDI:
97 return true;
98 default:
99 return false;
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));
110 switch (type) {
111 case PermissionType::MIDI:
112 return CONTENT_SETTING_ALLOW;
113 default:
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,
146 int request_id,
147 const GURL& requesting_origin,
148 bool user_gesture,
149 const base::Callback<void(PermissionStatus)>& callback) {
150 if (IsConstantPermission(permission)) {
151 callback.Run(GetPermissionStatusForConstantPermission(permission));
152 return;
155 PermissionContextBase* context = PermissionContext::Get(profile_, permission);
156 if (!context) {
157 callback.Run(content::PERMISSION_STATUS_DENIED);
158 return;
161 content::WebContents* web_contents =
162 content::WebContents::FromRenderFrameHost(render_frame_host);
163 if (IsPermissionBubbleManagerMissing(web_contents)) {
164 callback.Run(
165 GetPermissionStatus(permission, requesting_origin,
166 web_contents->GetLastCommittedURL().GetOrigin()));
167 return;
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,
173 render_frame_id,
174 request_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,
184 int request_id,
185 const GURL& requesting_origin) {
186 PermissionContextBase* context = PermissionContext::Get(profile_, permission);
187 if (!context)
188 return;
190 content::WebContents* web_contents =
191 content::WebContents::FromRenderFrameHost(render_frame_host);
192 if (IsPermissionBubbleManagerMissing(web_contents))
193 return;
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,
198 render_frame_id,
199 request_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);
208 if (!context)
209 return;
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);
223 if (!context)
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))
236 return;
238 HostContentSettingsMapFactory::GetForProfile(profile_)->UpdateLastUsage(
239 requesting_origin,
240 embedding_origin,
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(
260 permission);
261 } else {
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())
283 return false;
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) !=
299 content_type) {
300 continue;
303 if (primary_pattern.IsValid() &&
304 !primary_pattern.Matches(subscription->requesting_origin))
305 continue;
306 if (secondary_pattern.IsValid() &&
307 !secondary_pattern.Matches(subscription->embedding_origin))
308 continue;
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)
315 continue;
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.
321 callbacks.push_back(
322 base::Bind(subscription->callback,
323 ContentSettingToPermissionStatus(new_value)));
326 for (const auto& callback : callbacks)
327 callback.Run();