1 // Copyright 2014 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 "components/permissions/permission_context_base.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/logging.h"
15 #include "base/metrics/field_trial_params.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/time/time.h"
18 #include "build/build_config.h"
19 #include "components/content_settings/core/browser/content_settings_registry.h"
20 #include "components/content_settings/core/browser/host_content_settings_map.h"
21 #include "components/permissions/features.h"
22 #include "components/permissions/permission_decision_auto_blocker.h"
23 #include "components/permissions/permission_request.h"
24 #include "components/permissions/permission_request_id.h"
25 #include "components/permissions/permission_request_impl.h"
26 #include "components/permissions/permission_request_manager.h"
27 #include "components/permissions/permission_uma_util.h"
28 #include "components/permissions/permission_util.h"
29 #include "components/permissions/permissions_client.h"
30 #include "content/public/browser/back_forward_cache.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/global_routing_id.h"
33 #include "content/public/browser/navigation_entry.h"
34 #include "content/public/browser/render_frame_host.h"
35 #include "content/public/browser/web_contents.h"
36 #include "content/public/common/content_features.h"
37 #include "content/public/common/origin_util.h"
40 namespace permissions {
43 const char kPermissionBlockedKillSwitchMessage[] =
44 "%s permission has been blocked.";
46 #if defined(OS_ANDROID)
47 const char kPermissionBlockedRepeatedDismissalsMessage[] =
48 "%s permission has been blocked as the user has dismissed the permission "
49 "prompt several times. This can be reset in Site Settings. See "
50 "https://www.chromestatus.com/features/6443143280984064 for more "
53 const char kPermissionBlockedRepeatedIgnoresMessage[] =
54 "%s permission has been blocked as the user has ignored the permission "
55 "prompt several times. This can be reset in Site Settings. See "
56 "https://www.chromestatus.com/features/6443143280984064 for more "
59 const char kPermissionBlockedRepeatedDismissalsMessage[] =
60 "%s permission has been blocked as the user has dismissed the permission "
61 "prompt several times. This can be reset in Page Info which can be "
62 "accessed by clicking the lock icon next to the URL. See "
63 "https://www.chromestatus.com/features/6443143280984064 for more "
66 const char kPermissionBlockedRepeatedIgnoresMessage[] =
67 "%s permission has been blocked as the user has ignored the permission "
68 "prompt several times. This can be reset in Page Info which can be "
69 "accessed by clicking the lock icon next to the URL. See "
70 "https://www.chromestatus.com/features/6443143280984064 for more "
74 const char kPermissionBlockedFeaturePolicyMessage[] =
75 "%s permission has been blocked because of a Feature Policy applied to the "
76 "current document. See https://goo.gl/EuHzyv for more details.";
78 const char kPermissionBlockedPortalsMessage[] =
79 "%s permission has been blocked because it was requested inside a portal. "
80 "Portals don't currently support permission requests.";
82 void LogPermissionBlockedMessage(content::WebContents* web_contents,
84 ContentSettingsType type) {
85 web_contents->GetMainFrame()->AddMessageToConsole(
86 blink::mojom::ConsoleMessageLevel::kWarning,
87 base::StringPrintf(message,
88 PermissionUtil::GetPermissionString(type).c_str()));
94 const char PermissionContextBase::kPermissionsKillSwitchFieldStudy[] =
95 "PermissionsKillSwitch";
97 const char PermissionContextBase::kPermissionsKillSwitchBlockedValue[] =
100 PermissionContextBase::PermissionContextBase(
101 content::BrowserContext* browser_context,
102 ContentSettingsType content_settings_type,
103 blink::mojom::FeaturePolicyFeature feature_policy_feature)
104 : browser_context_(browser_context),
105 content_settings_type_(content_settings_type),
106 feature_policy_feature_(feature_policy_feature) {
107 PermissionDecisionAutoBlocker::UpdateFromVariations();
110 PermissionContextBase::~PermissionContextBase() {
111 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
114 void PermissionContextBase::RequestPermission(
115 content::WebContents* web_contents,
116 const PermissionRequestID& id,
117 const GURL& requesting_frame,
119 BrowserPermissionCallback callback) {
120 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
122 GURL requesting_origin = requesting_frame.GetOrigin();
123 GURL embedding_origin = web_contents->GetLastCommittedURL().GetOrigin();
125 if (!requesting_origin.is_valid() || !embedding_origin.is_valid()) {
126 std::string type_name =
127 PermissionUtil::GetPermissionString(content_settings_type_);
129 DVLOG(1) << "Attempt to use " << type_name
130 << " from an invalid URL: " << requesting_origin << ","
131 << embedding_origin << " (" << type_name
132 << " is not supported in popups)";
133 NotifyPermissionSet(id, requesting_origin, embedding_origin,
134 std::move(callback), false /* persist */,
135 CONTENT_SETTING_BLOCK);
139 // Check the content setting to see if the user has already made a decision,
140 // or if the origin is under embargo. If so, respect that decision.
141 content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
142 id.render_process_id(), id.render_frame_id());
143 PermissionResult result =
144 GetPermissionStatus(rfh, requesting_origin, embedding_origin);
146 if (result.content_setting == CONTENT_SETTING_ALLOW ||
147 result.content_setting == CONTENT_SETTING_BLOCK) {
148 switch (result.source) {
149 case PermissionStatusSource::KILL_SWITCH:
150 // Block the request and log to the developer console.
151 LogPermissionBlockedMessage(web_contents,
152 kPermissionBlockedKillSwitchMessage,
153 content_settings_type_);
154 std::move(callback).Run(CONTENT_SETTING_BLOCK);
156 case PermissionStatusSource::MULTIPLE_DISMISSALS:
157 LogPermissionBlockedMessage(web_contents,
158 kPermissionBlockedRepeatedDismissalsMessage,
159 content_settings_type_);
161 case PermissionStatusSource::MULTIPLE_IGNORES:
162 LogPermissionBlockedMessage(web_contents,
163 kPermissionBlockedRepeatedIgnoresMessage,
164 content_settings_type_);
166 case PermissionStatusSource::FEATURE_POLICY:
167 LogPermissionBlockedMessage(web_contents,
168 kPermissionBlockedFeaturePolicyMessage,
169 content_settings_type_);
171 case PermissionStatusSource::PORTAL:
172 LogPermissionBlockedMessage(web_contents,
173 kPermissionBlockedPortalsMessage,
174 content_settings_type_);
176 case PermissionStatusSource::INSECURE_ORIGIN:
177 case PermissionStatusSource::UNSPECIFIED:
178 case PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN:
182 // If we are under embargo, record the embargo reason for which we have
183 // suppressed the prompt.
184 PermissionUmaUtil::RecordEmbargoPromptSuppressionFromSource(result.source);
185 NotifyPermissionSet(id, requesting_origin, embedding_origin,
186 std::move(callback), false /* persist */,
187 result.content_setting);
191 // Make sure we do not show a UI for cached documents
192 if (content::BackForwardCache::EvictIfCached(
193 content::GlobalFrameRoutingId(id.render_process_id(),
194 id.render_frame_id()),
195 "PermissionContextBase::RequestPermission")) {
196 std::move(callback).Run(result.content_setting);
200 // We are going to show a prompt now.
201 PermissionUmaUtil::PermissionRequested(content_settings_type_,
203 PermissionUmaUtil::RecordEmbargoPromptSuppression(
204 PermissionEmbargoStatus::NOT_EMBARGOED);
206 DecidePermission(web_contents, id, requesting_origin, embedding_origin,
207 user_gesture, std::move(callback));
210 void PermissionContextBase::UserMadePermissionDecision(
211 const PermissionRequestID& id,
212 const GURL& requesting_origin,
213 const GURL& embedding_origin,
214 ContentSetting content_setting) {}
216 PermissionResult PermissionContextBase::GetPermissionStatus(
217 content::RenderFrameHost* render_frame_host,
218 const GURL& requesting_origin,
219 const GURL& embedding_origin) const {
220 // If the permission has been disabled through Finch, block all requests.
221 if (IsPermissionKillSwitchOn()) {
222 return PermissionResult(CONTENT_SETTING_BLOCK,
223 PermissionStatusSource::KILL_SWITCH);
226 if (!IsPermissionAvailableToOrigins(requesting_origin, embedding_origin)) {
227 return PermissionResult(CONTENT_SETTING_BLOCK,
228 PermissionStatusSource::INSECURE_ORIGIN);
231 // Check whether the feature is enabled for the frame by feature policy. We
232 // can only do this when a RenderFrameHost has been provided.
233 if (render_frame_host &&
234 !PermissionAllowedByFeaturePolicy(render_frame_host)) {
235 return PermissionResult(CONTENT_SETTING_BLOCK,
236 PermissionStatusSource::FEATURE_POLICY);
239 if (render_frame_host) {
240 content::WebContents* web_contents =
241 content::WebContents::FromRenderFrameHost(render_frame_host);
243 // Permissions are denied for portals.
244 if (web_contents && web_contents->IsPortal()) {
245 return PermissionResult(CONTENT_SETTING_BLOCK,
246 PermissionStatusSource::PORTAL);
249 // Automatically deny all HTTP or HTTPS requests where the virtual URL and
250 // the loaded URL are for different origins. The loaded URL is the one
251 // actually in the renderer, but the virtual URL is the one
252 // seen by the user. This may be very confusing for a user to see in a
253 // permissions request.
254 content::NavigationEntry* entry =
255 web_contents->GetController().GetLastCommittedEntry();
257 const GURL virtual_url = entry->GetVirtualURL();
258 const GURL loaded_url = entry->GetURL();
259 if (virtual_url.SchemeIsHTTPOrHTTPS() &&
260 loaded_url.SchemeIsHTTPOrHTTPS() &&
261 !url::Origin::Create(virtual_url)
262 .IsSameOriginWith(url::Origin::Create(loaded_url))) {
263 return PermissionResult(
264 CONTENT_SETTING_BLOCK,
265 PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN);
270 ContentSetting content_setting = GetPermissionStatusInternal(
271 render_frame_host, requesting_origin, embedding_origin);
273 if (content_setting != CONTENT_SETTING_ASK) {
274 return PermissionResult(content_setting,
275 PermissionStatusSource::UNSPECIFIED);
278 PermissionResult result =
279 PermissionsClient::Get()
280 ->GetPermissionDecisionAutoBlocker(browser_context_)
281 ->GetEmbargoResult(requesting_origin, content_settings_type_);
282 DCHECK(result.content_setting == CONTENT_SETTING_ASK ||
283 result.content_setting == CONTENT_SETTING_BLOCK);
287 bool PermissionContextBase::IsPermissionAvailableToOrigins(
288 const GURL& requesting_origin,
289 const GURL& embedding_origin) const {
290 if (IsRestrictedToSecureOrigins()) {
291 if (!content::IsOriginSecure(requesting_origin))
294 // TODO(raymes): We should check the entire chain of embedders here whenever
295 // possible as this corresponds to the requirements of the secure contexts
296 // spec and matches what is implemented in blink. Right now we just check
297 // the top level and requesting origins.
298 if (!PermissionsClient::Get()->CanBypassEmbeddingOriginCheck(
299 requesting_origin, embedding_origin) &&
300 !content::IsOriginSecure(embedding_origin)) {
307 PermissionResult PermissionContextBase::UpdatePermissionStatusWithDeviceStatus(
308 PermissionResult result,
309 const GURL& requesting_origin,
310 const GURL& embedding_origin) const {
314 void PermissionContextBase::ResetPermission(const GURL& requesting_origin,
315 const GURL& embedding_origin) {
316 if (!content_settings::ContentSettingsRegistry::GetInstance()->Get(
317 content_settings_type_)) {
320 PermissionsClient::Get()
321 ->GetSettingsMap(browser_context_)
322 ->SetContentSettingDefaultScope(requesting_origin, embedding_origin,
323 content_settings_type_, std::string(),
324 CONTENT_SETTING_DEFAULT);
327 bool PermissionContextBase::IsPermissionKillSwitchOn() const {
328 const std::string param = base::GetFieldTrialParamValue(
329 kPermissionsKillSwitchFieldStudy,
330 PermissionUtil::GetPermissionString(content_settings_type_));
332 return param == kPermissionsKillSwitchBlockedValue;
335 ContentSetting PermissionContextBase::GetPermissionStatusInternal(
336 content::RenderFrameHost* render_frame_host,
337 const GURL& requesting_origin,
338 const GURL& embedding_origin) const {
339 return PermissionsClient::Get()
340 ->GetSettingsMap(browser_context_)
341 ->GetContentSetting(requesting_origin, embedding_origin,
342 content_settings_type_, std::string());
345 void PermissionContextBase::DecidePermission(
346 content::WebContents* web_contents,
347 const PermissionRequestID& id,
348 const GURL& requesting_origin,
349 const GURL& embedding_origin,
351 BrowserPermissionCallback callback) {
352 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
354 // Under permission delegation, when we display a permission prompt, the
355 // origin displayed in the prompt should never differ from the top-level
356 // origin. Storage access API requests are excluded as they are expected to
357 // request permissions from the frame origin needing access.
358 DCHECK(!base::FeatureList::IsEnabled(features::kPermissionDelegation) ||
359 PermissionsClient::Get()->CanBypassEmbeddingOriginCheck(
360 requesting_origin, embedding_origin) ||
361 requesting_origin == embedding_origin ||
362 content_settings_type_ == ContentSettingsType::STORAGE_ACCESS);
364 PermissionRequestManager* permission_request_manager =
365 PermissionRequestManager::FromWebContents(web_contents);
366 // TODO(felt): sometimes |permission_request_manager| is null. This check is
367 // meant to prevent crashes. See crbug.com/457091.
368 if (!permission_request_manager)
371 std::unique_ptr<PermissionRequest> request_ptr =
372 std::make_unique<PermissionRequestImpl>(
373 embedding_origin, requesting_origin, content_settings_type_,
375 base::BindOnce(&PermissionContextBase::PermissionDecided,
376 weak_factory_.GetWeakPtr(), id, requesting_origin,
377 embedding_origin, std::move(callback)),
378 base::BindOnce(&PermissionContextBase::CleanUpRequest,
379 weak_factory_.GetWeakPtr(), id));
380 PermissionRequest* request = request_ptr.get();
384 .insert(std::make_pair(id.ToString(), std::move(request_ptr)))
386 DCHECK(inserted) << "Duplicate id " << id.ToString();
387 permission_request_manager->AddRequest(request);
390 void PermissionContextBase::PermissionDecided(
391 const PermissionRequestID& id,
392 const GURL& requesting_origin,
393 const GURL& embedding_origin,
394 BrowserPermissionCallback callback,
395 ContentSetting content_setting) {
396 DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
397 content_setting == CONTENT_SETTING_BLOCK ||
398 content_setting == CONTENT_SETTING_DEFAULT);
399 UserMadePermissionDecision(id, requesting_origin, embedding_origin,
402 bool persist = content_setting != CONTENT_SETTING_DEFAULT;
403 NotifyPermissionSet(id, requesting_origin, embedding_origin,
404 std::move(callback), persist, content_setting);
407 content::BrowserContext* PermissionContextBase::browser_context() const {
408 return browser_context_;
411 void PermissionContextBase::NotifyPermissionSet(
412 const PermissionRequestID& id,
413 const GURL& requesting_origin,
414 const GURL& embedding_origin,
415 BrowserPermissionCallback callback,
417 ContentSetting content_setting) {
418 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
421 UpdateContentSetting(requesting_origin, embedding_origin, content_setting);
423 UpdateTabContext(id, requesting_origin,
424 content_setting == CONTENT_SETTING_ALLOW);
426 if (content_setting == CONTENT_SETTING_DEFAULT)
427 content_setting = CONTENT_SETTING_ASK;
429 std::move(callback).Run(content_setting);
432 void PermissionContextBase::CleanUpRequest(const PermissionRequestID& id) {
433 size_t success = pending_requests_.erase(id.ToString());
434 DCHECK(success == 1) << "Missing request " << id.ToString();
437 void PermissionContextBase::UpdateContentSetting(
438 const GURL& requesting_origin,
439 const GURL& embedding_origin,
440 ContentSetting content_setting) {
441 DCHECK_EQ(requesting_origin, requesting_origin.GetOrigin());
442 DCHECK_EQ(embedding_origin, embedding_origin.GetOrigin());
443 DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
444 content_setting == CONTENT_SETTING_BLOCK);
445 DCHECK(!requesting_origin.SchemeIsFile());
446 DCHECK(!embedding_origin.SchemeIsFile());
448 PermissionsClient::Get()
449 ->GetSettingsMap(browser_context_)
450 ->SetContentSettingDefaultScope(requesting_origin, embedding_origin,
451 content_settings_type_, std::string(),
455 bool PermissionContextBase::PermissionAllowedByFeaturePolicy(
456 content::RenderFrameHost* rfh) const {
457 // Some features don't have an associated feature policy yet. Allow those.
458 if (feature_policy_feature_ == blink::mojom::FeaturePolicyFeature::kNotFound)
461 return rfh->IsFeatureEnabled(feature_policy_feature_);
464 } // namespace permissions