blob: 05321f0d14a3fdfb4327422a44ee469aacc42dfd [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/permissions/permission_context_base.h"
#include <stddef.h>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_forward.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/field_trial_params.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/content_settings/core/browser/content_settings_registry.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/features.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_context_base.h"
#include "components/permissions/permission_decision_auto_blocker.h"
#include "components/permissions/permission_request.h"
#include "components/permissions/permission_request_id.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/permissions_client.h"
#include "components/permissions/request_type.h"
#include "components/permissions/resolvers/content_setting_permission_resolver.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom.h"
#include "url/gurl.h"
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
#include "components/guest_view/browser/guest_view_base.h"
#endif
#if BUILDFLAG(IS_ANDROID)
#include "components/permissions/android/android_permission_util.h"
#include "ui/android/window_android.h"
#endif
namespace permissions {
namespace {
using PermissionStatus = blink::mojom::PermissionStatus;
void LogPermissionBlockedMessage(content::RenderFrameHost* rfh,
std::string_view reason,
ContentSettingsType type) {
rfh->GetOutermostMainFrame()->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kWarning,
base::StrCat({PermissionUtil::GetPermissionString(type),
" permission has been blocked", reason}));
}
} // namespace
// static
const char PermissionContextBase::kPermissionsKillSwitchFieldStudy[] =
"PermissionsKillSwitch";
// static
const char PermissionContextBase::kPermissionsKillSwitchBlockedValue[] =
"blocked";
PermissionContextBase::PermissionContextBase(
content::BrowserContext* browser_context,
ContentSettingsType content_settings_type,
network::mojom::PermissionsPolicyFeature permissions_policy_feature)
: browser_context_(browser_context),
content_settings_type_(content_settings_type),
permissions_policy_feature_(permissions_policy_feature) {
CHECK(permissions::PermissionUtil::IsPermission(content_settings_type_));
}
PermissionContextBase::~PermissionContextBase() {
DCHECK(permission_observers_.empty());
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
void PermissionContextBase::RequestPermission(
std::unique_ptr<PermissionRequestData> request_data,
BrowserPermissionCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::RenderFrameHost* const rfh = content::RenderFrameHost::FromID(
request_data->id.global_render_frame_host_id());
if (!rfh) {
// Permission request is not allowed without a valid RenderFrameHost.
std::move(callback).Run(CONTENT_SETTING_ASK);
return;
}
request_data
->WithRequestingOrigin(
request_data->requesting_origin.DeprecatedGetOriginAsURL())
.WithEmbeddingOrigin(GetEffectiveEmbedderOrigin(rfh));
if (!request_data->requesting_origin.is_valid() ||
!request_data->embedding_origin.is_valid()) {
std::string type_name =
PermissionUtil::GetPermissionString(content_settings_type_);
DVLOG(1) << "Attempt to use " << type_name
<< " from an invalid URL: " << request_data->requesting_origin
<< "," << request_data->embedding_origin << " (" << type_name
<< " is not supported in popups)";
NotifyPermissionSet(*request_data, std::move(callback),
/*persist=*/false, CONTENT_SETTING_BLOCK,
/*is_one_time=*/false,
/*is_final_decision=*/true);
return;
}
// Check the content setting to see if the user has already made a decision,
// or if the origin is under embargo. If so, respect that decision.
DCHECK(rfh);
content::PermissionResult result = GetPermissionStatus(
*request_data->resolver, rfh, request_data->requesting_origin,
request_data->embedding_origin);
bool status_ignorable = PermissionUtil::CanPermissionRequestIgnoreStatus(
request_data, result.source);
if (!status_ignorable && (result.status == PermissionStatus::GRANTED ||
result.status == PermissionStatus::DENIED)) {
static constexpr char kResetInstructions[] =
" This can be reset in "
#if BUILDFLAG(IS_ANDROID)
"Site Settings"
#else
"Page Info which can be accessed by clicking the tune icon next to "
"the URL"
#endif
". See https://www.chromestatus.com/feature/6443143280984064 for "
"more information.";
switch (result.source) {
case content::PermissionStatusSource::KILL_SWITCH:
// Block the request and log to the developer console.
static constexpr char kPermissionBlockedKillSwitchReason[] = ".";
LogPermissionBlockedMessage(rfh, kPermissionBlockedKillSwitchReason,
content_settings_type_);
PermissionUmaUtil::RecordPermissionRequestedFromFrame(
content_settings_type_, rfh);
std::move(callback).Run(CONTENT_SETTING_BLOCK);
return;
case content::PermissionStatusSource::MULTIPLE_DISMISSALS:
static constexpr char kPermissionBlockedRepeatedDismissalsReason[] =
" as the user has dismissed the permission prompt several times.";
LogPermissionBlockedMessage(
rfh,
base::StrCat({kPermissionBlockedRepeatedDismissalsReason,
kResetInstructions}),
content_settings_type_);
PermissionUmaUtil::RecordPermissionRequestedFromFrame(
content_settings_type_, rfh);
break;
case content::PermissionStatusSource::MULTIPLE_IGNORES:
static constexpr char kPermissionBlockedRepeatedIgnoresReason[] =
" as the user has ignored the permission prompt several times.";
LogPermissionBlockedMessage(
rfh,
base::StrCat(
{kPermissionBlockedRepeatedIgnoresReason, kResetInstructions}),
content_settings_type_);
PermissionUmaUtil::RecordPermissionRequestedFromFrame(
content_settings_type_, rfh);
break;
case content::PermissionStatusSource::FEATURE_POLICY:
static constexpr char kPermissionBlockedPermissionsPolicyReason[] =
" because of a permissions policy applied to the current document. "
"See https://crbug.com/414348233 for more details.";
LogPermissionBlockedMessage(rfh,
kPermissionBlockedPermissionsPolicyReason,
content_settings_type_);
break;
case content::PermissionStatusSource::RECENT_DISPLAY:
static constexpr char kPermissionBlockedRecentDisplayReason[] =
" as the prompt has already been displayed to the user recently.";
LogPermissionBlockedMessage(rfh, kPermissionBlockedRecentDisplayReason,
content_settings_type_);
break;
case content::PermissionStatusSource::UNSPECIFIED:
PermissionUmaUtil::RecordPermissionRequestedFromFrame(
content_settings_type_, rfh);
break;
case content::PermissionStatusSource::FENCED_FRAME:
case content::PermissionStatusSource::INSECURE_ORIGIN:
case content::PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN:
break;
}
// If we are under embargo, record the embargo reason for which we have
// suppressed the prompt.
PermissionUmaUtil::RecordEmbargoPromptSuppressionFromSource(result.source);
NotifyPermissionSet(
*request_data, std::move(callback),
/*persist=*/false,
PermissionUtil::PermissionStatusToContentSetting(result.status),
/*is_one_time=*/false,
/*is_final_decision=*/true);
return;
}
PermissionUmaUtil::RecordPermissionRequestedFromFrame(content_settings_type_,
rfh);
// We are going to show a prompt now.
PermissionUmaUtil::PermissionRequested(content_settings_type_);
PermissionUmaUtil::RecordEmbargoPromptSuppression(
PermissionEmbargoStatus::NOT_EMBARGOED);
DecidePermission(std::move(request_data), std::move(callback));
}
bool PermissionContextBase::IsRestrictedToSecureOrigins() const {
return true;
}
void PermissionContextBase::UserMadePermissionDecision(
const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
ContentSetting content_setting) {}
std::unique_ptr<PermissionRequest>
PermissionContextBase::CreatePermissionRequest(
content::WebContents* web_contents,
std::unique_ptr<PermissionRequestData> request_data,
PermissionRequest::PermissionDecidedCallback permission_decided_callback,
base::OnceClosure request_finished_callback) const {
return std::make_unique<PermissionRequest>(
std::move(request_data), std::move(permission_decided_callback),
std::move(request_finished_callback), UsesAutomaticEmbargo());
}
bool PermissionContextBase::UsesAutomaticEmbargo() const {
return true;
}
const PermissionRequest* PermissionContextBase::FindPermissionRequest(
const PermissionRequestID& id) const {
const auto request = pending_requests_.find(id.ToString());
if (request == pending_requests_.end()) {
return nullptr;
}
return request->second.first.get();
}
GURL PermissionContextBase::GetEffectiveEmbedderOrigin(
content::RenderFrameHost* rfh) const {
return PermissionUtil::GetLastCommittedOriginAsURL(rfh->GetMainFrame());
}
content::PermissionResult PermissionContextBase::GetPermissionStatus(
const PermissionResolver& resolver,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& embedding_origin) const {
// If the permission has been disabled through Finch, block all requests.
if (IsPermissionKillSwitchOn()) {
return content::PermissionResult(
PermissionStatus::DENIED, content::PermissionStatusSource::KILL_SWITCH);
}
if (!IsPermissionAvailableToOrigins(requesting_origin, embedding_origin)) {
return content::PermissionResult(
PermissionStatus::DENIED,
content::PermissionStatusSource::INSECURE_ORIGIN);
}
// Check whether the feature is enabled for the frame by permissions policy.
// We can only do this when a RenderFrameHost has been provided.
if (render_frame_host &&
!PermissionAllowedByPermissionsPolicy(render_frame_host)) {
return content::PermissionResult(
PermissionStatus::DENIED,
content::PermissionStatusSource::FEATURE_POLICY);
}
if (render_frame_host) {
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host);
// Automatically deny all HTTP or HTTPS requests where the virtual URL and
// the loaded URL are for different origins. The loaded URL is the one
// actually in the renderer, but the virtual URL is the one
// seen by the user. This may be very confusing for a user to see in a
// permissions request.
content::NavigationEntry* entry =
web_contents->GetController().GetLastCommittedEntry();
if (entry) {
const GURL virtual_url = entry->GetVirtualURL();
const GURL loaded_url = entry->GetURL();
if (virtual_url.SchemeIsHTTPOrHTTPS() &&
loaded_url.SchemeIsHTTPOrHTTPS() &&
!url::IsSameOriginWith(virtual_url, loaded_url)) {
return content::PermissionResult(
PermissionStatus::DENIED,
content::PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN);
}
}
}
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
guest_view::GuestViewBase* guest =
guest_view::GuestViewBase::FromRenderFrameHost(render_frame_host);
if (guest) {
// Content inside GuestView instances may have different permission
// behavior.
std::optional<content::PermissionResult> maybe_result =
guest->OverridePermissionResult(content_settings_type_);
if (maybe_result.has_value()) {
return maybe_result.value();
}
// Some GuestViews are loaded in a separate StoragePartition. Given that
// permissions are scoped to a BrowserContext, not a StoragePartition, we
// may have a situation where a user has granted a permission to an origin
// in a tab and then visits the same origin in a guest. This would lead to
// inappropriate sharing of the permission with the guest. To mitigate this,
// we drop permission requests from guests for cases where it's not possible
// for the guest to have been granted the permission. Note that sharing of
// permissions that the guest could legitimately be granted is still
// possible.
// TODO(crbug.com/40068594): Scope granted permissions to a
// StoragePartition.
if (base::FeatureList::IsEnabled(
features::kMitigateUnpartitionedWebviewPermissions) &&
!guest->IsPermissionRequestable(content_settings_type_)) {
return content::PermissionResult(
PermissionStatus::DENIED,
content::PermissionStatusSource::UNSPECIFIED);
}
}
#endif
ContentSetting content_setting = GetPermissionStatusInternal(
render_frame_host, requesting_origin, embedding_origin);
if (content_setting != CONTENT_SETTING_ASK) {
return content::PermissionResult(
PermissionUtil::ContentSettingToPermissionStatus(content_setting),
content::PermissionStatusSource::UNSPECIFIED);
}
if (UsesAutomaticEmbargo()) {
std::optional<content::PermissionResult> result =
PermissionsClient::Get()
->GetPermissionDecisionAutoBlocker(browser_context_)
->GetEmbargoResult(requesting_origin, content_settings_type_);
if (result) {
DCHECK(result->status == PermissionStatus::DENIED);
return result.value();
}
}
return content::PermissionResult(
PermissionStatus::ASK, content::PermissionStatusSource::UNSPECIFIED);
}
content::PermissionResult PermissionContextBase::GetPermissionStatus(
const blink::mojom::PermissionDescriptorPtr& permission_descriptor,
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& embedding_origin) const {
return GetPermissionStatus(*CreatePermissionResolver(permission_descriptor),
render_frame_host, requesting_origin,
embedding_origin);
}
bool PermissionContextBase::IsPermissionAvailableToOrigins(
const GURL& requesting_origin,
const GURL& embedding_origin) const {
if (IsRestrictedToSecureOrigins()) {
if (!network::IsUrlPotentiallyTrustworthy(requesting_origin))
return false;
// TODO(raymes): We should check the entire chain of embedders here whenever
// possible as this corresponds to the requirements of the secure contexts
// spec and matches what is implemented in blink. Right now we just check
// the top level and requesting origins.
if (!PermissionsClient::Get()->CanBypassEmbeddingOriginCheck(
requesting_origin, embedding_origin) &&
!network::IsUrlPotentiallyTrustworthy(embedding_origin)) {
return false;
}
}
return true;
}
content::PermissionResult
PermissionContextBase::UpdatePermissionStatusWithDeviceStatus(
content::WebContents* web_contents,
content::PermissionResult result,
const GURL& requesting_origin,
const GURL& embedding_origin) {
MaybeUpdateCachedHasDevicePermission(web_contents);
// If the site content setting is ASK/BLOCKED the device-level permission
// won't affect it.
if (result.status != blink::mojom::PermissionStatus::GRANTED) {
return result;
}
// If the device-level permission is granted, it has no effect on the result.
if (last_has_device_permission_result_.has_value() &&
last_has_device_permission_result_.value()) {
return result;
}
#if BUILDFLAG(IS_ANDROID)
if (!web_contents || !web_contents->GetNativeView() ||
!web_contents->GetNativeView()->GetWindowAndroid()) {
return result;
}
result.status =
CanRequestSystemPermission(content_settings_type(), web_contents)
? blink::mojom::PermissionStatus::ASK
: blink::mojom::PermissionStatus::DENIED;
#else
// Otherwise the result will be "ASK" if the browser can ask for the
// device-level permission, and "BLOCKED" otherwise.
result.status = PermissionsClient::Get()->CanRequestDevicePermission(
content_settings_type())
? blink::mojom::PermissionStatus::ASK
: blink::mojom::PermissionStatus::DENIED;
#endif
return result;
}
void PermissionContextBase::ResetPermission(const GURL& requesting_origin,
const GURL& embedding_origin) {
if (!content_settings::ContentSettingsRegistry::GetInstance()->Get(
content_settings_type_)) {
return;
}
PermissionsClient::Get()
->GetSettingsMap(browser_context_)
->SetContentSettingDefaultScope(requesting_origin, embedding_origin,
content_settings_type_,
CONTENT_SETTING_DEFAULT);
}
bool PermissionContextBase::AlwaysIncludeDeviceStatus() const {
return false;
}
bool PermissionContextBase::IsPermissionKillSwitchOn() const {
const std::string param = base::GetFieldTrialParamValue(
kPermissionsKillSwitchFieldStudy,
PermissionUtil::GetPermissionString(content_settings_type_));
return param == kPermissionsKillSwitchBlockedValue;
}
ContentSetting PermissionContextBase::GetPermissionStatusInternal(
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& embedding_origin) const {
return PermissionsClient::Get()
->GetSettingsMap(browser_context_)
->GetContentSetting(requesting_origin, embedding_origin,
content_settings_type_);
}
void PermissionContextBase::DecidePermission(
std::unique_ptr<PermissionRequestData> request_data,
BrowserPermissionCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// Under permission delegation, when we display a permission prompt, the
// origin displayed in the prompt should never differ from the top-level
// origin. Storage access API requests are excluded as they are expected to
// request permissions from the frame origin needing access.
DCHECK(PermissionsClient::Get()->CanBypassEmbeddingOriginCheck(
request_data->requesting_origin, request_data->embedding_origin) ||
request_data->requesting_origin == request_data->embedding_origin ||
content_settings_type_ == ContentSettingsType::STORAGE_ACCESS);
content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
request_data->id.global_render_frame_host_id());
DCHECK(rfh);
content::WebContents* web_contents =
content::WebContents::FromRenderFrameHost(rfh);
PermissionRequestManager* permission_request_manager =
PermissionRequestManager::FromWebContents(web_contents);
// TODO(felt): sometimes |permission_request_manager| is null. This check is
// meant to prevent crashes. See crbug.com/457091.
if (!permission_request_manager) {
std::move(callback).Run(CONTENT_SETTING_ASK);
return;
}
auto decided_cb = base::BindRepeating(
&PermissionContextBase::PermissionDecided, weak_factory_.GetWeakPtr());
auto cleanup_cb =
base::BindOnce(&PermissionContextBase::CleanUpRequest,
weak_factory_.GetWeakPtr(), web_contents, request_data->id,
request_data->embedded_permission_element_initiated);
PermissionRequestID permission_request_id = request_data->id;
std::unique_ptr<PermissionRequest> request =
CreatePermissionRequest(web_contents, std::move(request_data),
std::move(decided_cb), std::move(cleanup_cb));
bool inserted =
pending_requests_
.insert(std::make_pair(
permission_request_id.ToString(),
std::make_pair(request->GetWeakPtr(), std::move(callback))))
.second;
DCHECK(inserted) << "Duplicate id " << permission_request_id.ToString();
permission_request_manager->AddRequest(rfh, std::move(request));
}
void PermissionContextBase::PermissionDecided(
ContentSetting content_setting,
bool is_one_time,
bool is_final_decision,
const PermissionRequestData& request_data) {
DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
content_setting == CONTENT_SETTING_BLOCK ||
content_setting == CONTENT_SETTING_DEFAULT);
UserMadePermissionDecision(request_data.id, request_data.requesting_origin,
request_data.embedding_origin, content_setting);
bool persist = content_setting != CONTENT_SETTING_DEFAULT;
auto request = pending_requests_.find(request_data.id.ToString());
CHECK(request->second.first);
CHECK(request != pending_requests_.end());
// Check if `request` has `BrowserPermissionCallback`. The call back might be
// missing if a permission prompt was preignored and we already notified an
// origin about it.
if (request->second.second) {
NotifyPermissionSet(request_data, std::move(request->second.second),
persist, content_setting, is_one_time,
is_final_decision);
} else {
NotifyPermissionSet(request_data, base::DoNothing(), persist,
content_setting, is_one_time, is_final_decision);
}
}
content::BrowserContext* PermissionContextBase::browser_context() const {
return browser_context_;
}
void PermissionContextBase::OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsTypeSet content_type_set) {
NotifyObservers(primary_pattern, secondary_pattern, content_type_set);
}
void PermissionContextBase::AddObserver(
permissions::Observer* permission_observer) {
if (permission_observers_.empty() &&
!content_setting_observer_registered_by_subclass_) {
PermissionsClient::Get()
->GetSettingsMap(browser_context_)
->AddObserver(this);
}
permission_observers_.AddObserver(permission_observer);
}
void PermissionContextBase::RemoveObserver(
permissions::Observer* permission_observer) {
permission_observers_.RemoveObserver(permission_observer);
if (permission_observers_.empty() &&
!content_setting_observer_registered_by_subclass_) {
PermissionsClient::Get()
->GetSettingsMap(browser_context_)
->RemoveObserver(this);
}
}
std::unique_ptr<PermissionResolver>
PermissionContextBase::CreatePermissionResolver(
const blink::mojom::PermissionDescriptorPtr& permission_descriptor) const {
return CreateRequestIndependentPermissionResolver();
}
std::unique_ptr<PermissionResolver>
PermissionContextBase::CreateRequestIndependentPermissionResolver() const {
return std::make_unique<ContentSettingPermissionResolver>(
content_settings_type_);
}
void PermissionContextBase::MaybeUpdateCachedHasDevicePermission(
content::WebContents* web_contents) {
#if BUILDFLAG(IS_ANDROID)
if (!web_contents || !web_contents->GetNativeView() ||
!web_contents->GetNativeView()->GetWindowAndroid()) {
return;
}
const bool has_device_permission =
has_device_permission_for_test_.has_value()
? has_device_permission_for_test_.value()
: HasSystemPermission(content_settings_type(), web_contents);
#else
const bool has_device_permission =
has_device_permission_for_test_.has_value()
? has_device_permission_for_test_.value()
: PermissionsClient::Get()->HasDevicePermission(
content_settings_type());
#endif
const bool should_notify_observers =
last_has_device_permission_result_.has_value() &&
has_device_permission != last_has_device_permission_result_;
// We need to update |last_has_device_permission_result_| before calling
// |OnContentSettingChanged| to avoid causing a re-entrancy issue since the
// |OnContentSettingChanged| will likely end up calling |GetPermissionStatus|.
last_has_device_permission_result_ = has_device_permission;
if (should_notify_observers) {
NotifyObservers(ContentSettingsPattern::Wildcard(),
ContentSettingsPattern::Wildcard(),
ContentSettingsTypeSet(content_settings_type()));
}
}
void PermissionContextBase::NotifyPermissionSet(
const PermissionRequestData& request_data,
BrowserPermissionCallback callback,
bool persist,
ContentSetting content_setting,
bool is_one_time,
bool is_final_decision) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (persist) {
UpdateContentSetting(request_data, content_setting, is_one_time);
}
if (is_final_decision) {
UpdateTabContext(request_data.id, request_data.requesting_origin,
content_setting == CONTENT_SETTING_ALLOW);
if (content_setting == CONTENT_SETTING_ALLOW) {
if (auto* rfh = content::RenderFrameHost::FromID(
request_data.id.global_render_frame_host_id())) {
PermissionUmaUtil::RecordPermissionsUsageSourceAndPolicyConfiguration(
content_settings_type_, rfh);
}
}
}
if (content_setting == CONTENT_SETTING_DEFAULT)
content_setting = CONTENT_SETTING_ASK;
std::move(callback).Run(content_setting);
}
void PermissionContextBase::CleanUpRequest(
content::WebContents* web_contents,
const PermissionRequestID& id,
bool embedded_permission_element_initiated) {
size_t success = pending_requests_.erase(id.ToString());
// A request from an embedded permission element requires a notification
// `OnPermissionChanged` when changing the device status, which is currently
// unavailable. We compare the device status with the cached status and notify
// `OnPermissionChanged` here. We should remove this line once the device
// status change observer is implemented.
if (embedded_permission_element_initiated) {
MaybeUpdateCachedHasDevicePermission(web_contents);
}
DCHECK(success == 1) << "Missing request " << id.ToString();
}
void PermissionContextBase::UpdateContentSetting(
const PermissionRequestData& request_data,
ContentSetting content_setting,
bool is_one_time) {
DCHECK_EQ(request_data.requesting_origin,
request_data.requesting_origin.DeprecatedGetOriginAsURL());
DCHECK_EQ(request_data.embedding_origin,
request_data.embedding_origin.DeprecatedGetOriginAsURL());
DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
content_setting == CONTENT_SETTING_BLOCK);
content_settings::ContentSettingConstraints constraints;
constraints.set_session_model(
is_one_time ? content_settings::mojom::SessionModel::ONE_TIME
: content_settings::mojom::SessionModel::DURABLE);
// The Permissions module in Safety check will revoke permissions after
// a finite amount of time if the permission can be revoked.
if (content_settings::CanBeAutoRevoked(content_settings_type_,
content_setting, is_one_time)) {
// For #2, by definition, that should be all of them. If that changes in
// the future, consider whether revocation for such permission makes
// sense, and/or change this to an early return so that we don't
// unnecessarily record timestamps where we don't need them.
constraints.set_track_last_visit_for_autoexpiration(true);
}
if (is_one_time) {
if (content_settings::ShouldTypeExpireActively(content_settings_type_)) {
constraints.set_lifetime(kOneTimePermissionMaximumLifetime);
}
}
PermissionsClient::Get()
->GetSettingsMap(browser_context_)
->SetContentSettingDefaultScope(
request_data.requesting_origin, request_data.embedding_origin,
content_settings_type_, content_setting, constraints);
}
bool PermissionContextBase::PermissionAllowedByPermissionsPolicy(
content::RenderFrameHost* rfh) const {
// Some features don't have an associated permissions policy yet. Allow those.
if (permissions_policy_feature_ ==
network::mojom::PermissionsPolicyFeature::kNotFound) {
return true;
}
return rfh->IsFeatureEnabled(permissions_policy_feature_);
}
void PermissionContextBase::NotifyObservers(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsTypeSet content_type_set) const {
if (!content_type_set.Contains(content_settings_type_)) {
return;
}
for (permissions::Observer& obs : permission_observers_) {
obs.OnPermissionChanged(primary_pattern, secondary_pattern,
content_type_set);
}
}
} // namespace permissions