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