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_capture_devices_dispatcher.h" 6 7 #include "apps/app_window.h" 8 #include "apps/app_window_registry.h" 9 #include "base/command_line.h" 10 #include "base/logging.h" 11 #include "base/metrics/field_trial.h" 12 #include "base/prefs/pref_service.h" 13 #include "base/prefs/scoped_user_pref_update.h" 14 #include "base/sha1.h" 15 #include "base/strings/string_number_conversions.h" 16 #include "base/strings/string_util.h" 17 #include "base/strings/utf_string_conversions.h" 18 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" 19 #include "chrome/browser/media/desktop_streams_registry.h" 20 #include "chrome/browser/media/media_stream_capture_indicator.h" 21 #include "chrome/browser/media/media_stream_infobar_delegate.h" 22 #include "chrome/browser/profiles/profile.h" 23 #include "chrome/browser/ui/browser.h" 24 #include "chrome/browser/ui/browser_finder.h" 25 #include "chrome/browser/ui/browser_window.h" 26 #include "chrome/browser/ui/screen_capture_notification_ui.h" 27 #include "chrome/browser/ui/simple_message_box.h" 28 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h" 29 #include "chrome/common/chrome_switches.h" 30 #include "chrome/common/chrome_version_info.h" 31 #include "chrome/common/pref_names.h" 32 #include "components/pref_registry/pref_registry_syncable.h" 33 #include "content/public/browser/browser_thread.h" 34 #include "content/public/browser/desktop_media_id.h" 35 #include "content/public/browser/media_capture_devices.h" 36 #include "content/public/browser/notification_service.h" 37 #include "content/public/browser/notification_source.h" 38 #include "content/public/browser/notification_types.h" 39 #include "content/public/browser/render_frame_host.h" 40 #include "content/public/browser/web_contents.h" 41 #include "content/public/common/media_stream_request.h" 42 #include "extensions/common/constants.h" 43 #include "extensions/common/extension.h" 44 #include "extensions/common/permissions/permissions_data.h" 45 #include "grit/generated_resources.h" 46 #include "media/audio/audio_manager_base.h" 47 #include "media/base/media_switches.h" 48 #include "net/base/net_util.h" 49 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h" 50 #include "ui/base/l10n/l10n_util.h" 51 52 #if defined(OS_CHROMEOS) 53 #include "ash/shell.h" 54 #endif // defined(OS_CHROMEOS) 55 56 // Only do audio stream monitoring for platforms that use it for the tab media 57 // indicator UI or the OOM killer. 58 #if !defined(OS_ANDROID) && !defined(OS_IOS) 59 #define AUDIO_STREAM_MONITORING 60 #include "chrome/browser/media/audio_stream_monitor.h" 61 #endif // !defined(OS_ANDROID) && !defined(OS_IOS) 62 63 using content::BrowserThread; 64 using content::MediaCaptureDevices; 65 using content::MediaStreamDevices; 66 67 namespace { 68 69 // A finch experiment to enable the permission bubble for media requests only. 70 bool MediaStreamPermissionBubbleExperimentEnabled() { 71 const std::string group = 72 base::FieldTrialList::FindFullName("MediaStreamPermissionBubble"); 73 if (group == "enabled") 74 return true; 75 76 return false; 77 } 78 79 // Finds a device in |devices| that has |device_id|, or NULL if not found. 80 const content::MediaStreamDevice* FindDeviceWithId( 81 const content::MediaStreamDevices& devices, 82 const std::string& device_id) { 83 content::MediaStreamDevices::const_iterator iter = devices.begin(); 84 for (; iter != devices.end(); ++iter) { 85 if (iter->id == device_id) { 86 return &(*iter); 87 } 88 } 89 return NULL; 90 } 91 92 // This is a short-term solution to grant camera and/or microphone access to 93 // extensions: 94 // 1. Virtual keyboard extension. 95 // 2. Google Voice Search Hotword extension. 96 // 3. Flutter gesture recognition extension. 97 // 4. TODO(smus): Airbender experiment 1. 98 // 5. TODO(smus): Airbender experiment 2. 99 // Once http://crbug.com/292856 is fixed, remove this whitelist. 100 bool IsMediaRequestWhitelistedForExtension( 101 const extensions::Extension* extension) { 102 return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" || 103 extension->id() == "bepbmhgboaologfdajaanbcjmnhjmhfn" || 104 extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" || 105 extension->id() == "clffjmdilanldobdnedchkdbofoimcgb" || 106 extension->id() == "nnckehldicaciogcbchegobnafnjkcne"; 107 } 108 109 bool IsBuiltInExtension(const GURL& origin) { 110 return 111 // Feedback Extension. 112 origin.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/"; 113 } 114 115 // Returns true of the security origin is associated with casting. 116 bool IsOriginForCasting(const GURL& origin) { 117 #if defined(OFFICIAL_BUILD) 118 // Whitelisted tab casting extensions. 119 return 120 // Dev 121 origin.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" || 122 // Canary 123 origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" || 124 // Beta (internal) 125 origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" || 126 // Google Cast Beta 127 origin.spec() == "chrome-extension://dliochdbjfkdbacpmhlcpmleaejidimm/" || 128 // Google Cast Stable 129 origin.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/"; 130 #else 131 return false; 132 #endif 133 } 134 135 // Helper to get title of the calling application shown in the screen capture 136 // notification. 137 base::string16 GetApplicationTitle(content::WebContents* web_contents, 138 const extensions::Extension* extension) { 139 // Use extension name as title for extensions and host/origin for drive-by 140 // web. 141 std::string title; 142 if (extension) { 143 title = extension->name(); 144 } else { 145 GURL url = web_contents->GetURL(); 146 title = url.SchemeIsSecure() ? net::GetHostAndOptionalPort(url) 147 : url.GetOrigin().spec(); 148 } 149 return base::UTF8ToUTF16(title); 150 } 151 152 // Helper to get list of media stream devices for desktop capture in |devices|. 153 // Registers to display notification if |display_notification| is true. 154 // Returns an instance of MediaStreamUI to be passed to content layer. 155 scoped_ptr<content::MediaStreamUI> GetDevicesForDesktopCapture( 156 content::MediaStreamDevices& devices, 157 content::DesktopMediaID media_id, 158 bool capture_audio, 159 bool display_notification, 160 const base::string16& application_title, 161 const base::string16& registered_extension_name) { 162 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 163 scoped_ptr<content::MediaStreamUI> ui; 164 165 // Add selected desktop source to the list. 166 devices.push_back(content::MediaStreamDevice( 167 content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen")); 168 if (capture_audio) { 169 // Use the special loopback device ID for system audio capture. 170 devices.push_back(content::MediaStreamDevice( 171 content::MEDIA_LOOPBACK_AUDIO_CAPTURE, 172 media::AudioManagerBase::kLoopbackInputDeviceId, "System Audio")); 173 } 174 175 // If required, register to display the notification for stream capture. 176 if (display_notification) { 177 if (application_title == registered_extension_name) { 178 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16( 179 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT, 180 application_title)); 181 } else { 182 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16( 183 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT_DELEGATED, 184 registered_extension_name, 185 application_title)); 186 } 187 } 188 189 return ui.Pass(); 190 } 191 192 #if defined(AUDIO_STREAM_MONITORING) 193 194 AudioStreamMonitor* AudioStreamMonitorFromRenderFrame( 195 int render_process_id, 196 int render_frame_id) { 197 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 198 content::WebContents* const web_contents = 199 content::WebContents::FromRenderFrameHost( 200 content::RenderFrameHost::FromID(render_process_id, render_frame_id)); 201 if (!web_contents) 202 return NULL; 203 return AudioStreamMonitor::FromWebContents(web_contents); 204 } 205 206 void StartAudioStreamMonitoringOnUIThread( 207 int render_process_id, 208 int render_frame_id, 209 int stream_id, 210 const AudioStreamMonitor::ReadPowerAndClipCallback& read_power_callback) { 211 AudioStreamMonitor* const audio_stream_monitor = 212 AudioStreamMonitorFromRenderFrame(render_process_id, render_frame_id); 213 if (audio_stream_monitor) 214 audio_stream_monitor->StartMonitoringStream(stream_id, read_power_callback); 215 } 216 217 void StopAudioStreamMonitoringOnUIThread( 218 int render_process_id, 219 int render_frame_id, 220 int stream_id) { 221 AudioStreamMonitor* const audio_stream_monitor = 222 AudioStreamMonitorFromRenderFrame(render_process_id, render_frame_id); 223 if (audio_stream_monitor) 224 audio_stream_monitor->StopMonitoringStream(stream_id); 225 } 226 227 #endif // defined(AUDIO_STREAM_MONITORING) 228 229 #if !defined(OS_ANDROID) 230 // Find browser or app window from a given |web_contents|. 231 gfx::NativeWindow FindParentWindowForWebContents( 232 content::WebContents* web_contents) { 233 Browser* browser = chrome::FindBrowserWithWebContents(web_contents); 234 if (browser && browser->window()) 235 return browser->window()->GetNativeWindow(); 236 237 const apps::AppWindowRegistry::AppWindowList& window_list = 238 apps::AppWindowRegistry::Get( 239 web_contents->GetBrowserContext())->app_windows(); 240 for (apps::AppWindowRegistry::AppWindowList::const_iterator iter = 241 window_list.begin(); 242 iter != window_list.end(); ++iter) { 243 if ((*iter)->web_contents() == web_contents) 244 return (*iter)->GetNativeWindow(); 245 } 246 247 return NULL; 248 } 249 #endif 250 251 } // namespace 252 253 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest( 254 const content::MediaStreamRequest& request, 255 const content::MediaResponseCallback& callback) 256 : request(request), 257 callback(callback) { 258 } 259 260 MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {} 261 262 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() { 263 return Singleton<MediaCaptureDevicesDispatcher>::get(); 264 } 265 266 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() 267 : is_device_enumeration_disabled_(false), 268 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) { 269 // MediaCaptureDevicesDispatcher is a singleton. It should be created on 270 // UI thread. Otherwise, it will not receive 271 // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in 272 // possible use after free. 273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 274 notifications_registrar_.Add( 275 this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 276 content::NotificationService::AllSources()); 277 278 // AVFoundation is used for video/audio device monitoring and video capture in 279 // Mac. Experimentally, connect it in Canary and Unkown (developer builds). 280 #if defined(OS_MACOSX) 281 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 282 if (channel == chrome::VersionInfo::CHANNEL_CANARY || 283 channel == chrome::VersionInfo::CHANNEL_UNKNOWN) { 284 CommandLine::ForCurrentProcess()->AppendSwitch( 285 switches::kEnableAVFoundation); 286 } 287 #endif 288 } 289 290 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {} 291 292 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs( 293 user_prefs::PrefRegistrySyncable* registry) { 294 registry->RegisterStringPref( 295 prefs::kDefaultAudioCaptureDevice, 296 std::string(), 297 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 298 registry->RegisterStringPref( 299 prefs::kDefaultVideoCaptureDevice, 300 std::string(), 301 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 302 } 303 304 void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) { 305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 306 if (!observers_.HasObserver(observer)) 307 observers_.AddObserver(observer); 308 } 309 310 void MediaCaptureDevicesDispatcher::RemoveObserver(Observer* observer) { 311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 312 observers_.RemoveObserver(observer); 313 } 314 315 const MediaStreamDevices& 316 MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() { 317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 318 if (is_device_enumeration_disabled_ || !test_audio_devices_.empty()) 319 return test_audio_devices_; 320 321 return MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices(); 322 } 323 324 const MediaStreamDevices& 325 MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() { 326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 327 if (is_device_enumeration_disabled_ || !test_video_devices_.empty()) 328 return test_video_devices_; 329 330 return MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(); 331 } 332 333 void MediaCaptureDevicesDispatcher::Observe( 334 int type, 335 const content::NotificationSource& source, 336 const content::NotificationDetails& details) { 337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 338 if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) { 339 content::WebContents* web_contents = 340 content::Source<content::WebContents>(source).ptr(); 341 pending_requests_.erase(web_contents); 342 } 343 } 344 345 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest( 346 content::WebContents* web_contents, 347 const content::MediaStreamRequest& request, 348 const content::MediaResponseCallback& callback, 349 const extensions::Extension* extension) { 350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 351 352 if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE || 353 request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE) { 354 ProcessDesktopCaptureAccessRequest( 355 web_contents, request, callback, extension); 356 } else if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE || 357 request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) { 358 ProcessTabCaptureAccessRequest( 359 web_contents, request, callback, extension); 360 } else if (extension && (extension->is_platform_app() || 361 IsMediaRequestWhitelistedForExtension(extension))) { 362 // For extensions access is approved based on extension permissions. 363 ProcessMediaAccessRequestFromPlatformAppOrExtension( 364 web_contents, request, callback, extension); 365 } else { 366 ProcessRegularMediaAccessRequest(web_contents, request, callback); 367 } 368 } 369 370 void MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest( 371 content::WebContents* web_contents, 372 const content::MediaStreamRequest& request, 373 const content::MediaResponseCallback& callback, 374 const extensions::Extension* extension) { 375 content::MediaStreamDevices devices; 376 scoped_ptr<content::MediaStreamUI> ui; 377 378 if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) { 379 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass()); 380 return; 381 } 382 383 // If the device id wasn't specified then this is a screen capture request 384 // (i.e. chooseDesktopMedia() API wasn't used to generate device id). 385 if (request.requested_video_device_id.empty()) { 386 ProcessScreenCaptureAccessRequest( 387 web_contents, request, callback, extension); 388 return; 389 } 390 391 // The extension name that the stream is registered with. 392 std::string original_extension_name; 393 // Resolve DesktopMediaID for the specified device id. 394 content::DesktopMediaID media_id = 395 GetDesktopStreamsRegistry()->RequestMediaForStreamId( 396 request.requested_video_device_id, request.render_process_id, 397 request.render_view_id, request.security_origin, 398 &original_extension_name); 399 400 // Received invalid device id. 401 if (media_id.type == content::DesktopMediaID::TYPE_NONE) { 402 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass()); 403 return; 404 } 405 406 bool loopback_audio_supported = false; 407 #if defined(USE_CRAS) || defined(OS_WIN) 408 // Currently loopback audio capture is supported only on Windows and ChromeOS. 409 loopback_audio_supported = true; 410 #endif 411 412 // Audio is only supported for screen capture streams. 413 bool capture_audio = 414 (media_id.type == content::DesktopMediaID::TYPE_SCREEN && 415 request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE && 416 loopback_audio_supported); 417 418 ui = GetDevicesForDesktopCapture( 419 devices, media_id, capture_audio, true, 420 GetApplicationTitle(web_contents, extension), 421 base::UTF8ToUTF16(original_extension_name)); 422 423 callback.Run(devices, content::MEDIA_DEVICE_OK, ui.Pass()); 424 } 425 426 void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest( 427 content::WebContents* web_contents, 428 const content::MediaStreamRequest& request, 429 const content::MediaResponseCallback& callback, 430 const extensions::Extension* extension) { 431 content::MediaStreamDevices devices; 432 scoped_ptr<content::MediaStreamUI> ui; 433 434 DCHECK_EQ(request.video_type, content::MEDIA_DESKTOP_VIDEO_CAPTURE); 435 436 bool loopback_audio_supported = false; 437 #if defined(USE_CRAS) || defined(OS_WIN) 438 // Currently loopback audio capture is supported only on Windows and ChromeOS. 439 loopback_audio_supported = true; 440 #endif 441 442 const bool component_extension = 443 extension && extension->location() == extensions::Manifest::COMPONENT; 444 445 const bool screen_capture_enabled = 446 CommandLine::ForCurrentProcess()->HasSwitch( 447 switches::kEnableUserMediaScreenCapturing) || 448 IsOriginForCasting(request.security_origin) || 449 IsBuiltInExtension(request.security_origin); 450 451 const bool origin_is_secure = 452 request.security_origin.SchemeIsSecure() || 453 request.security_origin.SchemeIs(extensions::kExtensionScheme) || 454 CommandLine::ForCurrentProcess()->HasSwitch( 455 switches::kAllowHttpScreenCapture); 456 457 // Approve request only when the following conditions are met: 458 // 1. Screen capturing is enabled via command line switch or white-listed for 459 // the given origin. 460 // 2. Request comes from a page with a secure origin or from an extension. 461 if (screen_capture_enabled && origin_is_secure) { 462 // Get title of the calling application prior to showing the message box. 463 // chrome::ShowMessageBox() starts a nested message loop which may allow 464 // |web_contents| to be destroyed on the UI thread before the message box 465 // is closed. See http://crbug.com/326690. 466 base::string16 application_title = 467 GetApplicationTitle(web_contents, extension); 468 #if !defined(OS_ANDROID) 469 gfx::NativeWindow parent_window = 470 FindParentWindowForWebContents(web_contents); 471 #else 472 gfx::NativeWindow parent_window = NULL; 473 #endif 474 web_contents = NULL; 475 476 // For component extensions, bypass message box. 477 bool user_approved = false; 478 if (!component_extension) { 479 base::string16 application_name = base::UTF8ToUTF16( 480 extension ? extension->name() : request.security_origin.spec()); 481 base::string16 confirmation_text = l10n_util::GetStringFUTF16( 482 request.audio_type == content::MEDIA_NO_SERVICE ? 483 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT : 484 IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT, 485 application_name); 486 chrome::MessageBoxResult result = chrome::ShowMessageBox( 487 parent_window, 488 l10n_util::GetStringFUTF16( 489 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name), 490 confirmation_text, 491 chrome::MESSAGE_BOX_TYPE_QUESTION); 492 user_approved = (result == chrome::MESSAGE_BOX_RESULT_YES); 493 } 494 495 if (user_approved || component_extension) { 496 content::DesktopMediaID screen_id; 497 #if defined(OS_CHROMEOS) 498 screen_id = content::DesktopMediaID::RegisterAuraWindow( 499 ash::Shell::GetInstance()->GetPrimaryRootWindow()); 500 #else // defined(OS_CHROMEOS) 501 screen_id = 502 content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, 503 webrtc::kFullDesktopScreenId); 504 #endif // !defined(OS_CHROMEOS) 505 506 bool capture_audio = 507 (request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE && 508 loopback_audio_supported); 509 510 // Unless we're being invoked from a component extension, register to 511 // display the notification for stream capture. 512 bool display_notification = !component_extension; 513 514 ui = GetDevicesForDesktopCapture(devices, screen_id, capture_audio, 515 display_notification, application_title, 516 application_title); 517 } 518 } 519 520 callback.Run( 521 devices, 522 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE : 523 content::MEDIA_DEVICE_OK, 524 ui.Pass()); 525 } 526 527 void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest( 528 content::WebContents* web_contents, 529 const content::MediaStreamRequest& request, 530 const content::MediaResponseCallback& callback, 531 const extensions::Extension* extension) { 532 content::MediaStreamDevices devices; 533 scoped_ptr<content::MediaStreamUI> ui; 534 535 #if defined(OS_ANDROID) 536 // Tab capture is not supported on Android. 537 callback.Run(devices, content::MEDIA_DEVICE_TAB_CAPTURE_FAILURE, ui.Pass()); 538 #else // defined(OS_ANDROID) 539 Profile* profile = 540 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 541 extensions::TabCaptureRegistry* tab_capture_registry = 542 extensions::TabCaptureRegistry::Get(profile); 543 if (!tab_capture_registry) { 544 NOTREACHED(); 545 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass()); 546 return; 547 } 548 bool tab_capture_allowed = 549 tab_capture_registry->VerifyRequest(request.render_process_id, 550 request.render_view_id); 551 552 if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE && 553 tab_capture_allowed && 554 extension->permissions_data()->HasAPIPermission( 555 extensions::APIPermission::kTabCapture)) { 556 devices.push_back(content::MediaStreamDevice( 557 content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string())); 558 } 559 560 if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE && 561 tab_capture_allowed && 562 extension->permissions_data()->HasAPIPermission( 563 extensions::APIPermission::kTabCapture)) { 564 devices.push_back(content::MediaStreamDevice( 565 content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string())); 566 } 567 568 if (!devices.empty()) { 569 ui = media_stream_capture_indicator_->RegisterMediaStream( 570 web_contents, devices); 571 } 572 callback.Run( 573 devices, 574 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE : 575 content::MEDIA_DEVICE_OK, 576 ui.Pass()); 577 #endif // !defined(OS_ANDROID) 578 } 579 580 void MediaCaptureDevicesDispatcher:: 581 ProcessMediaAccessRequestFromPlatformAppOrExtension( 582 content::WebContents* web_contents, 583 const content::MediaStreamRequest& request, 584 const content::MediaResponseCallback& callback, 585 const extensions::Extension* extension) { 586 587 // TODO(vrk): This code is largely duplicated in 588 // MediaStreamDevicesController::Accept(). Move this code into a shared method 589 // between the two classes. 590 591 bool audio_allowed = 592 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE && 593 extension->permissions_data()->HasAPIPermission( 594 extensions::APIPermission::kAudioCapture); 595 bool video_allowed = 596 request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE && 597 extension->permissions_data()->HasAPIPermission( 598 extensions::APIPermission::kVideoCapture); 599 600 bool get_default_audio_device = audio_allowed; 601 bool get_default_video_device = video_allowed; 602 603 content::MediaStreamDevices devices; 604 605 // Get the exact audio or video device if an id is specified. 606 if (audio_allowed && !request.requested_audio_device_id.empty()) { 607 const content::MediaStreamDevice* audio_device = 608 GetRequestedAudioDevice(request.requested_audio_device_id); 609 if (audio_device) { 610 devices.push_back(*audio_device); 611 get_default_audio_device = false; 612 } 613 } 614 if (video_allowed && !request.requested_video_device_id.empty()) { 615 const content::MediaStreamDevice* video_device = 616 GetRequestedVideoDevice(request.requested_video_device_id); 617 if (video_device) { 618 devices.push_back(*video_device); 619 get_default_video_device = false; 620 } 621 } 622 623 // If either or both audio and video devices were requested but not 624 // specified by id, get the default devices. 625 if (get_default_audio_device || get_default_video_device) { 626 Profile* profile = 627 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 628 GetDefaultDevicesForProfile(profile, 629 get_default_audio_device, 630 get_default_video_device, 631 &devices); 632 } 633 634 scoped_ptr<content::MediaStreamUI> ui; 635 if (!devices.empty()) { 636 ui = media_stream_capture_indicator_->RegisterMediaStream( 637 web_contents, devices); 638 } 639 callback.Run( 640 devices, 641 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE : 642 content::MEDIA_DEVICE_OK, 643 ui.Pass()); 644 } 645 646 void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest( 647 content::WebContents* web_contents, 648 const content::MediaStreamRequest& request, 649 const content::MediaResponseCallback& callback) { 650 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 651 652 RequestsQueue& queue = pending_requests_[web_contents]; 653 queue.push_back(PendingAccessRequest(request, callback)); 654 655 // If this is the only request then show the infobar. 656 if (queue.size() == 1) 657 ProcessQueuedAccessRequest(web_contents); 658 } 659 660 void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest( 661 content::WebContents* web_contents) { 662 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 663 664 std::map<content::WebContents*, RequestsQueue>::iterator it = 665 pending_requests_.find(web_contents); 666 667 if (it == pending_requests_.end() || it->second.empty()) { 668 // Don't do anything if the tab was closed. 669 return; 670 } 671 672 DCHECK(!it->second.empty()); 673 674 if (PermissionBubbleManager::Enabled() || 675 MediaStreamPermissionBubbleExperimentEnabled()) { 676 scoped_ptr<MediaStreamDevicesController> controller( 677 new MediaStreamDevicesController(web_contents, 678 it->second.front().request, 679 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse, 680 base::Unretained(this), web_contents))); 681 if (controller->DismissInfoBarAndTakeActionOnSettings()) 682 return; 683 PermissionBubbleManager* bubble_manager = 684 PermissionBubbleManager::FromWebContents(web_contents); 685 if (bubble_manager) 686 bubble_manager->AddRequest(controller.release()); 687 return; 688 } 689 690 // TODO(gbillock): delete this block and the MediaStreamInfoBarDelegate 691 // when we've transitioned to bubbles. (crbug/337458) 692 MediaStreamInfoBarDelegate::Create( 693 web_contents, it->second.front().request, 694 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse, 695 base::Unretained(this), web_contents)); 696 } 697 698 void MediaCaptureDevicesDispatcher::OnAccessRequestResponse( 699 content::WebContents* web_contents, 700 const content::MediaStreamDevices& devices, 701 content::MediaStreamRequestResult result, 702 scoped_ptr<content::MediaStreamUI> ui) { 703 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 704 705 std::map<content::WebContents*, RequestsQueue>::iterator it = 706 pending_requests_.find(web_contents); 707 if (it == pending_requests_.end()) { 708 // WebContents has been destroyed. Don't need to do anything. 709 return; 710 } 711 712 RequestsQueue& queue(it->second); 713 if (queue.empty()) 714 return; 715 716 content::MediaResponseCallback callback = queue.front().callback; 717 queue.pop_front(); 718 719 if (!queue.empty()) { 720 // Post a task to process next queued request. It has to be done 721 // asynchronously to make sure that calling infobar is not destroyed until 722 // after this function returns. 723 BrowserThread::PostTask( 724 BrowserThread::UI, FROM_HERE, 725 base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest, 726 base::Unretained(this), web_contents)); 727 } 728 729 callback.Run(devices, result, ui.Pass()); 730 } 731 732 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile( 733 Profile* profile, 734 bool audio, 735 bool video, 736 content::MediaStreamDevices* devices) { 737 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 738 DCHECK(audio || video); 739 740 PrefService* prefs = profile->GetPrefs(); 741 std::string default_device; 742 if (audio) { 743 default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice); 744 const content::MediaStreamDevice* device = 745 GetRequestedAudioDevice(default_device); 746 if (!device) 747 device = GetFirstAvailableAudioDevice(); 748 if (device) 749 devices->push_back(*device); 750 } 751 752 if (video) { 753 default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice); 754 const content::MediaStreamDevice* device = 755 GetRequestedVideoDevice(default_device); 756 if (!device) 757 device = GetFirstAvailableVideoDevice(); 758 if (device) 759 devices->push_back(*device); 760 } 761 } 762 763 const content::MediaStreamDevice* 764 MediaCaptureDevicesDispatcher::GetRequestedAudioDevice( 765 const std::string& requested_audio_device_id) { 766 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 767 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices(); 768 const content::MediaStreamDevice* const device = 769 FindDeviceWithId(audio_devices, requested_audio_device_id); 770 return device; 771 } 772 773 const content::MediaStreamDevice* 774 MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() { 775 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 776 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices(); 777 if (audio_devices.empty()) 778 return NULL; 779 return &(*audio_devices.begin()); 780 } 781 782 const content::MediaStreamDevice* 783 MediaCaptureDevicesDispatcher::GetRequestedVideoDevice( 784 const std::string& requested_video_device_id) { 785 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 786 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices(); 787 const content::MediaStreamDevice* const device = 788 FindDeviceWithId(video_devices, requested_video_device_id); 789 return device; 790 } 791 792 const content::MediaStreamDevice* 793 MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() { 794 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 795 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices(); 796 if (video_devices.empty()) 797 return NULL; 798 return &(*video_devices.begin()); 799 } 800 801 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() { 802 is_device_enumeration_disabled_ = true; 803 } 804 805 scoped_refptr<MediaStreamCaptureIndicator> 806 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() { 807 return media_stream_capture_indicator_; 808 } 809 810 DesktopStreamsRegistry* 811 MediaCaptureDevicesDispatcher::GetDesktopStreamsRegistry() { 812 if (!desktop_streams_registry_) 813 desktop_streams_registry_.reset(new DesktopStreamsRegistry()); 814 return desktop_streams_registry_.get(); 815 } 816 817 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() { 818 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 819 BrowserThread::PostTask( 820 BrowserThread::UI, FROM_HERE, 821 base::Bind( 822 &MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread, 823 base::Unretained(this))); 824 } 825 826 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() { 827 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 828 BrowserThread::PostTask( 829 BrowserThread::UI, FROM_HERE, 830 base::Bind( 831 &MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread, 832 base::Unretained(this))); 833 } 834 835 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged( 836 int render_process_id, 837 int render_view_id, 838 int page_request_id, 839 const GURL& security_origin, 840 const content::MediaStreamDevice& device, 841 content::MediaRequestState state) { 842 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 843 BrowserThread::PostTask( 844 BrowserThread::UI, FROM_HERE, 845 base::Bind( 846 &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread, 847 base::Unretained(this), render_process_id, render_view_id, 848 page_request_id, security_origin, device, state)); 849 } 850 851 void MediaCaptureDevicesDispatcher::OnAudioStreamPlaying( 852 int render_process_id, 853 int render_frame_id, 854 int stream_id, 855 const ReadPowerAndClipCallback& read_power_callback) { 856 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 857 #if defined(AUDIO_STREAM_MONITORING) 858 BrowserThread::PostTask( 859 BrowserThread::UI, 860 FROM_HERE, 861 base::Bind(&StartAudioStreamMonitoringOnUIThread, 862 render_process_id, 863 render_frame_id, 864 stream_id, 865 read_power_callback)); 866 #endif 867 } 868 869 void MediaCaptureDevicesDispatcher::OnAudioStreamStopped( 870 int render_process_id, 871 int render_frame_id, 872 int stream_id) { 873 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 874 #if defined(AUDIO_STREAM_MONITORING) 875 BrowserThread::PostTask( 876 BrowserThread::UI, 877 FROM_HERE, 878 base::Bind(&StopAudioStreamMonitoringOnUIThread, 879 render_process_id, 880 render_frame_id, 881 stream_id)); 882 #endif 883 } 884 885 void MediaCaptureDevicesDispatcher::OnCreatingAudioStream( 886 int render_process_id, 887 int render_frame_id) { 888 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 889 BrowserThread::PostTask( 890 BrowserThread::UI, FROM_HERE, 891 base::Bind( 892 &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread, 893 base::Unretained(this), render_process_id, render_frame_id)); 894 } 895 896 void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() { 897 MediaStreamDevices devices = GetAudioCaptureDevices(); 898 FOR_EACH_OBSERVER(Observer, observers_, 899 OnUpdateAudioDevices(devices)); 900 } 901 902 void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() { 903 MediaStreamDevices devices = GetVideoCaptureDevices(); 904 FOR_EACH_OBSERVER(Observer, observers_, 905 OnUpdateVideoDevices(devices)); 906 } 907 908 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread( 909 int render_process_id, 910 int render_view_id, 911 int page_request_id, 912 const GURL& security_origin, 913 const content::MediaStreamDevice& device, 914 content::MediaRequestState state) { 915 // Track desktop capture sessions. Tracking is necessary to avoid unbalanced 916 // session counts since not all requests will reach MEDIA_REQUEST_STATE_DONE, 917 // but they will all reach MEDIA_REQUEST_STATE_CLOSING. 918 if (device.type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) { 919 if (state == content::MEDIA_REQUEST_STATE_DONE) { 920 DesktopCaptureSession session = { render_process_id, render_view_id, 921 page_request_id }; 922 desktop_capture_sessions_.push_back(session); 923 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) { 924 for (DesktopCaptureSessions::iterator it = 925 desktop_capture_sessions_.begin(); 926 it != desktop_capture_sessions_.end(); 927 ++it) { 928 if (it->render_process_id == render_process_id && 929 it->render_view_id == render_view_id && 930 it->page_request_id == page_request_id) { 931 desktop_capture_sessions_.erase(it); 932 break; 933 } 934 } 935 } 936 } 937 938 // Cancel the request. 939 if (state == content::MEDIA_REQUEST_STATE_CLOSING) { 940 bool found = false; 941 for (RequestsQueues::iterator rqs_it = pending_requests_.begin(); 942 rqs_it != pending_requests_.end(); ++rqs_it) { 943 RequestsQueue& queue = rqs_it->second; 944 for (RequestsQueue::iterator it = queue.begin(); 945 it != queue.end(); ++it) { 946 if (it->request.render_process_id == render_process_id && 947 it->request.render_view_id == render_view_id && 948 it->request.page_request_id == page_request_id) { 949 queue.erase(it); 950 found = true; 951 break; 952 } 953 } 954 if (found) 955 break; 956 } 957 } 958 959 #if defined(OS_CHROMEOS) 960 if (IsOriginForCasting(security_origin) && IsVideoMediaType(device.type)) { 961 // Notify ash that casting state has changed. 962 if (state == content::MEDIA_REQUEST_STATE_DONE) { 963 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(true); 964 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) { 965 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(false); 966 } 967 } 968 #endif 969 970 FOR_EACH_OBSERVER(Observer, observers_, 971 OnRequestUpdate(render_process_id, 972 render_view_id, 973 device, 974 state)); 975 } 976 977 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread( 978 int render_process_id, 979 int render_frame_id) { 980 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 981 FOR_EACH_OBSERVER(Observer, observers_, 982 OnCreatingAudioStream(render_process_id, render_frame_id)); 983 #if defined(AUDIO_STREAM_MONITORING) 984 content::WebContents* const web_contents = 985 content::WebContents::FromRenderFrameHost( 986 content::RenderFrameHost::FromID(render_process_id, render_frame_id)); 987 if (web_contents) { 988 // Note: Calling CreateForWebContents() multiple times is valid (see usage 989 // info for content::WebContentsUserData). 990 AudioStreamMonitor::CreateForWebContents(web_contents); 991 } 992 #endif 993 } 994 995 bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() { 996 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 997 return desktop_capture_sessions_.size() > 0; 998 } 999 1000 1001 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices( 1002 const MediaStreamDevices& devices) { 1003 test_audio_devices_ = devices; 1004 } 1005 1006 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices( 1007 const MediaStreamDevices& devices) { 1008 test_video_devices_ = devices; 1009 } 1010