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_manager.h"
26 #include "components/permissions/permission_uma_util.h"
27 #include "components/permissions/permission_util.h"
28 #include "components/permissions/permissions_client.h"
29 #include "components/permissions/request_type.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 "services/network/public/cpp/is_potentially_trustworthy.h"
38 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
41 namespace permissions {
44 const char kPermissionBlockedKillSwitchMessage[] =
45 "%s permission has been blocked.";
47 #if defined(OS_ANDROID)
48 const char kPermissionBlockedRepeatedDismissalsMessage[] =
49 "%s permission has been blocked as the user has dismissed the permission "
50 "prompt several times. This can be reset in Site Settings. See "
51 "https://www.chromestatus.com/features/6443143280984064 for more "
54 const char kPermissionBlockedRepeatedIgnoresMessage[] =
55 "%s permission has been blocked as the user has ignored the permission "
56 "prompt several times. This can be reset in Site Settings. See "
57 "https://www.chromestatus.com/features/6443143280984064 for more "
60 const char kPermissionBlockedRepeatedDismissalsMessage[] =
61 "%s permission has been blocked as the user has dismissed the permission "
62 "prompt several times. This can be reset in Page Info which can be "
63 "accessed by clicking the lock icon next to the URL. See "
64 "https://www.chromestatus.com/features/6443143280984064 for more "
67 const char kPermissionBlockedRepeatedIgnoresMessage[] =
68 "%s permission has been blocked as the user has ignored the permission "
69 "prompt several times. This can be reset in Page Info which can be "
70 "accessed by clicking the lock icon next to the URL. See "
71 "https://www.chromestatus.com/features/6443143280984064 for more "
75 const char kPermissionBlockedPermissionsPolicyMessage[] =
76 "%s permission has been blocked because of a permissions policy applied to"
77 " the current document. See https://goo.gl/EuHzyv for more details.";
79 const char kPermissionBlockedPortalsMessage[] =
80 "%s permission has been blocked because it was requested inside a portal. "
81 "Portals don't currently support permission requests.";
83 void LogPermissionBlockedMessage(content::WebContents* web_contents,
85 ContentSettingsType type) {
86 web_contents->GetMainFrame()->AddMessageToConsole(
87 blink::mojom::ConsoleMessageLevel::kWarning,
88 base::StringPrintf(message,
89 PermissionUtil::GetPermissionString(type).c_str()));
95 const char PermissionContextBase::kPermissionsKillSwitchFieldStudy[] =
96 "PermissionsKillSwitch";
98 const char PermissionContextBase::kPermissionsKillSwitchBlockedValue[] =
101 PermissionContextBase::PermissionContextBase(
102 content::BrowserContext* browser_context,
103 ContentSettingsType content_settings_type,
104 blink::mojom::PermissionsPolicyFeature permissions_policy_feature)
105 : browser_context_(browser_context),
106 content_settings_type_(content_settings_type),
107 permissions_policy_feature_(permissions_policy_feature) {
108 PermissionDecisionAutoBlocker::UpdateFromVariations();
111 PermissionContextBase::~PermissionContextBase() {
112 DCHECK(permission_observers_.empty());
113 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
116 void PermissionContextBase::RequestPermission(
117 content::WebContents* web_contents,
118 const PermissionRequestID& id,
119 const GURL& requesting_frame,
121 BrowserPermissionCallback callback) {
122 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
124 GURL requesting_origin = requesting_frame.GetOrigin();
125 GURL embedding_origin =
126 PermissionUtil::GetLastCommittedOriginAsURL(web_contents);
128 if (!requesting_origin.is_valid() || !embedding_origin.is_valid()) {
129 std::string type_name =
130 PermissionUtil::GetPermissionString(content_settings_type_);
132 DVLOG(1) << "Attempt to use " << type_name
133 << " from an invalid URL: " << requesting_origin << ","
134 << embedding_origin << " (" << type_name
135 << " is not supported in popups)";
136 NotifyPermissionSet(id, requesting_origin, embedding_origin,
137 std::move(callback), /*persist=*/false,
138 CONTENT_SETTING_BLOCK, /*is_one_time=*/false);
142 // Check the content setting to see if the user has already made a decision,
143 // or if the origin is under embargo. If so, respect that decision.
144 content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
145 id.render_process_id(), id.render_frame_id());
146 PermissionResult result =
147 GetPermissionStatus(rfh, requesting_origin, embedding_origin);
149 if (result.content_setting == CONTENT_SETTING_ALLOW ||
150 result.content_setting == CONTENT_SETTING_BLOCK) {
151 switch (result.source) {
152 case PermissionStatusSource::KILL_SWITCH:
153 // Block the request and log to the developer console.
154 LogPermissionBlockedMessage(web_contents,
155 kPermissionBlockedKillSwitchMessage,
156 content_settings_type_);
157 std::move(callback).Run(CONTENT_SETTING_BLOCK);
159 case PermissionStatusSource::MULTIPLE_DISMISSALS:
160 LogPermissionBlockedMessage(web_contents,
161 kPermissionBlockedRepeatedDismissalsMessage,
162 content_settings_type_);
164 case PermissionStatusSource::MULTIPLE_IGNORES:
165 LogPermissionBlockedMessage(web_contents,
166 kPermissionBlockedRepeatedIgnoresMessage,
167 content_settings_type_);
169 case PermissionStatusSource::FEATURE_POLICY:
170 LogPermissionBlockedMessage(web_contents,
171 kPermissionBlockedPermissionsPolicyMessage,
172 content_settings_type_);
174 case PermissionStatusSource::PORTAL:
175 LogPermissionBlockedMessage(web_contents,
176 kPermissionBlockedPortalsMessage,
177 content_settings_type_);
179 case PermissionStatusSource::INSECURE_ORIGIN:
180 case PermissionStatusSource::UNSPECIFIED:
181 case PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN:
185 // If we are under embargo, record the embargo reason for which we have
186 // suppressed the prompt.
187 PermissionUmaUtil::RecordEmbargoPromptSuppressionFromSource(result.source);
188 NotifyPermissionSet(id, requesting_origin, embedding_origin,
189 std::move(callback), /*persist=*/false,
190 result.content_setting, /*is_one_time=*/false);
194 // Don't show request permission UI for an inactive RenderFrameHost as the
195 // page might not distinguish properly between user denying the permission and
196 // automatic rejection, leading to an inconsistent UX once the page becomes
198 // - If this is called when RenderFrameHost is in BackForwardCache, evict the
199 // document from the cache.
200 // - If this is called when RenderFrameHost is in prerendering, cancel
202 if (rfh->IsInactiveAndDisallowActivation()) {
203 std::move(callback).Run(result.content_setting);
207 // We are going to show a prompt now.
208 PermissionUmaUtil::PermissionRequested(content_settings_type_,
210 PermissionUmaUtil::RecordEmbargoPromptSuppression(
211 PermissionEmbargoStatus::NOT_EMBARGOED);
213 DecidePermission(web_contents, id, requesting_origin, embedding_origin,
214 user_gesture, std::move(callback));
217 void PermissionContextBase::UserMadePermissionDecision(
218 const PermissionRequestID& id,
219 const GURL& requesting_origin,
220 const GURL& embedding_origin,
221 ContentSetting content_setting) {}
223 std::unique_ptr<PermissionRequest>
224 PermissionContextBase::CreatePermissionRequest(
225 const GURL& request_origin,
226 ContentSettingsType content_settings_type,
228 content::WebContents* web_contents,
229 PermissionRequest::PermissionDecidedCallback permission_decided_callback,
230 base::OnceClosure delete_callback) const {
231 return std::make_unique<PermissionRequest>(
232 request_origin, ContentSettingsTypeToRequestType(content_settings_type),
233 has_gesture, std::move(permission_decided_callback),
234 std::move(delete_callback));
237 PermissionResult PermissionContextBase::GetPermissionStatus(
238 content::RenderFrameHost* render_frame_host,
239 const GURL& requesting_origin,
240 const GURL& embedding_origin) const {
241 // If the permission has been disabled through Finch, block all requests.
242 if (IsPermissionKillSwitchOn()) {
243 return PermissionResult(CONTENT_SETTING_BLOCK,
244 PermissionStatusSource::KILL_SWITCH);
247 if (!IsPermissionAvailableToOrigins(requesting_origin, embedding_origin)) {
248 return PermissionResult(CONTENT_SETTING_BLOCK,
249 PermissionStatusSource::INSECURE_ORIGIN);
252 // Check whether the feature is enabled for the frame by permissions policy.
253 // We can only do this when a RenderFrameHost has been provided.
254 if (render_frame_host &&
255 !PermissionAllowedByPermissionsPolicy(render_frame_host)) {
256 return PermissionResult(CONTENT_SETTING_BLOCK,
257 PermissionStatusSource::FEATURE_POLICY);
260 if (render_frame_host) {
261 content::WebContents* web_contents =
262 content::WebContents::FromRenderFrameHost(render_frame_host);
264 // Permissions are denied for portals.
265 if (web_contents && web_contents->IsPortal()) {
266 return PermissionResult(CONTENT_SETTING_BLOCK,
267 PermissionStatusSource::PORTAL);
270 // Automatically deny all HTTP or HTTPS requests where the virtual URL and
271 // the loaded URL are for different origins. The loaded URL is the one
272 // actually in the renderer, but the virtual URL is the one
273 // seen by the user. This may be very confusing for a user to see in a
274 // permissions request.
275 content::NavigationEntry* entry =
276 web_contents->GetController().GetLastCommittedEntry();
278 const GURL virtual_url = entry->GetVirtualURL();
279 const GURL loaded_url = entry->GetURL();
280 if (virtual_url.SchemeIsHTTPOrHTTPS() &&
281 loaded_url.SchemeIsHTTPOrHTTPS() &&
282 !url::Origin::Create(virtual_url)
283 .IsSameOriginWith(url::Origin::Create(loaded_url))) {
284 return PermissionResult(
285 CONTENT_SETTING_BLOCK,
286 PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN);
291 ContentSetting content_setting = GetPermissionStatusInternal(
292 render_frame_host, requesting_origin, embedding_origin);
294 if (content_setting != CONTENT_SETTING_ASK) {
295 return PermissionResult(content_setting,
296 PermissionStatusSource::UNSPECIFIED);
299 PermissionResult result =
300 PermissionsClient::Get()
301 ->GetPermissionDecisionAutoBlocker(browser_context_)
302 ->GetEmbargoResult(requesting_origin, content_settings_type_);
303 DCHECK(result.content_setting == CONTENT_SETTING_ASK ||
304 result.content_setting == CONTENT_SETTING_BLOCK);
308 bool PermissionContextBase::IsPermissionAvailableToOrigins(
309 const GURL& requesting_origin,
310 const GURL& embedding_origin) const {
311 if (IsRestrictedToSecureOrigins()) {
312 if (!network::IsUrlPotentiallyTrustworthy(requesting_origin))
315 // TODO(raymes): We should check the entire chain of embedders here whenever
316 // possible as this corresponds to the requirements of the secure contexts
317 // spec and matches what is implemented in blink. Right now we just check
318 // the top level and requesting origins.
319 if (!PermissionsClient::Get()->CanBypassEmbeddingOriginCheck(
320 requesting_origin, embedding_origin) &&
321 !network::IsUrlPotentiallyTrustworthy(embedding_origin)) {
328 PermissionResult PermissionContextBase::UpdatePermissionStatusWithDeviceStatus(
329 PermissionResult result,
330 const GURL& requesting_origin,
331 const GURL& embedding_origin) const {
335 void PermissionContextBase::ResetPermission(const GURL& requesting_origin,
336 const GURL& embedding_origin) {
337 if (!content_settings::ContentSettingsRegistry::GetInstance()->Get(
338 content_settings_type_)) {
341 PermissionsClient::Get()
342 ->GetSettingsMap(browser_context_)
343 ->SetContentSettingDefaultScope(requesting_origin, embedding_origin,
344 content_settings_type_,
345 CONTENT_SETTING_DEFAULT);
348 bool PermissionContextBase::IsPermissionKillSwitchOn() const {
349 const std::string param = base::GetFieldTrialParamValue(
350 kPermissionsKillSwitchFieldStudy,
351 PermissionUtil::GetPermissionString(content_settings_type_));
353 return param == kPermissionsKillSwitchBlockedValue;
356 ContentSetting PermissionContextBase::GetPermissionStatusInternal(
357 content::RenderFrameHost* render_frame_host,
358 const GURL& requesting_origin,
359 const GURL& embedding_origin) const {
360 return PermissionsClient::Get()
361 ->GetSettingsMap(browser_context_)
362 ->GetContentSetting(requesting_origin, embedding_origin,
363 content_settings_type_);
366 void PermissionContextBase::DecidePermission(
367 content::WebContents* web_contents,
368 const PermissionRequestID& id,
369 const GURL& requesting_origin,
370 const GURL& embedding_origin,
372 BrowserPermissionCallback callback) {
373 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
375 // Under permission delegation, when we display a permission prompt, the
376 // origin displayed in the prompt should never differ from the top-level
377 // origin. Storage access API requests are excluded as they are expected to
378 // request permissions from the frame origin needing access.
379 DCHECK(PermissionsClient::Get()->CanBypassEmbeddingOriginCheck(
380 requesting_origin, embedding_origin) ||
381 requesting_origin == embedding_origin ||
382 content_settings_type_ == ContentSettingsType::STORAGE_ACCESS);
384 PermissionRequestManager* permission_request_manager =
385 PermissionRequestManager::FromWebContents(web_contents);
386 // TODO(felt): sometimes |permission_request_manager| is null. This check is
387 // meant to prevent crashes. See crbug.com/457091.
388 if (!permission_request_manager)
391 std::unique_ptr<PermissionRequest> request_ptr = CreatePermissionRequest(
392 requesting_origin, content_settings_type_, user_gesture, web_contents,
393 base::BindOnce(&PermissionContextBase::PermissionDecided,
394 weak_factory_.GetWeakPtr(), id, requesting_origin,
395 embedding_origin, std::move(callback)),
396 base::BindOnce(&PermissionContextBase::CleanUpRequest,
397 weak_factory_.GetWeakPtr(), id));
398 PermissionRequest* request = request_ptr.get();
402 .insert(std::make_pair(id.ToString(), std::move(request_ptr)))
404 DCHECK(inserted) << "Duplicate id " << id.ToString();
406 content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
407 id.render_process_id(), id.render_frame_id());
410 request->Cancelled();
411 request->RequestFinished();
415 permission_request_manager->AddRequest(rfh, request);
418 void PermissionContextBase::PermissionDecided(
419 const PermissionRequestID& id,
420 const GURL& requesting_origin,
421 const GURL& embedding_origin,
422 BrowserPermissionCallback callback,
423 ContentSetting content_setting,
425 DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
426 content_setting == CONTENT_SETTING_BLOCK ||
427 content_setting == CONTENT_SETTING_DEFAULT);
428 UserMadePermissionDecision(id, requesting_origin, embedding_origin,
431 bool persist = content_setting != CONTENT_SETTING_DEFAULT;
432 NotifyPermissionSet(id, requesting_origin, embedding_origin,
433 std::move(callback), persist, content_setting,
437 content::BrowserContext* PermissionContextBase::browser_context() const {
438 return browser_context_;
441 void PermissionContextBase::OnContentSettingChanged(
442 const ContentSettingsPattern& primary_pattern,
443 const ContentSettingsPattern& secondary_pattern,
444 ContentSettingsType content_type) {
445 if (content_type != content_settings_type_)
448 for (permissions::Observer& obs : permission_observers_)
449 obs.OnPermissionChanged(primary_pattern, secondary_pattern, content_type);
452 void PermissionContextBase::AddObserver(
453 permissions::Observer* permission_observer) {
454 if (permission_observers_.empty() &&
455 !content_setting_observer_registered_by_subclass_) {
456 PermissionsClient::Get()
457 ->GetSettingsMap(browser_context_)
460 permission_observers_.AddObserver(permission_observer);
463 void PermissionContextBase::RemoveObserver(
464 permissions::Observer* permission_observer) {
465 permission_observers_.RemoveObserver(permission_observer);
466 if (permission_observers_.empty() &&
467 !content_setting_observer_registered_by_subclass_) {
468 PermissionsClient::Get()
469 ->GetSettingsMap(browser_context_)
470 ->RemoveObserver(this);
474 void PermissionContextBase::NotifyPermissionSet(
475 const PermissionRequestID& id,
476 const GURL& requesting_origin,
477 const GURL& embedding_origin,
478 BrowserPermissionCallback callback,
480 ContentSetting content_setting,
482 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
485 UpdateContentSetting(requesting_origin, embedding_origin, content_setting,
489 UpdateTabContext(id, requesting_origin,
490 content_setting == CONTENT_SETTING_ALLOW);
492 if (content_setting == CONTENT_SETTING_DEFAULT)
493 content_setting = CONTENT_SETTING_ASK;
495 std::move(callback).Run(content_setting);
498 void PermissionContextBase::CleanUpRequest(const PermissionRequestID& id) {
499 size_t success = pending_requests_.erase(id.ToString());
500 DCHECK(success == 1) << "Missing request " << id.ToString();
503 void PermissionContextBase::UpdateContentSetting(const GURL& requesting_origin,
504 const GURL& embedding_origin,
505 ContentSetting content_setting,
507 DCHECK_EQ(requesting_origin, requesting_origin.GetOrigin());
508 DCHECK_EQ(embedding_origin, embedding_origin.GetOrigin());
509 DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
510 content_setting == CONTENT_SETTING_BLOCK);
512 using Constraints = content_settings::ContentSettingConstraints;
513 PermissionsClient::Get()
514 ->GetSettingsMap(browser_context_)
515 ->SetContentSettingDefaultScope(
516 requesting_origin, embedding_origin, content_settings_type_,
518 is_one_time ? Constraints{base::Time(),
519 content_settings::SessionModel::OneTime}
523 bool PermissionContextBase::PermissionAllowedByPermissionsPolicy(
524 content::RenderFrameHost* rfh) const {
525 // Some features don't have an associated permissions policy yet. Allow those.
526 if (permissions_policy_feature_ ==
527 blink::mojom::PermissionsPolicyFeature::kNotFound)
530 return rfh->IsFeatureEnabled(permissions_policy_feature_);
533 } // namespace permissions