blob: 71d0ba67f3b19ae56d5ded35c88816fa82345688 [file] [log] [blame]
// Copyright 2020 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/contexts/camera_pan_tilt_zoom_permission_context.h"
#include <memory>
#include "base/feature_list.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/common/content_settings_constraints.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/permissions/features.h"
#include "components/permissions/permission_decision.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_request_id.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/permissions_client.h"
#include "components/webrtc/media_stream_device_enumerator.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/permission_controller.h"
#include "content/public/browser/permission_descriptor_util.h"
#include "content/public/browser/permission_result.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-shared.h"
#include "third_party/blink/public/common/permissions/permission_utils.h"
namespace permissions {
CameraPanTiltZoomPermissionContext::CameraPanTiltZoomPermissionContext(
content::BrowserContext* browser_context,
std::unique_ptr<Delegate> delegate,
const webrtc::MediaStreamDeviceEnumerator* device_enumerator)
: ContentSettingPermissionContextBase(
browser_context,
ContentSettingsType::CAMERA_PAN_TILT_ZOOM,
network::mojom::PermissionsPolicyFeature::kNotFound),
delegate_(std::move(delegate)),
device_enumerator_(device_enumerator) {
DCHECK(device_enumerator_);
host_content_settings_map_ =
permissions::PermissionsClient::Get()->GetSettingsMap(browser_context);
content_setting_observer_registered_by_subclass_ = true;
host_content_settings_map_->AddObserver(this);
}
CameraPanTiltZoomPermissionContext::~CameraPanTiltZoomPermissionContext() {
host_content_settings_map_->RemoveObserver(this);
}
void CameraPanTiltZoomPermissionContext::RequestPermission(
std::unique_ptr<PermissionRequestData> request_data,
permissions::BrowserPermissionCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (HasAvailableCameraPtzDevices()) {
ContentSettingPermissionContextBase::RequestPermission(
std::move(request_data), std::move(callback));
return;
}
// If there is no camera with PTZ capabilities, let's request a "regular"
// camera permission instead.
content::RenderFrameHost* render_frame_host =
content::RenderFrameHost::FromID(
request_data->id.global_render_frame_host_id());
if (request_data->requesting_origin !=
render_frame_host->GetLastCommittedOrigin().GetURL()) {
std::move(callback).Run(content::PermissionResult(
blink::mojom::PermissionStatus::DENIED,
content::PermissionStatusSource::UNSPECIFIED));
return;
}
render_frame_host->GetBrowserContext()
->GetPermissionController()
->RequestPermissionFromCurrentDocument(
render_frame_host,
content::PermissionRequestDescription(
content::PermissionDescriptorUtil::
CreatePermissionDescriptorForPermissionType(
blink::PermissionType::VIDEO_CAPTURE),
request_data->user_gesture),
std::move(callback));
}
ContentSetting
CameraPanTiltZoomPermissionContext::GetContentSettingStatusInternal(
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& embedding_origin) const {
ContentSetting result = CONTENT_SETTING_DEFAULT;
if (delegate_->GetPermissionStatusInternal(requesting_origin,
embedding_origin, &result)) {
return result;
}
return ContentSettingPermissionContextBase::GetContentSettingStatusInternal(
render_frame_host, requesting_origin, embedding_origin);
}
void CameraPanTiltZoomPermissionContext::OnContentSettingChanged(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern,
ContentSettingsTypeSet content_type_set) {
ContentSettingPermissionContextBase::OnContentSettingChanged(
primary_pattern, secondary_pattern, content_type_set);
if (!content_type_set.Contains(ContentSettingsType::MEDIASTREAM_CAMERA) &&
!content_type_set.Contains(ContentSettingsType::CAMERA_PAN_TILT_ZOOM)) {
return;
}
// Skip if the camera permission is currently being updated to match camera
// PTZ permission as OnContentSettingChanged would have been called again
// causing a reentrancy issue.
if (updating_mediastream_camera_permission_) {
updating_mediastream_camera_permission_ = false;
return;
}
// Skip if the camera PTZ permission is currently being reset when camera
// permission got blocked or reset as OnContentSettingChanged would have been
// called again causing a reentrancy issue.
if (updating_camera_ptz_permission_) {
updating_camera_ptz_permission_ = false;
return;
}
// TODO(crbug.com/40129438): We should not need to deduce the url from the
// primary pattern here. Modify the infrastructure to facilitate this
// particular use case better.
const GURL url(primary_pattern.ToString());
if (url::Origin::Create(url).opaque())
return;
ContentSetting camera_ptz_setting =
host_content_settings_map_->GetContentSetting(url, url,
content_settings_type());
if (content_type_set.Contains(ContentSettingsType::CAMERA_PAN_TILT_ZOOM)) {
// Automatically update camera permission to camera PTZ permission as any
// change to camera PTZ should be reflected to camera.
updating_mediastream_camera_permission_ = true;
content_settings::ContentSettingConstraints constraints;
// Enable last-visit tracking for eligible MEDIASTREAM_CAMERA settings.
// This allows Safety Hub to auto-revoke the permission if the site is
// not visited for a finite amount of time.
if (base::FeatureList::IsEnabled(
permissions::features::
kSafetyHubUnusedPermissionRevocationForAllSurfaces) &&
camera_ptz_setting &&
content_settings::CanBeAutoRevokedAsUnusedPermission(
ContentSettingsType::MEDIASTREAM_CAMERA,
content_settings::ContentSettingToValue(camera_ptz_setting))) {
constraints.set_track_last_visit_for_autoexpiration(true);
}
host_content_settings_map_->SetContentSettingCustomScope(
primary_pattern, secondary_pattern,
ContentSettingsType::MEDIASTREAM_CAMERA, camera_ptz_setting,
constraints);
return;
}
// Don't reset camera PTZ permission if it is already blocked or in a
// "default" state.
if (camera_ptz_setting == CONTENT_SETTING_BLOCK ||
camera_ptz_setting == CONTENT_SETTING_ASK) {
return;
}
ContentSetting mediastream_camera_setting =
host_content_settings_map_->GetContentSetting(
url, url, ContentSettingsType::MEDIASTREAM_CAMERA);
if (mediastream_camera_setting == CONTENT_SETTING_BLOCK ||
mediastream_camera_setting == CONTENT_SETTING_ASK) {
// Automatically reset camera PTZ permission if camera permission
// gets blocked or reset.
updating_camera_ptz_permission_ = true;
host_content_settings_map_->SetContentSettingCustomScope(
primary_pattern, secondary_pattern,
ContentSettingsType::CAMERA_PAN_TILT_ZOOM, CONTENT_SETTING_DEFAULT);
}
}
bool CameraPanTiltZoomPermissionContext::HasAvailableCameraPtzDevices() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
const std::vector<blink::MediaStreamDevice> devices =
device_enumerator_->GetVideoCaptureDevices();
for (const blink::MediaStreamDevice& device : devices) {
if (device.video_control_support.pan || device.video_control_support.tilt ||
device.video_control_support.zoom) {
return true;
}
}
return false;
}
} // namespace permissions