media_stream_devices_controller.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
1// Copyright (c) 2012 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/media_stream_devices_controller.h" 6 7#include "base/command_line.h" 8#include "base/prefs/pref_service.h" 9#include "base/values.h" 10#include "chrome/browser/content_settings/content_settings_provider.h" 11#include "chrome/browser/content_settings/host_content_settings_map.h" 12#include "chrome/browser/content_settings/tab_specific_content_settings.h" 13#include "chrome/browser/media/media_capture_devices_dispatcher.h" 14#include "chrome/browser/media/media_stream_capture_indicator.h" 15#include "chrome/browser/prefs/scoped_user_pref_update.h" 16#include "chrome/browser/profiles/profile.h" 17#include "chrome/browser/ui/browser.h" 18#include "chrome/common/chrome_switches.h" 19#include "chrome/common/content_settings.h" 20#include "chrome/common/pref_names.h" 21#include "components/user_prefs/pref_registry_syncable.h" 22#include "content/public/browser/browser_thread.h" 23#include "content/public/common/media_stream_request.h" 24 25using content::BrowserThread; 26 27namespace { 28 29bool HasAnyAvailableDevice() { 30 const content::MediaStreamDevices& audio_devices = 31 MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices(); 32 const content::MediaStreamDevices& video_devices = 33 MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices(); 34 35 return !audio_devices.empty() || !video_devices.empty(); 36}; 37 38} // namespace 39 40MediaStreamDevicesController::MediaStreamDevicesController( 41 content::WebContents* web_contents, 42 const content::MediaStreamRequest& request, 43 const content::MediaResponseCallback& callback) 44 : web_contents_(web_contents), 45 request_(request), 46 callback_(callback), 47 microphone_requested_( 48 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE), 49 webcam_requested_( 50 request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) { 51 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext()); 52 content_settings_ = TabSpecificContentSettings::FromWebContents(web_contents); 53 54 // Don't call GetDevicePolicy from the initializer list since the 55 // implementation depends on member variables. 56 if (microphone_requested_ && 57 GetDevicePolicy(prefs::kAudioCaptureAllowed) == ALWAYS_DENY) { 58 microphone_requested_ = false; 59 } 60 61 if (webcam_requested_ && 62 GetDevicePolicy(prefs::kVideoCaptureAllowed) == ALWAYS_DENY) { 63 webcam_requested_ = false; 64 } 65} 66 67MediaStreamDevicesController::~MediaStreamDevicesController() {} 68 69// static 70void MediaStreamDevicesController::RegisterUserPrefs( 71 user_prefs::PrefRegistrySyncable* prefs) { 72 prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed, 73 true, 74 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 75 prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed, 76 true, 77 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 78} 79 80 81bool MediaStreamDevicesController::DismissInfoBarAndTakeActionOnSettings() { 82 // If this is a no UI check for policies only go straight to accept - policy 83 // check will be done automatically on the way. 84 if (request_.request_type == content::MEDIA_OPEN_DEVICE) { 85 Accept(false); 86 return true; 87 } 88 89 // Tab capture is allowed for extensions only and infobar is not shown for 90 // extensions. 91 if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE || 92 request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE) { 93 Deny(false); 94 return true; 95 } 96 97 // Deny the request if the security origin is empty, this happens with 98 // file access without |--allow-file-access-from-files| flag. 99 if (request_.security_origin.is_empty()) { 100 Deny(false); 101 return true; 102 } 103 104 // Deny the request if there is no device attached to the OS. 105 if (!HasAnyAvailableDevice()) { 106 Deny(false); 107 return true; 108 } 109 110 // Check if any allow exception has been made for this request. 111 if (IsRequestAllowedByDefault()) { 112 Accept(false); 113 return true; 114 } 115 116 // Check if any block exception has been made for this request. 117 if (IsRequestBlockedByDefault()) { 118 Deny(false); 119 return true; 120 } 121 122 // Check if the media default setting is set to block. 123 if (IsDefaultMediaAccessBlocked()) { 124 Deny(false); 125 return true; 126 } 127 128 // Show the infobar. 129 return false; 130} 131 132const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const { 133 return request_.security_origin.spec(); 134} 135 136void MediaStreamDevicesController::Accept(bool update_content_setting) { 137 if (content_settings_) 138 content_settings_->OnMediaStreamAllowed(); 139 140 // Get the default devices for the request. 141 content::MediaStreamDevices devices; 142 if (microphone_requested_ || webcam_requested_) { 143 switch (request_.request_type) { 144 case content::MEDIA_OPEN_DEVICE: 145 // For open device request pick the desired device or fall back to the 146 // first available of the given type. 147 MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedDevice( 148 request_.requested_device_id, 149 microphone_requested_, 150 webcam_requested_, 151 &devices); 152 break; 153 case content::MEDIA_DEVICE_ACCESS: 154 case content::MEDIA_GENERATE_STREAM: 155 case content::MEDIA_ENUMERATE_DEVICES: 156 // Get the default devices for the request. 157 MediaCaptureDevicesDispatcher::GetInstance()-> 158 GetDefaultDevicesForProfile(profile_, 159 microphone_requested_, 160 webcam_requested_, 161 &devices); 162 break; 163 } 164 165 if (update_content_setting && IsSchemeSecure() && !devices.empty()) 166 SetPermission(true); 167 } 168 169 scoped_ptr<content::MediaStreamUI> ui; 170 if (!devices.empty()) { 171 ui = MediaCaptureDevicesDispatcher::GetInstance()-> 172 GetMediaStreamCaptureIndicator()->RegisterMediaStream( 173 web_contents_, devices); 174 } 175 callback_.Run(devices, ui.Pass()); 176} 177 178void MediaStreamDevicesController::Deny(bool update_content_setting) { 179 // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the 180 // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and 181 // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA. 182 if (content_settings_) { 183 content_settings_->OnContentBlocked(CONTENT_SETTINGS_TYPE_MEDIASTREAM, 184 std::string()); 185 } 186 187 if (update_content_setting) 188 SetPermission(false); 189 190 callback_.Run(content::MediaStreamDevices(), 191 scoped_ptr<content::MediaStreamUI>()); 192} 193 194MediaStreamDevicesController::DevicePolicy 195MediaStreamDevicesController::GetDevicePolicy(const char* policy_name) const { 196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 197 198 PrefService* prefs = profile_->GetPrefs(); 199 if (!prefs->IsManagedPreference(policy_name)) 200 return POLICY_NOT_SET; 201 202 return prefs->GetBoolean(policy_name) ? ALWAYS_ALLOW : ALWAYS_DENY; 203} 204 205bool MediaStreamDevicesController::IsRequestAllowedByDefault() const { 206 // The request from internal objects like chrome://URLs is always allowed. 207 if (ShouldAlwaysAllowOrigin()) 208 return true; 209 210 struct { 211 bool has_capability; 212 const char* policy_name; 213 ContentSettingsType settings_type; 214 } device_checks[] = { 215 { microphone_requested_, prefs::kAudioCaptureAllowed, 216 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC }, 217 { webcam_requested_, prefs::kVideoCaptureAllowed, 218 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA }, 219 }; 220 221 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(device_checks); ++i) { 222 if (!device_checks[i].has_capability) 223 continue; 224 225 DevicePolicy policy = GetDevicePolicy(device_checks[i].policy_name); 226 if (policy == ALWAYS_DENY || 227 (policy == POLICY_NOT_SET && 228 profile_->GetHostContentSettingsMap()->GetContentSetting( 229 request_.security_origin, request_.security_origin, 230 device_checks[i].settings_type, NO_RESOURCE_IDENTIFIER) != 231 CONTENT_SETTING_ALLOW)) { 232 return false; 233 } 234 // If we get here, then either policy is set to ALWAYS_ALLOW or the content 235 // settings allow the request by default. 236 } 237 238 return true; 239} 240 241bool MediaStreamDevicesController::IsRequestBlockedByDefault() const { 242 if (microphone_requested_ && 243 profile_->GetHostContentSettingsMap()->GetContentSetting( 244 request_.security_origin, 245 request_.security_origin, 246 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, 247 NO_RESOURCE_IDENTIFIER) != CONTENT_SETTING_BLOCK) { 248 return false; 249 } 250 251 if (webcam_requested_ && 252 profile_->GetHostContentSettingsMap()->GetContentSetting( 253 request_.security_origin, 254 request_.security_origin, 255 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, 256 NO_RESOURCE_IDENTIFIER) != CONTENT_SETTING_BLOCK) { 257 return false; 258 } 259 260 return true; 261} 262 263bool MediaStreamDevicesController::IsDefaultMediaAccessBlocked() const { 264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 265 // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the 266 // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and 267 // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA. 268 ContentSetting current_setting = 269 profile_->GetHostContentSettingsMap()->GetDefaultContentSetting( 270 CONTENT_SETTINGS_TYPE_MEDIASTREAM, NULL); 271 return (current_setting == CONTENT_SETTING_BLOCK); 272} 273 274bool MediaStreamDevicesController::IsSchemeSecure() const { 275 return (request_.security_origin.SchemeIsSecure()); 276} 277 278bool MediaStreamDevicesController::ShouldAlwaysAllowOrigin() const { 279 // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the 280 // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and 281 // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA. 282 return profile_->GetHostContentSettingsMap()->ShouldAllowAllContent( 283 request_.security_origin, request_.security_origin, 284 CONTENT_SETTINGS_TYPE_MEDIASTREAM); 285} 286 287void MediaStreamDevicesController::SetPermission(bool allowed) const { 288 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 289#if defined(OS_ANDROID) 290 // We do not support sticky operations on Android yet. 291 return; 292#endif 293 ContentSettingsPattern primary_pattern = 294 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin); 295 // Check the pattern is valid or not. When the request is from a file access, 296 // no exception will be made. 297 if (!primary_pattern.IsValid()) 298 return; 299 300 ContentSetting content_setting = allowed ? 301 CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; 302 if (microphone_requested_) { 303 profile_->GetHostContentSettingsMap()->SetContentSetting( 304 primary_pattern, 305 ContentSettingsPattern::Wildcard(), 306 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, 307 std::string(), 308 content_setting); 309 } 310 if (webcam_requested_) { 311 profile_->GetHostContentSettingsMap()->SetContentSetting( 312 primary_pattern, 313 ContentSettingsPattern::Wildcard(), 314 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, 315 std::string(), 316 content_setting); 317 } 318} 319