1 // Copyright 2014 The Chromium Authors
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/observer_list.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/time/time.h"
19 #include "build/build_config.h"
20 #include "components/content_settings/core/browser/content_settings_registry.h"
21 #include "components/content_settings/core/browser/content_settings_utils.h"
22 #include "components/content_settings/core/browser/host_content_settings_map.h"
23 #include "components/permissions/features.h"
24 #include "components/permissions/permission_decision_auto_blocker.h"
25 #include "components/permissions/permission_request.h"
26 #include "components/permissions/permission_request_id.h"
27 #include "components/permissions/permission_request_manager.h"
28 #include "components/permissions/permission_uma_util.h"
29 #include "components/permissions/permission_util.h"
30 #include "components/permissions/permissions_client.h"
31 #include "components/permissions/request_type.h"
32 #include "components/permissions/unused_site_permissions_service.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/global_routing_id.h"
35 #include "content/public/browser/navigation_entry.h"
36 #include "content/public/browser/render_frame_host.h"
37 #include "content/public/browser/web_contents.h"
38 #include "content/public/common/content_features.h"
39 #include "services/network/public/cpp/is_potentially_trustworthy.h"
40 #include "third_party/blink/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
43 namespace permissions {
46 const char kPermissionBlockedKillSwitchMessage[] =
47 "%s permission has been blocked.";
49 #if BUILDFLAG(IS_ANDROID)
50 const char kPermissionBlockedRepeatedDismissalsMessage[] =
51 "%s permission has been blocked as the user has dismissed the permission "
52 "prompt several times. This can be reset in Site Settings. See "
53 "https://www.chromestatus.com/feature/6443143280984064 for more "
56 const char kPermissionBlockedRepeatedIgnoresMessage[] =
57 "%s permission has been blocked as the user has ignored the permission "
58 "prompt several times. This can be reset in Site Settings. See "
59 "https://www.chromestatus.com/feature/6443143280984064 for more "
62 const char kPermissionBlockedRepeatedDismissalsMessage[] =
63 "%s permission has been blocked as the user has dismissed the permission "
64 "prompt several times. This can be reset in Page Info which can be "
65 "accessed by clicking the lock icon next to the URL. See "
66 "https://www.chromestatus.com/feature/6443143280984064 for more "
69 const char kPermissionBlockedRepeatedIgnoresMessage[] =
70 "%s permission has been blocked as the user has ignored the permission "
71 "prompt several times. This can be reset in Page Info which can be "
72 "accessed by clicking the lock icon next to the URL. See "
73 "https://www.chromestatus.com/feature/6443143280984064 for more "
77 const char kPermissionBlockedPermissionsPolicyMessage[] =
78 "%s permission has been blocked because of a permissions policy applied to"
79 " the current document. See https://goo.gl/EuHzyv for more details.";
81 void LogPermissionBlockedMessage(content::RenderFrameHost* rfh,
83 ContentSettingsType type) {
84 rfh->GetOutermostMainFrame()->AddMessageToConsole(
85 blink::mojom::ConsoleMessageLevel::kWarning,
86 base::StringPrintf(message,
87 PermissionUtil::GetPermissionString(type).c_str()));
93 const char PermissionContextBase::kPermissionsKillSwitchFieldStudy[] =
94 "PermissionsKillSwitch";
96 const char PermissionContextBase::kPermissionsKillSwitchBlockedValue[] =
99 PermissionContextBase::PermissionContextBase(
100 content::BrowserContext* browser_context,
101 ContentSettingsType content_settings_type,
102 blink::mojom::PermissionsPolicyFeature permissions_policy_feature)
103 : browser_context_(browser_context),
104 content_settings_type_(content_settings_type),
105 permissions_policy_feature_(permissions_policy_feature) {
106 PermissionDecisionAutoBlocker::UpdateFromVariations();
109 PermissionContextBase::~PermissionContextBase() {
110 DCHECK(permission_observers_.empty());
111 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
114 void PermissionContextBase::RequestPermission(
115 const PermissionRequestID& id,
116 const GURL& requesting_frame,
118 BrowserPermissionCallback callback) {
119 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
121 content::RenderFrameHost* const rfh = content::RenderFrameHost::FromID(
122 id.render_process_id(), id.render_frame_id());
125 // Permission request is not allowed without a valid RenderFrameHost.
126 std::move(callback).Run(CONTENT_SETTING_ASK);
130 const GURL requesting_origin = requesting_frame.DeprecatedGetOriginAsURL();
131 const GURL embedding_origin =
132 PermissionUtil::GetLastCommittedOriginAsURL(rfh->GetMainFrame());
134 if (!requesting_origin.is_valid() || !embedding_origin.is_valid()) {
135 std::string type_name =
136 PermissionUtil::GetPermissionString(content_settings_type_);
138 DVLOG(1) << "Attempt to use " << type_name
139 << " from an invalid URL: " << requesting_origin << ","
140 << embedding_origin << " (" << type_name
141 << " is not supported in popups)";
142 NotifyPermissionSet(id, requesting_origin, embedding_origin,
143 std::move(callback), /*persist=*/false,
144 CONTENT_SETTING_BLOCK, /*is_one_time=*/false);
148 // Check the content setting to see if the user has already made a decision,
149 // or if the origin is under embargo. If so, respect that decision.
151 PermissionResult result =
152 GetPermissionStatus(rfh, requesting_origin, embedding_origin);
154 if (result.content_setting == CONTENT_SETTING_ALLOW ||
155 result.content_setting == CONTENT_SETTING_BLOCK) {
156 switch (result.source) {
157 case PermissionStatusSource::KILL_SWITCH:
158 // Block the request and log to the developer console.
159 LogPermissionBlockedMessage(rfh, kPermissionBlockedKillSwitchMessage,
160 content_settings_type_);
161 std::move(callback).Run(CONTENT_SETTING_BLOCK);
163 case PermissionStatusSource::MULTIPLE_DISMISSALS:
164 LogPermissionBlockedMessage(rfh,
165 kPermissionBlockedRepeatedDismissalsMessage,
166 content_settings_type_);
168 case PermissionStatusSource::MULTIPLE_IGNORES:
169 LogPermissionBlockedMessage(rfh,
170 kPermissionBlockedRepeatedIgnoresMessage,
171 content_settings_type_);
173 case PermissionStatusSource::FEATURE_POLICY:
174 LogPermissionBlockedMessage(rfh,
175 kPermissionBlockedPermissionsPolicyMessage,
176 content_settings_type_);
178 case PermissionStatusSource::PORTAL:
179 case PermissionStatusSource::FENCED_FRAME:
180 case PermissionStatusSource::INSECURE_ORIGIN:
181 case PermissionStatusSource::UNSPECIFIED:
182 case PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN:
186 // If we are under embargo, record the embargo reason for which we have
187 // suppressed the prompt.
188 PermissionUmaUtil::RecordEmbargoPromptSuppressionFromSource(result.source);
189 NotifyPermissionSet(id, requesting_origin, embedding_origin,
190 std::move(callback), /*persist=*/false,
191 result.content_setting, /*is_one_time=*/false);
195 // We are going to show a prompt now.
196 PermissionUmaUtil::PermissionRequested(content_settings_type_,
198 PermissionUmaUtil::RecordEmbargoPromptSuppression(
199 PermissionEmbargoStatus::NOT_EMBARGOED);
201 DecidePermission(id, requesting_origin, embedding_origin, user_gesture,
202 std::move(callback));
205 bool PermissionContextBase::IsRestrictedToSecureOrigins() const {
209 void PermissionContextBase::UserMadePermissionDecision(
210 const PermissionRequestID& id,
211 const GURL& requesting_origin,
212 const GURL& embedding_origin,
213 ContentSetting content_setting) {}
215 std::unique_ptr<PermissionRequest>
216 PermissionContextBase::CreatePermissionRequest(
217 const GURL& request_origin,
218 ContentSettingsType content_settings_type,
220 content::WebContents* web_contents,
221 PermissionRequest::PermissionDecidedCallback permission_decided_callback,
222 base::OnceClosure delete_callback) const {
223 return std::make_unique<PermissionRequest>(
224 request_origin, ContentSettingsTypeToRequestType(content_settings_type),
225 has_gesture, std::move(permission_decided_callback),
226 std::move(delete_callback));
229 PermissionResult PermissionContextBase::GetPermissionStatus(
230 content::RenderFrameHost* render_frame_host,
231 const GURL& requesting_origin,
232 const GURL& embedding_origin) const {
233 // If the permission has been disabled through Finch, block all requests.
234 if (IsPermissionKillSwitchOn()) {
235 return PermissionResult(CONTENT_SETTING_BLOCK,
236 PermissionStatusSource::KILL_SWITCH);
239 if (!IsPermissionAvailableToOrigins(requesting_origin, embedding_origin)) {
240 return PermissionResult(CONTENT_SETTING_BLOCK,
241 PermissionStatusSource::INSECURE_ORIGIN);
244 // Check whether the feature is enabled for the frame by permissions policy.
245 // We can only do this when a RenderFrameHost has been provided.
246 if (render_frame_host &&
247 !PermissionAllowedByPermissionsPolicy(render_frame_host)) {
248 return PermissionResult(CONTENT_SETTING_BLOCK,
249 PermissionStatusSource::FEATURE_POLICY);
252 if (render_frame_host) {
253 content::WebContents* web_contents =
254 content::WebContents::FromRenderFrameHost(render_frame_host);
256 // Automatically deny all HTTP or HTTPS requests where the virtual URL and
257 // the loaded URL are for different origins. The loaded URL is the one
258 // actually in the renderer, but the virtual URL is the one
259 // seen by the user. This may be very confusing for a user to see in a
260 // permissions request.
261 content::NavigationEntry* entry =
262 web_contents->GetController().GetLastCommittedEntry();
264 const GURL virtual_url = entry->GetVirtualURL();
265 const GURL loaded_url = entry->GetURL();
266 if (virtual_url.SchemeIsHTTPOrHTTPS() &&
267 loaded_url.SchemeIsHTTPOrHTTPS() &&
268 !url::IsSameOriginWith(virtual_url, loaded_url)) {
269 return PermissionResult(
270 CONTENT_SETTING_BLOCK,
271 PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN);
276 ContentSetting content_setting = GetPermissionStatusInternal(
277 render_frame_host, requesting_origin, embedding_origin);
279 if (content_setting != CONTENT_SETTING_ASK) {
280 return PermissionResult(content_setting,
281 PermissionStatusSource::UNSPECIFIED);
284 absl::optional<PermissionResult> result =
285 PermissionsClient::Get()
286 ->GetPermissionDecisionAutoBlocker(browser_context_)
287 ->GetEmbargoResult(requesting_origin, content_settings_type_);
289 DCHECK(result->content_setting == CONTENT_SETTING_BLOCK);
292 return PermissionResult(CONTENT_SETTING_ASK,
293 PermissionStatusSource::UNSPECIFIED);
296 bool PermissionContextBase::IsPermissionAvailableToOrigins(
297 const GURL& requesting_origin,
298 const GURL& embedding_origin) const {
299 if (IsRestrictedToSecureOrigins()) {
300 if (!network::IsUrlPotentiallyTrustworthy(requesting_origin))
303 // TODO(raymes): We should check the entire chain of embedders here whenever
304 // possible as this corresponds to the requirements of the secure contexts
305 // spec and matches what is implemented in blink. Right now we just check
306 // the top level and requesting origins.
307 if (!PermissionsClient::Get()->CanBypassEmbeddingOriginCheck(
308 requesting_origin, embedding_origin) &&
309 !network::IsUrlPotentiallyTrustworthy(embedding_origin)) {
316 PermissionResult PermissionContextBase::UpdatePermissionStatusWithDeviceStatus(
317 PermissionResult result,
318 const GURL& requesting_origin,
319 const GURL& embedding_origin) const {
323 void PermissionContextBase::ResetPermission(const GURL& requesting_origin,
324 const GURL& embedding_origin) {
325 if (!content_settings::ContentSettingsRegistry::GetInstance()->Get(
326 content_settings_type_)) {
329 PermissionsClient::Get()
330 ->GetSettingsMap(browser_context_)
331 ->SetContentSettingDefaultScope(requesting_origin, embedding_origin,
332 content_settings_type_,
333 CONTENT_SETTING_DEFAULT);
336 bool PermissionContextBase::IsPermissionKillSwitchOn() const {
337 const std::string param = base::GetFieldTrialParamValue(
338 kPermissionsKillSwitchFieldStudy,
339 PermissionUtil::GetPermissionString(content_settings_type_));
341 return param == kPermissionsKillSwitchBlockedValue;
344 ContentSetting PermissionContextBase::GetPermissionStatusInternal(
345 content::RenderFrameHost* render_frame_host,
346 const GURL& requesting_origin,
347 const GURL& embedding_origin) const {
348 return PermissionsClient::Get()
349 ->GetSettingsMap(browser_context_)
350 ->GetContentSetting(requesting_origin, embedding_origin,
351 content_settings_type_);
354 void PermissionContextBase::DecidePermission(
355 const PermissionRequestID& id,
356 const GURL& requesting_origin,
357 const GURL& embedding_origin,
359 BrowserPermissionCallback callback) {
360 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
362 // Under permission delegation, when we display a permission prompt, the
363 // origin displayed in the prompt should never differ from the top-level
364 // origin. Storage access API requests are excluded as they are expected to
365 // request permissions from the frame origin needing access.
366 DCHECK(PermissionsClient::Get()->CanBypassEmbeddingOriginCheck(
367 requesting_origin, embedding_origin) ||
368 requesting_origin == embedding_origin ||
369 content_settings_type_ == ContentSettingsType::STORAGE_ACCESS);
371 content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
372 id.render_process_id(), id.render_frame_id());
375 content::WebContents* web_contents =
376 content::WebContents::FromRenderFrameHost(rfh);
377 PermissionRequestManager* permission_request_manager =
378 PermissionRequestManager::FromWebContents(web_contents);
379 // TODO(felt): sometimes |permission_request_manager| is null. This check is
380 // meant to prevent crashes. See crbug.com/457091.
381 if (!permission_request_manager)
384 std::unique_ptr<PermissionRequest> request_ptr = CreatePermissionRequest(
385 requesting_origin, content_settings_type_, user_gesture, web_contents,
386 base::BindOnce(&PermissionContextBase::PermissionDecided,
387 weak_factory_.GetWeakPtr(), id, requesting_origin,
388 embedding_origin, std::move(callback)),
389 base::BindOnce(&PermissionContextBase::CleanUpRequest,
390 weak_factory_.GetWeakPtr(), id));
391 PermissionRequest* request = request_ptr.get();
395 .insert(std::make_pair(id.ToString(), std::move(request_ptr)))
397 DCHECK(inserted) << "Duplicate id " << id.ToString();
399 permission_request_manager->AddRequest(rfh, request);
402 void PermissionContextBase::PermissionDecided(
403 const PermissionRequestID& id,
404 const GURL& requesting_origin,
405 const GURL& embedding_origin,
406 BrowserPermissionCallback callback,
407 ContentSetting content_setting,
409 DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
410 content_setting == CONTENT_SETTING_BLOCK ||
411 content_setting == CONTENT_SETTING_DEFAULT);
412 UserMadePermissionDecision(id, requesting_origin, embedding_origin,
415 bool persist = content_setting != CONTENT_SETTING_DEFAULT;
416 NotifyPermissionSet(id, requesting_origin, embedding_origin,
417 std::move(callback), persist, content_setting,
421 content::BrowserContext* PermissionContextBase::browser_context() const {
422 return browser_context_;
425 void PermissionContextBase::OnContentSettingChanged(
426 const ContentSettingsPattern& primary_pattern,
427 const ContentSettingsPattern& secondary_pattern,
428 ContentSettingsTypeSet content_type_set) {
429 if (!content_type_set.Contains(content_settings_type_))
432 for (permissions::Observer& obs : permission_observers_) {
433 obs.OnPermissionChanged(primary_pattern, secondary_pattern,
438 void PermissionContextBase::AddObserver(
439 permissions::Observer* permission_observer) {
440 if (permission_observers_.empty() &&
441 !content_setting_observer_registered_by_subclass_) {
442 PermissionsClient::Get()
443 ->GetSettingsMap(browser_context_)
446 permission_observers_.AddObserver(permission_observer);
449 void PermissionContextBase::RemoveObserver(
450 permissions::Observer* permission_observer) {
451 permission_observers_.RemoveObserver(permission_observer);
452 if (permission_observers_.empty() &&
453 !content_setting_observer_registered_by_subclass_) {
454 PermissionsClient::Get()
455 ->GetSettingsMap(browser_context_)
456 ->RemoveObserver(this);
460 void PermissionContextBase::NotifyPermissionSet(
461 const PermissionRequestID& id,
462 const GURL& requesting_origin,
463 const GURL& embedding_origin,
464 BrowserPermissionCallback callback,
466 ContentSetting content_setting,
468 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
471 UpdateContentSetting(requesting_origin, embedding_origin, content_setting,
475 UpdateTabContext(id, requesting_origin,
476 content_setting == CONTENT_SETTING_ALLOW);
478 if (content_setting == CONTENT_SETTING_DEFAULT)
479 content_setting = CONTENT_SETTING_ASK;
481 std::move(callback).Run(content_setting);
484 void PermissionContextBase::CleanUpRequest(const PermissionRequestID& id) {
485 size_t success = pending_requests_.erase(id.ToString());
486 DCHECK(success == 1) << "Missing request " << id.ToString();
489 void PermissionContextBase::UpdateContentSetting(const GURL& requesting_origin,
490 const GURL& embedding_origin,
491 ContentSetting content_setting,
493 DCHECK_EQ(requesting_origin, requesting_origin.DeprecatedGetOriginAsURL());
494 DCHECK_EQ(embedding_origin, embedding_origin.DeprecatedGetOriginAsURL());
495 DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
496 content_setting == CONTENT_SETTING_BLOCK);
498 content_settings::ContentSettingConstraints constraints = {
499 base::Time(), is_one_time ? content_settings::SessionModel::OneTime
500 : content_settings::SessionModel::Durable};
502 #if !BUILDFLAG(IS_ANDROID)
503 if (base::FeatureList::IsEnabled(
504 features::kRecordPermissionExpirationTimestamps)) {
505 // The Permissions module in Safety check will revoke permissions after
506 // a finite amount of time.
507 // We're only interested in expiring permissions that:
509 // 2. Fall back to ASK.
510 // 3. Are not already a one-time grant.
511 if (content_setting == CONTENT_SETTING_ALLOW && !is_one_time &&
512 content_settings::CanTrackLastVisit(content_settings_type_)) {
513 // For #2, by definition, that should be all of them. If that changes in
514 // the future, consider whether revocation for such permission makes
515 // sense, and/or change this to an early return so that we don't
516 // unnecessarily record timestamps where we don't need them.
517 constraints.track_last_visit_for_autoexpiration = true;
520 #endif // !BUILDFLAG(IS_ANDROID)
522 PermissionsClient::Get()
523 ->GetSettingsMap(browser_context_)
524 ->SetContentSettingDefaultScope(requesting_origin, embedding_origin,
525 content_settings_type_, content_setting,
529 bool PermissionContextBase::PermissionAllowedByPermissionsPolicy(
530 content::RenderFrameHost* rfh) const {
531 // Some features don't have an associated permissions policy yet. Allow those.
532 if (permissions_policy_feature_ ==
533 blink::mojom::PermissionsPolicyFeature::kNotFound)
536 return rfh->IsFeatureEnabled(permissions_policy_feature_);
539 } // namespace permissions