Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / permissions / permission_manager.cc
blobf5564e6ec45908ef4f39e89e80cbf380f6391e97
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;
22 namespace {
24 // Helper method to convert ContentSetting to PermissionStatus.
25 PermissionStatus ContentSettingToPermissionStatus(ContentSetting setting) {
26 switch (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:
37 break;
40 NOTREACHED();
41 return content::PERMISSION_STATUS_DENIED;
44 // Helper method to convert PermissionType to ContentSettingType.
45 ContentSettingsType PermissionTypeToContentSetting(PermissionType permission) {
46 switch (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;
58 #else
59 NOTIMPLEMENTED();
60 break;
61 #endif
62 case PermissionType::DURABLE_STORAGE:
63 return CONTENT_SETTINGS_TYPE_DURABLE_STORAGE;
64 case PermissionType::MIDI:
65 // This will hit the NOTREACHED below.
66 break;
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.
73 break;
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) {
94 switch (type) {
95 case PermissionType::MIDI:
96 return true;
97 default:
98 return false;
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));
109 switch (type) {
110 case PermissionType::MIDI:
111 return CONTENT_SETTING_ALLOW;
112 default:
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,
144 int request_id,
145 const GURL& requesting_origin,
146 bool user_gesture,
147 const base::Callback<void(PermissionStatus)>& callback) {
148 if (IsConstantPermission(permission)) {
149 callback.Run(GetPermissionStatusForConstantPermission(permission));
150 return;
153 PermissionContextBase* context = PermissionContext::Get(profile_, permission);
154 if (!context) {
155 callback.Run(content::PERMISSION_STATUS_DENIED);
156 return;
159 content::WebContents* web_contents =
160 content::WebContents::FromRenderFrameHost(render_frame_host);
161 if (IsPermissionBubbleManagerMissing(web_contents)) {
162 callback.Run(
163 GetPermissionStatus(permission, requesting_origin,
164 web_contents->GetLastCommittedURL().GetOrigin()));
165 return;
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,
171 render_frame_id,
172 request_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,
182 int request_id,
183 const GURL& requesting_origin) {
184 PermissionContextBase* context = PermissionContext::Get(profile_, permission);
185 if (!context)
186 return;
188 content::WebContents* web_contents =
189 content::WebContents::FromRenderFrameHost(render_frame_host);
190 if (IsPermissionBubbleManagerMissing(web_contents))
191 return;
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,
196 render_frame_id,
197 request_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);
206 if (!context)
207 return;
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);
221 if (!context)
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))
234 return;
236 profile_->GetHostContentSettingsMap()->UpdateLastUsage(
237 requesting_origin,
238 embedding_origin,
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(
258 permission);
259 } else {
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())
280 return false;
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) !=
296 content_type) {
297 continue;
300 if (primary_pattern.IsValid() &&
301 !primary_pattern.Matches(subscription->requesting_origin))
302 continue;
303 if (secondary_pattern.IsValid() &&
304 !secondary_pattern.Matches(subscription->embedding_origin))
305 continue;
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)
312 continue;
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.
318 callbacks.push_back(
319 base::Bind(subscription->callback,
320 ContentSettingToPermissionStatus(new_value)));
323 for (const auto& callback : callbacks)
324 callback.Run();