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.
5 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
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/media/desktop_streams_registry.h"
19 #include "chrome/browser/media/media_stream_capture_indicator.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/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/render_process_host.h"
41 #include "content/public/browser/web_contents.h"
42 #include "content/public/common/media_stream_request.h"
43 #include "extensions/common/constants.h"
44 #include "extensions/common/extension.h"
45 #include "extensions/common/permissions/permissions_data.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"
52 #if defined(OS_CHROMEOS)
53 #include "ash/shell.h"
54 #endif // defined(OS_CHROMEOS)
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)
63 #if defined(ENABLE_EXTENSIONS)
64 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
67 using content::BrowserThread
;
68 using content::MediaCaptureDevices
;
69 using content::MediaStreamDevices
;
73 // A finch experiment to enable the permission bubble for media requests only.
74 bool MediaStreamPermissionBubbleExperimentEnabled() {
75 const std::string group
=
76 base::FieldTrialList::FindFullName("MediaStreamPermissionBubble");
77 if (group
== "enabled")
83 // Finds a device in |devices| that has |device_id|, or NULL if not found.
84 const content::MediaStreamDevice
* FindDeviceWithId(
85 const content::MediaStreamDevices
& devices
,
86 const std::string
& device_id
) {
87 content::MediaStreamDevices::const_iterator iter
= devices
.begin();
88 for (; iter
!= devices
.end(); ++iter
) {
89 if (iter
->id
== device_id
) {
96 // This is a short-term solution to grant camera and/or microphone access to
98 // 1. Virtual keyboard extension.
99 // 2. Google Voice Search Hotword extension.
100 // 3. Flutter gesture recognition extension.
101 // 4. TODO(smus): Airbender experiment 1.
102 // 5. TODO(smus): Airbender experiment 2.
103 // 6. Hotwording component extension.
104 // Once http://crbug.com/292856 is fixed, remove this whitelist.
105 bool IsMediaRequestWhitelistedForExtension(
106 const extensions::Extension
* extension
) {
107 return extension
->id() == "mppnpdlheglhdfmldimlhpnegondlapf" ||
108 extension
->id() == "bepbmhgboaologfdajaanbcjmnhjmhfn" ||
109 extension
->id() == "jokbpnebhdcladagohdnfgjcpejggllo" ||
110 extension
->id() == "clffjmdilanldobdnedchkdbofoimcgb" ||
111 extension
->id() == "nnckehldicaciogcbchegobnafnjkcne" ||
112 extension
->id() == "nbpagnldghgfoolbancepceaanlmhfmd";
115 bool IsBuiltInExtension(const GURL
& origin
) {
117 // Feedback Extension.
118 origin
.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/";
121 // Returns true of the security origin is associated with casting.
122 bool IsOriginForCasting(const GURL
& origin
) {
123 // Whitelisted tab casting extensions.
126 origin
.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" ||
128 origin
.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
130 origin
.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
132 origin
.spec() == "chrome-extension://dliochdbjfkdbacpmhlcpmleaejidimm/" ||
133 // Google Cast Stable
134 origin
.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/";
137 // Helper to get title of the calling application shown in the screen capture
139 base::string16
GetApplicationTitle(content::WebContents
* web_contents
,
140 const extensions::Extension
* extension
) {
141 // Use extension name as title for extensions and host/origin for drive-by
145 title
= extension
->name();
147 GURL url
= web_contents
->GetURL();
148 title
= url
.SchemeIsSecure() ? net::GetHostAndOptionalPort(url
)
149 : url
.GetOrigin().spec();
151 return base::UTF8ToUTF16(title
);
154 // Helper to get list of media stream devices for desktop capture in |devices|.
155 // Registers to display notification if |display_notification| is true.
156 // Returns an instance of MediaStreamUI to be passed to content layer.
157 scoped_ptr
<content::MediaStreamUI
> GetDevicesForDesktopCapture(
158 content::MediaStreamDevices
& devices
,
159 content::DesktopMediaID media_id
,
161 bool display_notification
,
162 const base::string16
& application_title
,
163 const base::string16
& registered_extension_name
) {
164 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
165 scoped_ptr
<content::MediaStreamUI
> ui
;
167 // Add selected desktop source to the list.
168 devices
.push_back(content::MediaStreamDevice(
169 content::MEDIA_DESKTOP_VIDEO_CAPTURE
, media_id
.ToString(), "Screen"));
171 // Use the special loopback device ID for system audio capture.
172 devices
.push_back(content::MediaStreamDevice(
173 content::MEDIA_LOOPBACK_AUDIO_CAPTURE
,
174 media::AudioManagerBase::kLoopbackInputDeviceId
, "System Audio"));
177 // If required, register to display the notification for stream capture.
178 if (display_notification
) {
179 if (application_title
== registered_extension_name
) {
180 ui
= ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
181 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT
,
184 ui
= ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
185 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT_DELEGATED
,
186 registered_extension_name
,
194 #if defined(AUDIO_STREAM_MONITORING)
196 AudioStreamMonitor
* AudioStreamMonitorFromRenderFrame(
197 int render_process_id
,
198 int render_frame_id
) {
199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
200 content::WebContents
* const web_contents
=
201 content::WebContents::FromRenderFrameHost(
202 content::RenderFrameHost::FromID(render_process_id
, render_frame_id
));
205 return AudioStreamMonitor::FromWebContents(web_contents
);
208 void StartAudioStreamMonitoringOnUIThread(
209 int render_process_id
,
212 const AudioStreamMonitor::ReadPowerAndClipCallback
& read_power_callback
) {
213 AudioStreamMonitor
* const audio_stream_monitor
=
214 AudioStreamMonitorFromRenderFrame(render_process_id
, render_frame_id
);
215 if (audio_stream_monitor
)
216 audio_stream_monitor
->StartMonitoringStream(stream_id
, read_power_callback
);
219 void StopAudioStreamMonitoringOnUIThread(
220 int render_process_id
,
223 AudioStreamMonitor
* const audio_stream_monitor
=
224 AudioStreamMonitorFromRenderFrame(render_process_id
, render_frame_id
);
225 if (audio_stream_monitor
)
226 audio_stream_monitor
->StopMonitoringStream(stream_id
);
229 #endif // defined(AUDIO_STREAM_MONITORING)
231 #if !defined(OS_ANDROID)
232 // Find browser or app window from a given |web_contents|.
233 gfx::NativeWindow
FindParentWindowForWebContents(
234 content::WebContents
* web_contents
) {
235 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents
);
236 if (browser
&& browser
->window())
237 return browser
->window()->GetNativeWindow();
239 const apps::AppWindowRegistry::AppWindowList
& window_list
=
240 apps::AppWindowRegistry::Get(
241 web_contents
->GetBrowserContext())->app_windows();
242 for (apps::AppWindowRegistry::AppWindowList::const_iterator iter
=
244 iter
!= window_list
.end(); ++iter
) {
245 if ((*iter
)->web_contents() == web_contents
)
246 return (*iter
)->GetNativeWindow();
255 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
256 const content::MediaStreamRequest
& request
,
257 const content::MediaResponseCallback
& callback
)
262 MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {}
264 MediaCaptureDevicesDispatcher
* MediaCaptureDevicesDispatcher::GetInstance() {
265 return Singleton
<MediaCaptureDevicesDispatcher
>::get();
268 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
269 : is_device_enumeration_disabled_(false),
270 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) {
271 // MediaCaptureDevicesDispatcher is a singleton. It should be created on
272 // UI thread. Otherwise, it will not receive
273 // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in
274 // possible use after free.
275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
276 notifications_registrar_
.Add(
277 this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
278 content::NotificationService::AllSources());
280 // AVFoundation is used for video/audio device monitoring and video capture in
281 // Mac. Experimentally, connect it in Canary and Unknown (developer builds).
282 #if defined(OS_MACOSX)
283 chrome::VersionInfo::Channel channel
= chrome::VersionInfo::GetChannel();
284 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kForceQTKit
)) {
285 if (channel
== chrome::VersionInfo::CHANNEL_CANARY
||
286 channel
== chrome::VersionInfo::CHANNEL_UNKNOWN
) {
287 CommandLine::ForCurrentProcess()->AppendSwitch(
288 switches::kEnableAVFoundation
);
294 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
296 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
297 user_prefs::PrefRegistrySyncable
* registry
) {
298 registry
->RegisterStringPref(
299 prefs::kDefaultAudioCaptureDevice
,
301 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
302 registry
->RegisterStringPref(
303 prefs::kDefaultVideoCaptureDevice
,
305 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
308 void MediaCaptureDevicesDispatcher::AddObserver(Observer
* observer
) {
309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
310 if (!observers_
.HasObserver(observer
))
311 observers_
.AddObserver(observer
);
314 void MediaCaptureDevicesDispatcher::RemoveObserver(Observer
* observer
) {
315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
316 observers_
.RemoveObserver(observer
);
319 const MediaStreamDevices
&
320 MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() {
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
322 if (is_device_enumeration_disabled_
|| !test_audio_devices_
.empty())
323 return test_audio_devices_
;
325 return MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices();
328 const MediaStreamDevices
&
329 MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() {
330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
331 if (is_device_enumeration_disabled_
|| !test_video_devices_
.empty())
332 return test_video_devices_
;
334 return MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices();
337 void MediaCaptureDevicesDispatcher::Observe(
339 const content::NotificationSource
& source
,
340 const content::NotificationDetails
& details
) {
341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
342 if (type
== content::NOTIFICATION_WEB_CONTENTS_DESTROYED
) {
343 content::WebContents
* web_contents
=
344 content::Source
<content::WebContents
>(source
).ptr();
345 pending_requests_
.erase(web_contents
);
349 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest(
350 content::WebContents
* web_contents
,
351 const content::MediaStreamRequest
& request
,
352 const content::MediaResponseCallback
& callback
,
353 const extensions::Extension
* extension
) {
354 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
356 if (request
.video_type
== content::MEDIA_DESKTOP_VIDEO_CAPTURE
||
357 request
.audio_type
== content::MEDIA_LOOPBACK_AUDIO_CAPTURE
) {
358 ProcessDesktopCaptureAccessRequest(
359 web_contents
, request
, callback
, extension
);
360 } else if (request
.video_type
== content::MEDIA_TAB_VIDEO_CAPTURE
||
361 request
.audio_type
== content::MEDIA_TAB_AUDIO_CAPTURE
) {
362 ProcessTabCaptureAccessRequest(
363 web_contents
, request
, callback
, extension
);
364 } else if (extension
&& (extension
->is_platform_app() ||
365 IsMediaRequestWhitelistedForExtension(extension
))) {
366 // For extensions access is approved based on extension permissions.
367 ProcessMediaAccessRequestFromPlatformAppOrExtension(
368 web_contents
, request
, callback
, extension
);
370 ProcessRegularMediaAccessRequest(web_contents
, request
, callback
);
374 void MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest(
375 content::WebContents
* web_contents
,
376 const content::MediaStreamRequest
& request
,
377 const content::MediaResponseCallback
& callback
,
378 const extensions::Extension
* extension
) {
379 content::MediaStreamDevices devices
;
380 scoped_ptr
<content::MediaStreamUI
> ui
;
382 if (request
.video_type
!= content::MEDIA_DESKTOP_VIDEO_CAPTURE
) {
383 callback
.Run(devices
, content::MEDIA_DEVICE_INVALID_STATE
, ui
.Pass());
387 // If the device id wasn't specified then this is a screen capture request
388 // (i.e. chooseDesktopMedia() API wasn't used to generate device id).
389 if (request
.requested_video_device_id
.empty()) {
390 ProcessScreenCaptureAccessRequest(
391 web_contents
, request
, callback
, extension
);
395 // The extension name that the stream is registered with.
396 std::string original_extension_name
;
397 // Resolve DesktopMediaID for the specified device id.
398 content::DesktopMediaID media_id
;
399 // TODO(miu): Replace "main RenderFrame" IDs with the request's actual
400 // RenderFrame IDs once the desktop capture extension API implementation is
401 // fixed. http://crbug.com/304341
402 content::WebContents
* const web_contents_for_stream
=
403 content::WebContents::FromRenderFrameHost(
404 content::RenderFrameHost::FromID(request
.render_process_id
,
405 request
.render_frame_id
));
406 content::RenderFrameHost
* const main_frame
= web_contents_for_stream
?
407 web_contents_for_stream
->GetMainFrame() : NULL
;
409 media_id
= GetDesktopStreamsRegistry()->RequestMediaForStreamId(
410 request
.requested_video_device_id
,
411 main_frame
->GetProcess()->GetID(),
412 main_frame
->GetRoutingID(),
413 request
.security_origin
,
414 &original_extension_name
);
417 // Received invalid device id.
418 if (media_id
.type
== content::DesktopMediaID::TYPE_NONE
) {
419 callback
.Run(devices
, content::MEDIA_DEVICE_INVALID_STATE
, ui
.Pass());
423 bool loopback_audio_supported
= false;
424 #if defined(USE_CRAS) || defined(OS_WIN)
425 // Currently loopback audio capture is supported only on Windows and ChromeOS.
426 loopback_audio_supported
= true;
429 // Audio is only supported for screen capture streams.
431 (media_id
.type
== content::DesktopMediaID::TYPE_SCREEN
&&
432 request
.audio_type
== content::MEDIA_LOOPBACK_AUDIO_CAPTURE
&&
433 loopback_audio_supported
);
435 ui
= GetDevicesForDesktopCapture(
436 devices
, media_id
, capture_audio
, true,
437 GetApplicationTitle(web_contents
, extension
),
438 base::UTF8ToUTF16(original_extension_name
));
440 callback
.Run(devices
, content::MEDIA_DEVICE_OK
, ui
.Pass());
443 void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest(
444 content::WebContents
* web_contents
,
445 const content::MediaStreamRequest
& request
,
446 const content::MediaResponseCallback
& callback
,
447 const extensions::Extension
* extension
) {
448 content::MediaStreamDevices devices
;
449 scoped_ptr
<content::MediaStreamUI
> ui
;
451 DCHECK_EQ(request
.video_type
, content::MEDIA_DESKTOP_VIDEO_CAPTURE
);
453 bool loopback_audio_supported
= false;
454 #if defined(USE_CRAS) || defined(OS_WIN)
455 // Currently loopback audio capture is supported only on Windows and ChromeOS.
456 loopback_audio_supported
= true;
459 const bool component_extension
=
460 extension
&& extension
->location() == extensions::Manifest::COMPONENT
;
462 const bool screen_capture_enabled
=
463 CommandLine::ForCurrentProcess()->HasSwitch(
464 switches::kEnableUserMediaScreenCapturing
) ||
465 IsOriginForCasting(request
.security_origin
) ||
466 IsBuiltInExtension(request
.security_origin
);
468 const bool origin_is_secure
=
469 request
.security_origin
.SchemeIsSecure() ||
470 request
.security_origin
.SchemeIs(extensions::kExtensionScheme
) ||
471 CommandLine::ForCurrentProcess()->HasSwitch(
472 switches::kAllowHttpScreenCapture
);
474 // Approve request only when the following conditions are met:
475 // 1. Screen capturing is enabled via command line switch or white-listed for
477 // 2. Request comes from a page with a secure origin or from an extension.
478 if (screen_capture_enabled
&& origin_is_secure
) {
479 // Get title of the calling application prior to showing the message box.
480 // chrome::ShowMessageBox() starts a nested message loop which may allow
481 // |web_contents| to be destroyed on the UI thread before the message box
482 // is closed. See http://crbug.com/326690.
483 base::string16 application_title
=
484 GetApplicationTitle(web_contents
, extension
);
485 #if !defined(OS_ANDROID)
486 gfx::NativeWindow parent_window
=
487 FindParentWindowForWebContents(web_contents
);
489 gfx::NativeWindow parent_window
= NULL
;
493 // For component extensions, bypass message box.
494 bool user_approved
= false;
495 if (!component_extension
) {
496 base::string16 application_name
= base::UTF8ToUTF16(
497 extension
? extension
->name() : request
.security_origin
.spec());
498 base::string16 confirmation_text
= l10n_util::GetStringFUTF16(
499 request
.audio_type
== content::MEDIA_NO_SERVICE
?
500 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT
:
501 IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT
,
503 chrome::MessageBoxResult result
= chrome::ShowMessageBox(
505 l10n_util::GetStringFUTF16(
506 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE
, application_name
),
508 chrome::MESSAGE_BOX_TYPE_QUESTION
);
509 user_approved
= (result
== chrome::MESSAGE_BOX_RESULT_YES
);
512 if (user_approved
|| component_extension
) {
513 content::DesktopMediaID screen_id
;
514 #if defined(OS_CHROMEOS)
515 screen_id
= content::DesktopMediaID::RegisterAuraWindow(
516 ash::Shell::GetInstance()->GetPrimaryRootWindow());
517 #else // defined(OS_CHROMEOS)
519 content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN
,
520 webrtc::kFullDesktopScreenId
);
521 #endif // !defined(OS_CHROMEOS)
524 (request
.audio_type
== content::MEDIA_LOOPBACK_AUDIO_CAPTURE
&&
525 loopback_audio_supported
);
527 // Unless we're being invoked from a component extension, register to
528 // display the notification for stream capture.
529 bool display_notification
= !component_extension
;
531 ui
= GetDevicesForDesktopCapture(devices
, screen_id
, capture_audio
,
532 display_notification
, application_title
,
539 devices
.empty() ? content::MEDIA_DEVICE_INVALID_STATE
:
540 content::MEDIA_DEVICE_OK
,
544 void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest(
545 content::WebContents
* web_contents
,
546 const content::MediaStreamRequest
& request
,
547 const content::MediaResponseCallback
& callback
,
548 const extensions::Extension
* extension
) {
549 content::MediaStreamDevices devices
;
550 scoped_ptr
<content::MediaStreamUI
> ui
;
552 #if defined(ENABLE_EXTENSIONS)
554 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
555 extensions::TabCaptureRegistry
* tab_capture_registry
=
556 extensions::TabCaptureRegistry::Get(profile
);
557 if (!tab_capture_registry
) {
559 callback
.Run(devices
, content::MEDIA_DEVICE_INVALID_STATE
, ui
.Pass());
562 const bool tab_capture_allowed
= tab_capture_registry
->VerifyRequest(
563 request
.render_process_id
, request
.render_frame_id
, extension
->id());
565 if (request
.audio_type
== content::MEDIA_TAB_AUDIO_CAPTURE
&&
566 tab_capture_allowed
&&
567 extension
->permissions_data()->HasAPIPermission(
568 extensions::APIPermission::kTabCapture
)) {
569 devices
.push_back(content::MediaStreamDevice(
570 content::MEDIA_TAB_AUDIO_CAPTURE
, std::string(), std::string()));
573 if (request
.video_type
== content::MEDIA_TAB_VIDEO_CAPTURE
&&
574 tab_capture_allowed
&&
575 extension
->permissions_data()->HasAPIPermission(
576 extensions::APIPermission::kTabCapture
)) {
577 devices
.push_back(content::MediaStreamDevice(
578 content::MEDIA_TAB_VIDEO_CAPTURE
, std::string(), std::string()));
581 if (!devices
.empty()) {
582 ui
= media_stream_capture_indicator_
->RegisterMediaStream(
583 web_contents
, devices
);
587 devices
.empty() ? content::MEDIA_DEVICE_INVALID_STATE
:
588 content::MEDIA_DEVICE_OK
,
590 #else // defined(ENABLE_EXTENSIONS)
591 callback
.Run(devices
, content::MEDIA_DEVICE_TAB_CAPTURE_FAILURE
, ui
.Pass());
592 #endif // defined(ENABLE_EXTENSIONS)
595 void MediaCaptureDevicesDispatcher::
596 ProcessMediaAccessRequestFromPlatformAppOrExtension(
597 content::WebContents
* web_contents
,
598 const content::MediaStreamRequest
& request
,
599 const content::MediaResponseCallback
& callback
,
600 const extensions::Extension
* extension
) {
602 // TODO(vrk): This code is largely duplicated in
603 // MediaStreamDevicesController::Accept(). Move this code into a shared method
604 // between the two classes.
607 request
.audio_type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
&&
608 extension
->permissions_data()->HasAPIPermission(
609 extensions::APIPermission::kAudioCapture
);
611 request
.video_type
== content::MEDIA_DEVICE_VIDEO_CAPTURE
&&
612 extension
->permissions_data()->HasAPIPermission(
613 extensions::APIPermission::kVideoCapture
);
615 bool get_default_audio_device
= audio_allowed
;
616 bool get_default_video_device
= video_allowed
;
618 content::MediaStreamDevices devices
;
620 // Get the exact audio or video device if an id is specified.
621 if (audio_allowed
&& !request
.requested_audio_device_id
.empty()) {
622 const content::MediaStreamDevice
* audio_device
=
623 GetRequestedAudioDevice(request
.requested_audio_device_id
);
625 devices
.push_back(*audio_device
);
626 get_default_audio_device
= false;
629 if (video_allowed
&& !request
.requested_video_device_id
.empty()) {
630 const content::MediaStreamDevice
* video_device
=
631 GetRequestedVideoDevice(request
.requested_video_device_id
);
633 devices
.push_back(*video_device
);
634 get_default_video_device
= false;
638 // If either or both audio and video devices were requested but not
639 // specified by id, get the default devices.
640 if (get_default_audio_device
|| get_default_video_device
) {
642 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
643 GetDefaultDevicesForProfile(profile
,
644 get_default_audio_device
,
645 get_default_video_device
,
649 scoped_ptr
<content::MediaStreamUI
> ui
;
650 if (!devices
.empty()) {
651 ui
= media_stream_capture_indicator_
->RegisterMediaStream(
652 web_contents
, devices
);
656 devices
.empty() ? content::MEDIA_DEVICE_INVALID_STATE
:
657 content::MEDIA_DEVICE_OK
,
661 void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest(
662 content::WebContents
* web_contents
,
663 const content::MediaStreamRequest
& request
,
664 const content::MediaResponseCallback
& callback
) {
665 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
667 RequestsQueue
& queue
= pending_requests_
[web_contents
];
668 queue
.push_back(PendingAccessRequest(request
, callback
));
670 // If this is the only request then show the infobar.
671 if (queue
.size() == 1)
672 ProcessQueuedAccessRequest(web_contents
);
675 void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(
676 content::WebContents
* web_contents
) {
677 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
679 std::map
<content::WebContents
*, RequestsQueue
>::iterator it
=
680 pending_requests_
.find(web_contents
);
682 if (it
== pending_requests_
.end() || it
->second
.empty()) {
683 // Don't do anything if the tab was closed.
687 DCHECK(!it
->second
.empty());
689 if (PermissionBubbleManager::Enabled() ||
690 MediaStreamPermissionBubbleExperimentEnabled()) {
691 scoped_ptr
<MediaStreamDevicesController
> controller(
692 new MediaStreamDevicesController(web_contents
,
693 it
->second
.front().request
,
694 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse
,
695 base::Unretained(this), web_contents
)));
696 if (controller
->DismissInfoBarAndTakeActionOnSettings())
698 PermissionBubbleManager
* bubble_manager
=
699 PermissionBubbleManager::FromWebContents(web_contents
);
701 bubble_manager
->AddRequest(controller
.release());
705 // TODO(gbillock): delete this block and the MediaStreamInfoBarDelegate
706 // when we've transitioned to bubbles. (crbug/337458)
707 MediaStreamInfoBarDelegate::Create(
708 web_contents
, it
->second
.front().request
,
709 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse
,
710 base::Unretained(this), web_contents
));
713 void MediaCaptureDevicesDispatcher::OnAccessRequestResponse(
714 content::WebContents
* web_contents
,
715 const content::MediaStreamDevices
& devices
,
716 content::MediaStreamRequestResult result
,
717 scoped_ptr
<content::MediaStreamUI
> ui
) {
718 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
720 std::map
<content::WebContents
*, RequestsQueue
>::iterator it
=
721 pending_requests_
.find(web_contents
);
722 if (it
== pending_requests_
.end()) {
723 // WebContents has been destroyed. Don't need to do anything.
727 RequestsQueue
& queue(it
->second
);
731 content::MediaResponseCallback callback
= queue
.front().callback
;
734 if (!queue
.empty()) {
735 // Post a task to process next queued request. It has to be done
736 // asynchronously to make sure that calling infobar is not destroyed until
737 // after this function returns.
738 BrowserThread::PostTask(
739 BrowserThread::UI
, FROM_HERE
,
740 base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest
,
741 base::Unretained(this), web_contents
));
744 callback
.Run(devices
, result
, ui
.Pass());
747 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile(
751 content::MediaStreamDevices
* devices
) {
752 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
753 DCHECK(audio
|| video
);
755 PrefService
* prefs
= profile
->GetPrefs();
756 std::string default_device
;
758 default_device
= prefs
->GetString(prefs::kDefaultAudioCaptureDevice
);
759 const content::MediaStreamDevice
* device
=
760 GetRequestedAudioDevice(default_device
);
762 device
= GetFirstAvailableAudioDevice();
764 devices
->push_back(*device
);
768 default_device
= prefs
->GetString(prefs::kDefaultVideoCaptureDevice
);
769 const content::MediaStreamDevice
* device
=
770 GetRequestedVideoDevice(default_device
);
772 device
= GetFirstAvailableVideoDevice();
774 devices
->push_back(*device
);
778 const content::MediaStreamDevice
*
779 MediaCaptureDevicesDispatcher::GetRequestedAudioDevice(
780 const std::string
& requested_audio_device_id
) {
781 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
782 const content::MediaStreamDevices
& audio_devices
= GetAudioCaptureDevices();
783 const content::MediaStreamDevice
* const device
=
784 FindDeviceWithId(audio_devices
, requested_audio_device_id
);
788 const content::MediaStreamDevice
*
789 MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() {
790 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
791 const content::MediaStreamDevices
& audio_devices
= GetAudioCaptureDevices();
792 if (audio_devices
.empty())
794 return &(*audio_devices
.begin());
797 const content::MediaStreamDevice
*
798 MediaCaptureDevicesDispatcher::GetRequestedVideoDevice(
799 const std::string
& requested_video_device_id
) {
800 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
801 const content::MediaStreamDevices
& video_devices
= GetVideoCaptureDevices();
802 const content::MediaStreamDevice
* const device
=
803 FindDeviceWithId(video_devices
, requested_video_device_id
);
807 const content::MediaStreamDevice
*
808 MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() {
809 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
810 const content::MediaStreamDevices
& video_devices
= GetVideoCaptureDevices();
811 if (video_devices
.empty())
813 return &(*video_devices
.begin());
816 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
817 is_device_enumeration_disabled_
= true;
820 scoped_refptr
<MediaStreamCaptureIndicator
>
821 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
822 return media_stream_capture_indicator_
;
825 DesktopStreamsRegistry
*
826 MediaCaptureDevicesDispatcher::GetDesktopStreamsRegistry() {
827 if (!desktop_streams_registry_
)
828 desktop_streams_registry_
.reset(new DesktopStreamsRegistry());
829 return desktop_streams_registry_
.get();
832 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() {
833 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
834 BrowserThread::PostTask(
835 BrowserThread::UI
, FROM_HERE
,
837 &MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread
,
838 base::Unretained(this)));
841 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() {
842 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
843 BrowserThread::PostTask(
844 BrowserThread::UI
, FROM_HERE
,
846 &MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread
,
847 base::Unretained(this)));
850 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
851 int render_process_id
,
854 const GURL
& security_origin
,
855 content::MediaStreamType stream_type
,
856 content::MediaRequestState state
) {
857 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
858 BrowserThread::PostTask(
859 BrowserThread::UI
, FROM_HERE
,
861 &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread
,
862 base::Unretained(this), render_process_id
, render_frame_id
,
863 page_request_id
, security_origin
, stream_type
, state
));
866 void MediaCaptureDevicesDispatcher::OnAudioStreamPlaying(
867 int render_process_id
,
870 const ReadPowerAndClipCallback
& read_power_callback
) {
871 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
872 #if defined(AUDIO_STREAM_MONITORING)
873 BrowserThread::PostTask(
876 base::Bind(&StartAudioStreamMonitoringOnUIThread
,
880 read_power_callback
));
884 void MediaCaptureDevicesDispatcher::OnAudioStreamStopped(
885 int render_process_id
,
888 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
889 #if defined(AUDIO_STREAM_MONITORING)
890 BrowserThread::PostTask(
893 base::Bind(&StopAudioStreamMonitoringOnUIThread
,
900 void MediaCaptureDevicesDispatcher::OnCreatingAudioStream(
901 int render_process_id
,
902 int render_frame_id
) {
903 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
904 BrowserThread::PostTask(
905 BrowserThread::UI
, FROM_HERE
,
907 &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread
,
908 base::Unretained(this), render_process_id
, render_frame_id
));
911 void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() {
912 MediaStreamDevices devices
= GetAudioCaptureDevices();
913 FOR_EACH_OBSERVER(Observer
, observers_
,
914 OnUpdateAudioDevices(devices
));
917 void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() {
918 MediaStreamDevices devices
= GetVideoCaptureDevices();
919 FOR_EACH_OBSERVER(Observer
, observers_
,
920 OnUpdateVideoDevices(devices
));
923 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
924 int render_process_id
,
927 const GURL
& security_origin
,
928 content::MediaStreamType stream_type
,
929 content::MediaRequestState state
) {
930 // Track desktop capture sessions. Tracking is necessary to avoid unbalanced
931 // session counts since not all requests will reach MEDIA_REQUEST_STATE_DONE,
932 // but they will all reach MEDIA_REQUEST_STATE_CLOSING.
933 if (stream_type
== content::MEDIA_DESKTOP_VIDEO_CAPTURE
) {
934 if (state
== content::MEDIA_REQUEST_STATE_DONE
) {
935 DesktopCaptureSession session
= { render_process_id
, render_frame_id
,
937 desktop_capture_sessions_
.push_back(session
);
938 } else if (state
== content::MEDIA_REQUEST_STATE_CLOSING
) {
939 for (DesktopCaptureSessions::iterator it
=
940 desktop_capture_sessions_
.begin();
941 it
!= desktop_capture_sessions_
.end();
943 if (it
->render_process_id
== render_process_id
&&
944 it
->render_frame_id
== render_frame_id
&&
945 it
->page_request_id
== page_request_id
) {
946 desktop_capture_sessions_
.erase(it
);
953 // Cancel the request.
954 if (state
== content::MEDIA_REQUEST_STATE_CLOSING
) {
956 for (RequestsQueues::iterator rqs_it
= pending_requests_
.begin();
957 rqs_it
!= pending_requests_
.end(); ++rqs_it
) {
958 RequestsQueue
& queue
= rqs_it
->second
;
959 for (RequestsQueue::iterator it
= queue
.begin();
960 it
!= queue
.end(); ++it
) {
961 if (it
->request
.render_process_id
== render_process_id
&&
962 it
->request
.render_frame_id
== render_frame_id
&&
963 it
->request
.page_request_id
== page_request_id
) {
974 #if defined(OS_CHROMEOS)
975 if (IsOriginForCasting(security_origin
) && IsVideoMediaType(stream_type
)) {
976 // Notify ash that casting state has changed.
977 if (state
== content::MEDIA_REQUEST_STATE_DONE
) {
978 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(true);
979 } else if (state
== content::MEDIA_REQUEST_STATE_CLOSING
) {
980 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(false);
985 FOR_EACH_OBSERVER(Observer
, observers_
,
986 OnRequestUpdate(render_process_id
,
992 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread(
993 int render_process_id
,
994 int render_frame_id
) {
995 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
996 FOR_EACH_OBSERVER(Observer
, observers_
,
997 OnCreatingAudioStream(render_process_id
, render_frame_id
));
998 #if defined(AUDIO_STREAM_MONITORING)
999 content::WebContents
* const web_contents
=
1000 content::WebContents::FromRenderFrameHost(
1001 content::RenderFrameHost::FromID(render_process_id
, render_frame_id
));
1003 // Note: Calling CreateForWebContents() multiple times is valid (see usage
1004 // info for content::WebContentsUserData).
1005 AudioStreamMonitor::CreateForWebContents(web_contents
);
1010 bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() {
1011 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1012 return desktop_capture_sessions_
.size() > 0;
1016 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices(
1017 const MediaStreamDevices
& devices
) {
1018 test_audio_devices_
= devices
;
1021 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices(
1022 const MediaStreamDevices
& devices
) {
1023 test_video_devices_
= devices
;