#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_util.h"
#include "components/permissions/permissions_client.h"
#include "components/permissions/request_type.h"
-#include "components/permissions/unused_site_permissions_service.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/navigation_entry.h"
#include "third_party/blink/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
+
namespace permissions {
namespace {
+using PermissionStatus = blink::mojom::PermissionStatus;
+
const char kPermissionBlockedKillSwitchMessage[] =
"%s permission has been blocked.";
ContentSettingsType type) {
rfh->GetOutermostMainFrame()->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kWarning,
- base::StringPrintf(message,
- PermissionUtil::GetPermissionString(type).c_str()));
+ base::StringPrintfNonConstexpr(
+ message, PermissionUtil::GetPermissionString(type).c_str()));
}
} // namespace
: browser_context_(browser_context),
content_settings_type_(content_settings_type),
permissions_policy_feature_(permissions_policy_feature) {
+ CHECK(permissions::PermissionUtil::IsPermission(content_settings_type_));
PermissionDecisionAutoBlocker::UpdateFromVariations();
}
}
void PermissionContextBase::RequestPermission(
- const PermissionRequestID& id,
- const GURL& requesting_frame,
- bool user_gesture,
+ PermissionRequestData request_data,
BrowserPermissionCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- content::RenderFrameHost* const rfh =
- content::RenderFrameHost::FromID(id.global_render_frame_host_id());
+ 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.
return;
}
- const GURL requesting_origin = requesting_frame.DeprecatedGetOriginAsURL();
- const GURL embedding_origin =
- PermissionUtil::GetLastCommittedOriginAsURL(rfh->GetMainFrame());
+ request_data
+ .WithRequestingOrigin(
+ request_data.requesting_origin.DeprecatedGetOriginAsURL())
+ .WithEmbeddingOrigin(
+ PermissionUtil::GetLastCommittedOriginAsURL(rfh->GetMainFrame()));
- if (!requesting_origin.is_valid() || !embedding_origin.is_valid()) {
+ 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: " << requesting_origin << ","
- << embedding_origin << " (" << type_name
+ << " from an invalid URL: " << request_data.requesting_origin
+ << "," << request_data.embedding_origin << " (" << type_name
<< " is not supported in popups)";
- NotifyPermissionSet(id, requesting_origin, embedding_origin,
- std::move(callback), /*persist=*/false,
- CONTENT_SETTING_BLOCK, /*is_one_time=*/false,
+ NotifyPermissionSet(request_data.id, request_data.requesting_origin,
+ request_data.embedding_origin, 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);
- PermissionResult result =
- GetPermissionStatus(rfh, requesting_origin, embedding_origin);
+ content::PermissionResult result = GetPermissionStatus(
+ rfh, request_data.requesting_origin, request_data.embedding_origin);
- if (result.content_setting == CONTENT_SETTING_ALLOW ||
- result.content_setting == CONTENT_SETTING_BLOCK) {
+ bool status_ignorable = PermissionUtil::CanPermissionRequestIgnoreStatus(
+ request_data, result.source);
+
+ if (!status_ignorable && (result.status == PermissionStatus::GRANTED ||
+ result.status == PermissionStatus::DENIED)) {
switch (result.source) {
- case PermissionStatusSource::KILL_SWITCH:
+ case content::PermissionStatusSource::KILL_SWITCH:
// Block the request and log to the developer console.
LogPermissionBlockedMessage(rfh, kPermissionBlockedKillSwitchMessage,
content_settings_type_);
content_settings_type_, rfh);
std::move(callback).Run(CONTENT_SETTING_BLOCK);
return;
- case PermissionStatusSource::MULTIPLE_DISMISSALS:
+ case content::PermissionStatusSource::MULTIPLE_DISMISSALS:
LogPermissionBlockedMessage(rfh,
kPermissionBlockedRepeatedDismissalsMessage,
content_settings_type_);
PermissionUmaUtil::RecordPermissionRequestedFromFrame(
content_settings_type_, rfh);
break;
- case PermissionStatusSource::MULTIPLE_IGNORES:
+ case content::PermissionStatusSource::MULTIPLE_IGNORES:
LogPermissionBlockedMessage(rfh,
kPermissionBlockedRepeatedIgnoresMessage,
content_settings_type_);
PermissionUmaUtil::RecordPermissionRequestedFromFrame(
content_settings_type_, rfh);
break;
- case PermissionStatusSource::FEATURE_POLICY:
+ case content::PermissionStatusSource::FEATURE_POLICY:
LogPermissionBlockedMessage(rfh,
kPermissionBlockedPermissionsPolicyMessage,
content_settings_type_);
break;
- case PermissionStatusSource::RECENT_DISPLAY:
+ case content::PermissionStatusSource::RECENT_DISPLAY:
LogPermissionBlockedMessage(rfh, kPermissionBlockedRecentDisplayMessage,
content_settings_type_);
break;
- case PermissionStatusSource::UNSPECIFIED:
+ case content::PermissionStatusSource::UNSPECIFIED:
PermissionUmaUtil::RecordPermissionRequestedFromFrame(
content_settings_type_, rfh);
break;
- case PermissionStatusSource::PORTAL:
- case PermissionStatusSource::FENCED_FRAME:
- case PermissionStatusSource::INSECURE_ORIGIN:
- case PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN:
+ case content::PermissionStatusSource::PORTAL:
+ 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(id, requesting_origin, embedding_origin,
- std::move(callback), /*persist=*/false,
- result.content_setting, /*is_one_time=*/false,
- /*is_final_decision=*/true);
+ NotifyPermissionSet(
+ request_data.id, request_data.requesting_origin,
+ request_data.embedding_origin, std::move(callback),
+ /*persist=*/false,
+ PermissionUtil::PermissionStatusToContentSetting(result.status),
+ /*is_one_time=*/false,
+ /*is_final_decision=*/true);
return;
}
PermissionUmaUtil::RecordEmbargoPromptSuppression(
PermissionEmbargoStatus::NOT_EMBARGOED);
- DecidePermission(id, requesting_origin, embedding_origin, user_gesture,
- std::move(callback));
+ DecidePermission(std::move(request_data), std::move(callback));
}
bool PermissionContextBase::IsRestrictedToSecureOrigins() const {
std::unique_ptr<PermissionRequest>
PermissionContextBase::CreatePermissionRequest(
- const GURL& request_origin,
- ContentSettingsType content_settings_type,
- bool has_gesture,
content::WebContents* web_contents,
+ PermissionRequestData request_data,
PermissionRequest::PermissionDecidedCallback permission_decided_callback,
base::OnceClosure delete_callback) const {
return std::make_unique<PermissionRequest>(
- request_origin, ContentSettingsTypeToRequestType(content_settings_type),
- has_gesture, std::move(permission_decided_callback),
+ std::move(request_data), std::move(permission_decided_callback),
std::move(delete_callback));
}
-PermissionResult PermissionContextBase::GetPermissionStatus(
+content::PermissionResult PermissionContextBase::GetPermissionStatus(
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 PermissionResult(CONTENT_SETTING_BLOCK,
- PermissionStatusSource::KILL_SWITCH);
+ return content::PermissionResult(
+ PermissionStatus::DENIED, content::PermissionStatusSource::KILL_SWITCH);
}
if (!IsPermissionAvailableToOrigins(requesting_origin, embedding_origin)) {
- return PermissionResult(CONTENT_SETTING_BLOCK,
- PermissionStatusSource::INSECURE_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 PermissionResult(CONTENT_SETTING_BLOCK,
- PermissionStatusSource::FEATURE_POLICY);
+ return content::PermissionResult(
+ PermissionStatus::DENIED,
+ content::PermissionStatusSource::FEATURE_POLICY);
}
if (render_frame_host) {
if (virtual_url.SchemeIsHTTPOrHTTPS() &&
loaded_url.SchemeIsHTTPOrHTTPS() &&
!url::IsSameOriginWith(virtual_url, loaded_url)) {
- return PermissionResult(
- CONTENT_SETTING_BLOCK,
- PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN);
+ return content::PermissionResult(
+ PermissionStatus::DENIED,
+ content::PermissionStatusSource::VIRTUAL_URL_DIFFERENT_ORIGIN);
}
}
}
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+ // 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/1469672): Scope granted permissions to a StoragePartition.
+ if (base::FeatureList::IsEnabled(
+ features::kMitigateUnpartitionedWebviewPermissions)) {
+ guest_view::GuestViewBase* guest =
+ guest_view::GuestViewBase::FromRenderFrameHost(render_frame_host);
+ if (guest && !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 PermissionResult(content_setting,
- PermissionStatusSource::UNSPECIFIED);
+ return content::PermissionResult(
+ PermissionUtil::ContentSettingToPermissionStatus(content_setting),
+ content::PermissionStatusSource::UNSPECIFIED);
}
- absl::optional<PermissionResult> result =
+ absl::optional<content::PermissionResult> result =
PermissionsClient::Get()
->GetPermissionDecisionAutoBlocker(browser_context_)
->GetEmbargoResult(requesting_origin, content_settings_type_);
if (result) {
- DCHECK(result->content_setting == CONTENT_SETTING_BLOCK);
- return *result;
+ DCHECK(result->status == PermissionStatus::DENIED);
+ return result.value();
}
- return PermissionResult(CONTENT_SETTING_ASK,
- PermissionStatusSource::UNSPECIFIED);
+ return content::PermissionResult(
+ PermissionStatus::ASK, content::PermissionStatusSource::UNSPECIFIED);
}
bool PermissionContextBase::IsPermissionAvailableToOrigins(
return true;
}
-PermissionResult PermissionContextBase::UpdatePermissionStatusWithDeviceStatus(
- PermissionResult result,
+content::PermissionResult
+PermissionContextBase::UpdatePermissionStatusWithDeviceStatus(
+ content::PermissionResult result,
const GURL& requesting_origin,
const GURL& embedding_origin) const {
+ const bool has_device_permission =
+ PermissionsClient::Get()->HasDevicePermission(content_settings_type());
+ 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()));
+ }
+
+ // 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 (has_device_permission) {
+ return result;
+ }
+
+ // 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;
+
return result;
}
}
void PermissionContextBase::DecidePermission(
- const PermissionRequestID& id,
- const GURL& requesting_origin,
- const GURL& embedding_origin,
- bool user_gesture,
+ PermissionRequestData request_data,
BrowserPermissionCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// origin. Storage access API requests are excluded as they are expected to
// request permissions from the frame origin needing access.
DCHECK(PermissionsClient::Get()->CanBypassEmbeddingOriginCheck(
- requesting_origin, embedding_origin) ||
- requesting_origin == embedding_origin ||
+ 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(id.global_render_frame_host_id());
+ content::RenderFrameHost* rfh = content::RenderFrameHost::FromID(
+ request_data.id.global_render_frame_host_id());
DCHECK(rfh);
content::WebContents* web_contents =
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)
+ if (!permission_request_manager) {
+ std::move(callback).Run(CONTENT_SETTING_ASK);
return;
+ }
- std::unique_ptr<PermissionRequest> request_ptr = CreatePermissionRequest(
- requesting_origin, content_settings_type_, user_gesture, web_contents,
- base::BindRepeating(&PermissionContextBase::PermissionDecided,
- weak_factory_.GetWeakPtr(), id, requesting_origin,
- embedding_origin),
- base::BindOnce(&PermissionContextBase::CleanUpRequest,
- weak_factory_.GetWeakPtr(), id));
+ auto decided_cb = base::BindRepeating(
+ &PermissionContextBase::PermissionDecided, weak_factory_.GetWeakPtr(),
+ request_data.id, request_data.requesting_origin,
+ request_data.embedding_origin);
+ auto cleanup_cb = base::BindOnce(&PermissionContextBase::CleanUpRequest,
+ weak_factory_.GetWeakPtr(), request_data.id);
+ PermissionRequestID permission_request_id = request_data.id;
+
+ std::unique_ptr<PermissionRequest> request_ptr =
+ CreatePermissionRequest(web_contents, std::move(request_data),
+ std::move(decided_cb), std::move(cleanup_cb));
PermissionRequest* request = request_ptr.get();
- bool inserted = pending_requests_
- .insert(std::make_pair(
- id.ToString(), std::make_pair(std::move(request_ptr),
- std::move(callback))))
- .second;
- DCHECK(inserted) << "Duplicate id " << id.ToString();
+ bool inserted =
+ pending_requests_
+ .insert(std::make_pair(
+ permission_request_id.ToString(),
+ std::make_pair(std::move(request_ptr), std::move(callback))))
+ .second;
+ DCHECK(inserted) << "Duplicate id " << permission_request_id.ToString();
permission_request_manager->AddRequest(rfh, request);
}
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsTypeSet content_type_set) {
- if (!content_type_set.Contains(content_settings_type_))
- return;
-
- for (permissions::Observer& obs : permission_observers_) {
- obs.OnPermissionChanged(primary_pattern, secondary_pattern,
- content_type_set);
- }
+ NotifyObservers(primary_pattern, secondary_pattern, content_type_set);
}
void PermissionContextBase::AddObserver(
DCHECK(content_setting == CONTENT_SETTING_ALLOW ||
content_setting == CONTENT_SETTING_BLOCK);
- content_settings::ContentSettingConstraints constraints = {
- base::Time(), is_one_time ? content_settings::SessionModel::OneTime
- : content_settings::SessionModel::Durable};
+ content_settings::ContentSettingConstraints constraints;
+ constraints.set_session_model(is_one_time
+ ? content_settings::SessionModel::OneTime
+ : content_settings::SessionModel::Durable);
#if !BUILDFLAG(IS_ANDROID)
if (base::FeatureList::IsEnabled(
// 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.track_last_visit_for_autoexpiration = true;
+ constraints.set_track_last_visit_for_autoexpiration(true);
}
}
#endif // !BUILDFLAG(IS_ANDROID)
+ if (base::FeatureList::IsEnabled(permissions::features::kOneTimePermission) &&
+ is_one_time) {
+ if (base::FeatureList::IsEnabled(
+ content_settings::features::kActiveContentSettingExpiry)) {
+ constraints.set_lifetime(kOneTimePermissionMaximumLifetime);
+ }
+ }
+
PermissionsClient::Get()
->GetSettingsMap(browser_context_)
->SetContentSettingDefaultScope(requesting_origin, embedding_origin,
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