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