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/prefs/scoped_user_pref_update.h" 10 #include "base/values.h" 11 #include "chrome/browser/content_settings/content_settings_provider.h" 12 #include "chrome/browser/content_settings/host_content_settings_map.h" 13 #include "chrome/browser/content_settings/tab_specific_content_settings.h" 14 #include "chrome/browser/media/media_capture_devices_dispatcher.h" 15 #include "chrome/browser/media/media_stream_capture_indicator.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/content_settings_pattern.h" 21 #include "chrome/common/pref_names.h" 22 #include "components/user_prefs/pref_registry_syncable.h" 23 #include "content/public/browser/browser_thread.h" 24 #include "content/public/common/media_stream_request.h" 25 #include "extensions/common/constants.h" 26 27 #if defined(OS_CHROMEOS) 28 #include "chrome/browser/chromeos/login/user_manager.h" 29 #endif 30 31 using content::BrowserThread; 32 33 namespace { 34 35 bool HasAnyAvailableDevice() { 36 const content::MediaStreamDevices& audio_devices = 37 MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices(); 38 const content::MediaStreamDevices& video_devices = 39 MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices(); 40 41 return !audio_devices.empty() || !video_devices.empty(); 42 } 43 44 bool IsInKioskMode() { 45 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode)) 46 return true; 47 48 #if defined(OS_CHROMEOS) 49 const chromeos::UserManager* user_manager = chromeos::UserManager::Get(); 50 return user_manager && user_manager->IsLoggedInAsKioskApp(); 51 #else 52 return false; 53 #endif 54 } 55 56 } // namespace 57 58 MediaStreamDevicesController::MediaStreamTypeSettings::MediaStreamTypeSettings( 59 Permission permission, const std::string& requested_device_id): 60 permission(permission), requested_device_id(requested_device_id) {} 61 62 MediaStreamDevicesController::MediaStreamTypeSettings:: 63 MediaStreamTypeSettings(): permission(MEDIA_NONE) {} 64 65 MediaStreamDevicesController::MediaStreamTypeSettings:: 66 ~MediaStreamTypeSettings() {} 67 68 MediaStreamDevicesController::MediaStreamDevicesController( 69 content::WebContents* web_contents, 70 const content::MediaStreamRequest& request, 71 const content::MediaResponseCallback& callback) 72 : web_contents_(web_contents), 73 request_(request), 74 callback_(callback) { 75 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext()); 76 content_settings_ = TabSpecificContentSettings::FromWebContents(web_contents); 77 78 // For MEDIA_OPEN_DEVICE requests (Pepper) we always request both webcam 79 // and microphone to avoid popping two infobars. 80 // We start with setting the requested media type to allowed or blocked 81 // depending on the policy. If not blocked by policy it may be blocked later 82 // in the two remaining filtering steps (by user setting or by user when 83 // clicking the infobar). 84 // TODO(grunell): It's not the nicest solution to let the MEDIA_OPEN_DEVICE 85 // case take a ride on the MEDIA_DEVICE_*_CAPTURE permission. Should be fixed. 86 if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE || 87 request.request_type == content::MEDIA_OPEN_DEVICE) { 88 if (GetDevicePolicy(prefs::kAudioCaptureAllowed, 89 prefs::kAudioCaptureAllowedUrls) == ALWAYS_DENY) { 90 request_permissions_.insert(std::make_pair( 91 content::MEDIA_DEVICE_AUDIO_CAPTURE, 92 MediaStreamTypeSettings(MEDIA_BLOCKED_BY_POLICY, 93 request.requested_audio_device_id))); 94 } else { 95 request_permissions_.insert(std::make_pair( 96 content::MEDIA_DEVICE_AUDIO_CAPTURE, 97 MediaStreamTypeSettings(MEDIA_ALLOWED, 98 request.requested_audio_device_id))); 99 } 100 } 101 if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE || 102 request.request_type == content::MEDIA_OPEN_DEVICE) { 103 if (GetDevicePolicy(prefs::kVideoCaptureAllowed, 104 prefs::kVideoCaptureAllowedUrls) == ALWAYS_DENY) { 105 request_permissions_.insert(std::make_pair( 106 content::MEDIA_DEVICE_VIDEO_CAPTURE, 107 MediaStreamTypeSettings(MEDIA_BLOCKED_BY_POLICY, 108 request.requested_video_device_id))); 109 } else { 110 request_permissions_.insert(std::make_pair( 111 content::MEDIA_DEVICE_VIDEO_CAPTURE, 112 MediaStreamTypeSettings(MEDIA_ALLOWED, 113 request.requested_video_device_id))); 114 } 115 } 116 } 117 118 MediaStreamDevicesController::~MediaStreamDevicesController() { 119 if (!callback_.is_null()) { 120 callback_.Run(content::MediaStreamDevices(), 121 scoped_ptr<content::MediaStreamUI>()); 122 } 123 } 124 125 // static 126 void MediaStreamDevicesController::RegisterProfilePrefs( 127 user_prefs::PrefRegistrySyncable* prefs) { 128 prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed, 129 true, 130 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 131 prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed, 132 true, 133 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 134 prefs->RegisterListPref(prefs::kVideoCaptureAllowedUrls, 135 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 136 prefs->RegisterListPref(prefs::kAudioCaptureAllowedUrls, 137 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 138 } 139 140 141 bool MediaStreamDevicesController::DismissInfoBarAndTakeActionOnSettings() { 142 // Tab capture is allowed for extensions only and infobar is not shown for 143 // extensions. 144 if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE || 145 request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE) { 146 Deny(false); 147 return true; 148 } 149 150 // Deny the request if the security origin is empty, this happens with 151 // file access without |--allow-file-access-from-files| flag. 152 if (request_.security_origin.is_empty()) { 153 Deny(false); 154 return true; 155 } 156 157 // Deny the request if there is no device attached to the OS. 158 if (!HasAnyAvailableDevice()) { 159 Deny(false); 160 return true; 161 } 162 163 // Check if any allow exception has been made for this request. 164 if (IsRequestAllowedByDefault()) { 165 Accept(false); 166 return true; 167 } 168 169 // Filter any parts of the request that have been blocked by default and deny 170 // it if nothing is left to accept. 171 if (FilterBlockedByDefaultDevices() == 0) { 172 Deny(false); 173 return true; 174 } 175 176 // Check if the media default setting is set to block. 177 if (IsDefaultMediaAccessBlocked()) { 178 Deny(false); 179 return true; 180 } 181 182 if (request_.request_type == content::MEDIA_OPEN_DEVICE) { 183 bool no_matched_audio_device = 184 (request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE && 185 !request_.requested_audio_device_id.empty() && 186 MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedAudioDevice( 187 request_.requested_audio_device_id) == NULL); 188 bool no_matched_video_device = 189 (request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE && 190 !request_.requested_video_device_id.empty() && 191 MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedVideoDevice( 192 request_.requested_video_device_id) == NULL); 193 if (no_matched_audio_device || no_matched_video_device) { 194 Deny(false); 195 return true; 196 } 197 } 198 199 // Show the infobar. 200 return false; 201 } 202 203 bool MediaStreamDevicesController::HasAudio() const { 204 return IsDeviceAudioCaptureRequestedAndAllowed(); 205 } 206 207 bool MediaStreamDevicesController::HasVideo() const { 208 return IsDeviceVideoCaptureRequestedAndAllowed(); 209 } 210 211 const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const { 212 return request_.security_origin.spec(); 213 } 214 215 void MediaStreamDevicesController::Accept(bool update_content_setting) { 216 NotifyUIRequestAccepted(); 217 218 // Get the default devices for the request. 219 content::MediaStreamDevices devices; 220 bool audio_allowed = IsDeviceAudioCaptureRequestedAndAllowed(); 221 bool video_allowed = IsDeviceVideoCaptureRequestedAndAllowed(); 222 if (audio_allowed || video_allowed) { 223 switch (request_.request_type) { 224 case content::MEDIA_OPEN_DEVICE: { 225 const content::MediaStreamDevice* device = NULL; 226 // For open device request, when requested device_id is empty, pick 227 // the first available of the given type. If requested device_id is 228 // not empty, return the desired device if it's available. Otherwise, 229 // return no device. 230 if (audio_allowed && 231 request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) { 232 if (!request_.requested_audio_device_id.empty()) { 233 device = MediaCaptureDevicesDispatcher::GetInstance()-> 234 GetRequestedAudioDevice(request_.requested_audio_device_id); 235 } else { 236 device = MediaCaptureDevicesDispatcher::GetInstance()-> 237 GetFirstAvailableAudioDevice(); 238 } 239 } else if (video_allowed && 240 request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) { 241 // Pepper API opens only one device at a time. 242 if (!request_.requested_video_device_id.empty()) { 243 device = MediaCaptureDevicesDispatcher::GetInstance()-> 244 GetRequestedVideoDevice(request_.requested_video_device_id); 245 } else { 246 device = MediaCaptureDevicesDispatcher::GetInstance()-> 247 GetFirstAvailableVideoDevice(); 248 } 249 } 250 if (device) 251 devices.push_back(*device); 252 break; 253 } 254 case content::MEDIA_GENERATE_STREAM: { 255 bool get_default_audio_device = audio_allowed; 256 bool get_default_video_device = video_allowed; 257 258 // Get the exact audio or video device if an id is specified. 259 if (audio_allowed && !request_.requested_audio_device_id.empty()) { 260 const content::MediaStreamDevice* audio_device = 261 MediaCaptureDevicesDispatcher::GetInstance()-> 262 GetRequestedAudioDevice(request_.requested_audio_device_id); 263 if (audio_device) { 264 devices.push_back(*audio_device); 265 get_default_audio_device = false; 266 } 267 } 268 if (video_allowed && !request_.requested_video_device_id.empty()) { 269 const content::MediaStreamDevice* video_device = 270 MediaCaptureDevicesDispatcher::GetInstance()-> 271 GetRequestedVideoDevice(request_.requested_video_device_id); 272 if (video_device) { 273 devices.push_back(*video_device); 274 get_default_video_device = false; 275 } 276 } 277 278 // If either or both audio and video devices were requested but not 279 // specified by id, get the default devices. 280 if (get_default_audio_device || get_default_video_device) { 281 MediaCaptureDevicesDispatcher::GetInstance()-> 282 GetDefaultDevicesForProfile(profile_, 283 get_default_audio_device, 284 get_default_video_device, 285 &devices); 286 } 287 break; 288 } 289 case content::MEDIA_DEVICE_ACCESS: { 290 // Get the default devices for the request. 291 MediaCaptureDevicesDispatcher::GetInstance()-> 292 GetDefaultDevicesForProfile(profile_, 293 audio_allowed, 294 video_allowed, 295 &devices); 296 break; 297 } 298 case content::MEDIA_ENUMERATE_DEVICES: { 299 // Do nothing. 300 NOTREACHED(); 301 break; 302 } 303 } // switch 304 305 // TODO(raymes): We currently set the content permission for non-https 306 // websites for Pepper requests as well. This is temporary and should be 307 // removed. 308 if (update_content_setting) { 309 if ((IsSchemeSecure() && !devices.empty()) || 310 request_.request_type == content::MEDIA_OPEN_DEVICE) { 311 SetPermission(true); 312 } 313 } 314 } 315 316 scoped_ptr<content::MediaStreamUI> ui; 317 if (!devices.empty()) { 318 ui = MediaCaptureDevicesDispatcher::GetInstance()-> 319 GetMediaStreamCaptureIndicator()->RegisterMediaStream( 320 web_contents_, devices); 321 } 322 content::MediaResponseCallback cb = callback_; 323 callback_.Reset(); 324 cb.Run(devices, ui.Pass()); 325 } 326 327 void MediaStreamDevicesController::Deny(bool update_content_setting) { 328 NotifyUIRequestDenied(); 329 330 if (update_content_setting) 331 SetPermission(false); 332 333 content::MediaResponseCallback cb = callback_; 334 callback_.Reset(); 335 cb.Run(content::MediaStreamDevices(), scoped_ptr<content::MediaStreamUI>()); 336 } 337 338 MediaStreamDevicesController::DevicePolicy 339 MediaStreamDevicesController::GetDevicePolicy( 340 const char* policy_name, 341 const char* whitelist_policy_name) const { 342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 343 344 // If the security origin policy matches a value in the whitelist, allow it. 345 // Otherwise, check the |policy_name| master switch for the default behavior. 346 347 PrefService* prefs = profile_->GetPrefs(); 348 349 // TODO(tommi): Remove the kiosk mode check when the whitelist below 350 // is visible in the media exceptions UI. 351 // See discussion here: https://codereview.chromium.org/15738004/ 352 if (IsInKioskMode()) { 353 const base::ListValue* list = prefs->GetList(whitelist_policy_name); 354 std::string value; 355 for (size_t i = 0; i < list->GetSize(); ++i) { 356 if (list->GetString(i, &value)) { 357 ContentSettingsPattern pattern = 358 ContentSettingsPattern::FromString(value); 359 if (pattern == ContentSettingsPattern::Wildcard()) { 360 DLOG(WARNING) << "Ignoring wildcard URL pattern: " << value; 361 continue; 362 } 363 DLOG_IF(ERROR, !pattern.IsValid()) << "Invalid URL pattern: " << value; 364 if (pattern.IsValid() && pattern.Matches(request_.security_origin)) 365 return ALWAYS_ALLOW; 366 } 367 } 368 } 369 370 // If a match was not found, check if audio capture is otherwise disallowed 371 // or if the user should be prompted. Setting the policy value to "true" 372 // is equal to not setting it at all, so from hereon out, we will return 373 // either POLICY_NOT_SET (prompt) or ALWAYS_DENY (no prompt, no access). 374 if (!prefs->GetBoolean(policy_name)) 375 return ALWAYS_DENY; 376 377 return POLICY_NOT_SET; 378 } 379 380 bool MediaStreamDevicesController::IsRequestAllowedByDefault() const { 381 // The request from internal objects like chrome://URLs is always allowed. 382 if (ShouldAlwaysAllowOrigin()) 383 return true; 384 385 struct { 386 bool has_capability; 387 const char* policy_name; 388 const char* list_policy_name; 389 ContentSettingsType settings_type; 390 } device_checks[] = { 391 { IsDeviceAudioCaptureRequestedAndAllowed(), prefs::kAudioCaptureAllowed, 392 prefs::kAudioCaptureAllowedUrls, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC }, 393 { IsDeviceVideoCaptureRequestedAndAllowed(), prefs::kVideoCaptureAllowed, 394 prefs::kVideoCaptureAllowedUrls, 395 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA }, 396 }; 397 398 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(device_checks); ++i) { 399 if (!device_checks[i].has_capability) 400 continue; 401 402 DevicePolicy policy = GetDevicePolicy(device_checks[i].policy_name, 403 device_checks[i].list_policy_name); 404 405 if (policy == ALWAYS_DENY) 406 return false; 407 408 if (policy == POLICY_NOT_SET) { 409 // Only load content settings from secure origins unless it is a 410 // content::MEDIA_OPEN_DEVICE (Pepper) request. 411 if (!IsSchemeSecure() && 412 request_.request_type != content::MEDIA_OPEN_DEVICE) { 413 return false; 414 } 415 if (profile_->GetHostContentSettingsMap()->GetContentSetting( 416 request_.security_origin, request_.security_origin, 417 device_checks[i].settings_type, NO_RESOURCE_IDENTIFIER) != 418 CONTENT_SETTING_ALLOW) { 419 return false; 420 } 421 } 422 // If we get here, then either policy is set to ALWAYS_ALLOW or the content 423 // settings allow the request by default. 424 } 425 426 return true; 427 } 428 429 int MediaStreamDevicesController::FilterBlockedByDefaultDevices() { 430 int requested_devices = 0; 431 432 if (IsDeviceAudioCaptureRequestedAndAllowed()) { 433 if (profile_->GetHostContentSettingsMap()->GetContentSetting( 434 request_.security_origin, 435 request_.security_origin, 436 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, 437 NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_BLOCK) { 438 request_permissions_[content::MEDIA_DEVICE_AUDIO_CAPTURE].permission = 439 MEDIA_BLOCKED_BY_USER_SETTING; 440 } else { 441 ++requested_devices; 442 } 443 } 444 445 if (IsDeviceVideoCaptureRequestedAndAllowed()) { 446 if (profile_->GetHostContentSettingsMap()->GetContentSetting( 447 request_.security_origin, 448 request_.security_origin, 449 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, 450 NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_BLOCK) { 451 request_permissions_[content::MEDIA_DEVICE_VIDEO_CAPTURE].permission = 452 MEDIA_BLOCKED_BY_USER_SETTING; 453 } else { 454 ++requested_devices; 455 } 456 } 457 458 return requested_devices; 459 } 460 461 bool MediaStreamDevicesController::IsDefaultMediaAccessBlocked() const { 462 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 463 // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the 464 // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and 465 // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA. 466 ContentSetting current_setting = 467 profile_->GetHostContentSettingsMap()->GetDefaultContentSetting( 468 CONTENT_SETTINGS_TYPE_MEDIASTREAM, NULL); 469 return (current_setting == CONTENT_SETTING_BLOCK); 470 } 471 472 bool MediaStreamDevicesController::IsSchemeSecure() const { 473 return request_.security_origin.SchemeIsSecure() || 474 request_.security_origin.SchemeIs(extensions::kExtensionScheme) || 475 CommandLine::ForCurrentProcess()->HasSwitch( 476 switches::kDisableUserMediaSecurity); 477 } 478 479 bool MediaStreamDevicesController::ShouldAlwaysAllowOrigin() const { 480 // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the 481 // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and 482 // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA. 483 return profile_->GetHostContentSettingsMap()->ShouldAllowAllContent( 484 request_.security_origin, request_.security_origin, 485 CONTENT_SETTINGS_TYPE_MEDIASTREAM); 486 } 487 488 void MediaStreamDevicesController::SetPermission(bool allowed) const { 489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 490 ContentSettingsPattern primary_pattern = 491 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin); 492 // Check the pattern is valid or not. When the request is from a file access, 493 // no exception will be made. 494 if (!primary_pattern.IsValid()) 495 return; 496 497 ContentSetting content_setting = allowed ? 498 CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; 499 if (request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE) != 500 request_permissions_.end()) { 501 profile_->GetHostContentSettingsMap()->SetContentSetting( 502 primary_pattern, 503 ContentSettingsPattern::Wildcard(), 504 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, 505 std::string(), 506 content_setting); 507 } 508 if (request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE) != 509 request_permissions_.end()) { 510 profile_->GetHostContentSettingsMap()->SetContentSetting( 511 primary_pattern, 512 ContentSettingsPattern::Wildcard(), 513 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, 514 std::string(), 515 content_setting); 516 } 517 } 518 519 void MediaStreamDevicesController::NotifyUIRequestAccepted() const { 520 if (!content_settings_) 521 return; 522 523 content_settings_->OnMediaStreamPermissionSet(request_.security_origin, 524 request_permissions_); 525 } 526 527 void MediaStreamDevicesController::NotifyUIRequestDenied() { 528 if (!content_settings_) 529 return; 530 531 if (IsDeviceAudioCaptureRequestedAndAllowed()) { 532 request_permissions_[content::MEDIA_DEVICE_AUDIO_CAPTURE].permission = 533 MEDIA_BLOCKED_BY_USER; 534 } 535 if (IsDeviceVideoCaptureRequestedAndAllowed()) { 536 request_permissions_[content::MEDIA_DEVICE_VIDEO_CAPTURE].permission = 537 MEDIA_BLOCKED_BY_USER; 538 } 539 540 content_settings_->OnMediaStreamPermissionSet(request_.security_origin, 541 request_permissions_); 542 } 543 544 bool MediaStreamDevicesController::IsDeviceAudioCaptureRequestedAndAllowed() 545 const { 546 MediaStreamTypeSettingsMap::const_iterator it = 547 request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE); 548 return (it != request_permissions_.end() && 549 it->second.permission == MEDIA_ALLOWED); 550 } 551 552 bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed() 553 const { 554 MediaStreamTypeSettingsMap::const_iterator it = 555 request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE); 556 return (it != request_permissions_.end() && 557 it->second.permission == MEDIA_ALLOWED); 558 } 559