1 // Copyright 2020 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.
4
5 #include "chrome/browser/media/webrtc/camera_pan_tilt_zoom_permission_context.h"
6
7 #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
8 #include "chrome/browser/permissions/permission_manager_factory.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "components/permissions/permission_manager.h"
11 #include "components/permissions/permission_request_id.h"
12 #include "components/permissions/permissions_client.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom-shared.h"
15
CameraPanTiltZoomPermissionContext(content::BrowserContext * browser_context)16 CameraPanTiltZoomPermissionContext::CameraPanTiltZoomPermissionContext(
17 content::BrowserContext* browser_context)
18 : PermissionContextBase(browser_context,
19 ContentSettingsType::CAMERA_PAN_TILT_ZOOM,
20 blink::mojom::FeaturePolicyFeature::kNotFound) {
21 host_content_settings_map_ =
22 permissions::PermissionsClient::Get()->GetSettingsMap(browser_context);
23 host_content_settings_map_->AddObserver(this);
24 }
25
~CameraPanTiltZoomPermissionContext()26 CameraPanTiltZoomPermissionContext::~CameraPanTiltZoomPermissionContext() {
27 host_content_settings_map_->RemoveObserver(this);
28 }
29
RequestPermission(content::WebContents * web_contents,const permissions::PermissionRequestID & id,const GURL & requesting_frame_origin,bool user_gesture,permissions::BrowserPermissionCallback callback)30 void CameraPanTiltZoomPermissionContext::RequestPermission(
31 content::WebContents* web_contents,
32 const permissions::PermissionRequestID& id,
33 const GURL& requesting_frame_origin,
34 bool user_gesture,
35 permissions::BrowserPermissionCallback callback) {
36 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
37
38 if (HasAvailableCameraPtzDevices()) {
39 PermissionContextBase::RequestPermission(web_contents, id,
40 requesting_frame_origin,
41 user_gesture, std::move(callback));
42 return;
43 }
44
45 // If there is no camera with PTZ capabilities, let's request a "regular"
46 // camera permission instead.
47 content::RenderFrameHost* frame = content::RenderFrameHost::FromID(
48 id.render_process_id(), id.render_frame_id());
49 permissions::PermissionManager* permission_manager =
50 PermissionManagerFactory::GetForProfile(
51 Profile::FromBrowserContext(web_contents->GetBrowserContext()));
52 permission_manager->RequestPermission(ContentSettingsType::MEDIASTREAM_CAMERA,
53 frame, requesting_frame_origin,
54 user_gesture, std::move(callback));
55 }
56
57 #if defined(OS_ANDROID)
GetPermissionStatusInternal(content::RenderFrameHost * render_frame_host,const GURL & requesting_origin,const GURL & embedding_origin) const58 ContentSetting CameraPanTiltZoomPermissionContext::GetPermissionStatusInternal(
59 content::RenderFrameHost* render_frame_host,
60 const GURL& requesting_origin,
61 const GURL& embedding_origin) const {
62 // The PTZ permission is automatically granted on Android. It is safe to do so
63 // because pan and tilt are not supported on Android.
64 return CONTENT_SETTING_ALLOW;
65 }
66 #endif
67
IsRestrictedToSecureOrigins() const68 bool CameraPanTiltZoomPermissionContext::IsRestrictedToSecureOrigins() const {
69 return true;
70 }
71
OnContentSettingChanged(const ContentSettingsPattern & primary_pattern,const ContentSettingsPattern & secondary_pattern,ContentSettingsType content_type)72 void CameraPanTiltZoomPermissionContext::OnContentSettingChanged(
73 const ContentSettingsPattern& primary_pattern,
74 const ContentSettingsPattern& secondary_pattern,
75 ContentSettingsType content_type) {
76 if (content_type != ContentSettingsType::MEDIASTREAM_CAMERA &&
77 content_type != ContentSettingsType::CAMERA_PAN_TILT_ZOOM) {
78 return;
79 }
80
81 // Skip if the camera permission is currently being updated to match camera
82 // PTZ permission as OnContentSettingChanged would have been called again
83 // causing a reentrancy issue.
84 if (updating_mediastream_camera_permission_) {
85 updating_mediastream_camera_permission_ = false;
86 return;
87 }
88
89 // Skip if the camera PTZ permission is currently being reset when camera
90 // permission got blocked or reset as OnContentSettingChanged would have been
91 // called again causing a reentrancy issue.
92 if (updating_camera_ptz_permission_) {
93 updating_camera_ptz_permission_ = false;
94 return;
95 }
96
97 // TODO(crbug.com/1078272): We should not need to deduce the url from the
98 // primary pattern here. Modify the infrastructure to facilitate this
99 // particular use case better.
100 const GURL url(primary_pattern.ToString());
101 if (url::Origin::Create(url).opaque())
102 return;
103
104 ContentSetting camera_ptz_setting =
105 host_content_settings_map_->GetContentSetting(url, url,
106 content_settings_type());
107
108 if (content_type == ContentSettingsType::CAMERA_PAN_TILT_ZOOM) {
109 // Automatically update camera permission to camera PTZ permission as any
110 // change to camera PTZ should be reflected to camera.
111 updating_mediastream_camera_permission_ = true;
112 host_content_settings_map_->SetContentSettingCustomScope(
113 primary_pattern, secondary_pattern,
114 ContentSettingsType::MEDIASTREAM_CAMERA, camera_ptz_setting);
115 return;
116 }
117
118 // Don't reset camera PTZ permission if it is already blocked or in a
119 // "default" state.
120 if (camera_ptz_setting == CONTENT_SETTING_BLOCK ||
121 camera_ptz_setting == CONTENT_SETTING_ASK) {
122 return;
123 }
124
125 ContentSetting mediastream_camera_setting =
126 host_content_settings_map_->GetContentSetting(url, url, content_type);
127 if (mediastream_camera_setting == CONTENT_SETTING_BLOCK ||
128 mediastream_camera_setting == CONTENT_SETTING_ASK) {
129 // Automatically reset camera PTZ permission if camera permission
130 // gets blocked or reset.
131 updating_camera_ptz_permission_ = true;
132 host_content_settings_map_->SetContentSettingCustomScope(
133 primary_pattern, secondary_pattern,
134 ContentSettingsType::CAMERA_PAN_TILT_ZOOM, CONTENT_SETTING_DEFAULT);
135 }
136 }
137
HasAvailableCameraPtzDevices() const138 bool CameraPanTiltZoomPermissionContext::HasAvailableCameraPtzDevices() const {
139 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
140
141 const std::vector<blink::MediaStreamDevice> devices =
142 MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices();
143 for (const blink::MediaStreamDevice& device : devices) {
144 if (device.video_control_support.pan || device.video_control_support.tilt ||
145 device.video_control_support.zoom) {
146 return true;
147 }
148 }
149 return false;
150 }
151