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/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"
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 using content::BrowserThread;
64 using content::MediaCaptureDevices;
65 using content::MediaStreamDevices;
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")
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) {
92 // This is a short-term solution to grant camera and/or microphone access to
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";
109 bool IsBuiltInExtension(const GURL& origin) {
111 // Feedback Extension.
112 origin.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/";
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.
121 origin.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" ||
123 origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
125 origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
127 origin.spec() == "chrome-extension://dliochdbjfkdbacpmhlcpmleaejidimm/" ||
128 // Google Cast Stable
129 origin.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/";
135 // Helper to get title of the calling application shown in the screen capture
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
143 title = extension->name();
145 GURL url = web_contents->GetURL();
146 title = url.SchemeIsSecure() ? net::GetHostAndOptionalPort(url)
147 : url.GetOrigin().spec();
149 return base::UTF8ToUTF16(title);
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,
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;
165 // Add selected desktop source to the list.
166 devices.push_back(content::MediaStreamDevice(
167 content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen"));
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"));
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,
182 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
183 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT_DELEGATED,
184 registered_extension_name,
192 #if defined(AUDIO_STREAM_MONITORING)
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));
203 return AudioStreamMonitor::FromWebContents(web_contents);
206 void StartAudioStreamMonitoringOnUIThread(
207 int render_process_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);
217 void StopAudioStreamMonitoringOnUIThread(
218 int render_process_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);
227 #endif // defined(AUDIO_STREAM_MONITORING)
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();
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 =
242 iter != window_list.end(); ++iter) {
243 if ((*iter)->web_contents() == web_contents)
244 return (*iter)->GetNativeWindow();
253 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
254 const content::MediaStreamRequest& request,
255 const content::MediaResponseCallback& callback)
260 MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {}
262 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
263 return Singleton<MediaCaptureDevicesDispatcher>::get();
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());
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);
290 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
292 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
293 user_prefs::PrefRegistrySyncable* registry) {
294 registry->RegisterStringPref(
295 prefs::kDefaultAudioCaptureDevice,
297 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
298 registry->RegisterStringPref(
299 prefs::kDefaultVideoCaptureDevice,
301 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
304 void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) {
305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
306 if (!observers_.HasObserver(observer))
307 observers_.AddObserver(observer);
310 void MediaCaptureDevicesDispatcher::RemoveObserver(Observer* observer) {
311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
312 observers_.RemoveObserver(observer);
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_;
321 return MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices();
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_;
330 return MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices();
333 void MediaCaptureDevicesDispatcher::Observe(
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);
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));
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);
366 ProcessRegularMediaAccessRequest(web_contents, request, callback);
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;
378 if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
379 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
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);
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);
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());
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;
412 // Audio is only supported for screen capture streams.
414 (media_id.type == content::DesktopMediaID::TYPE_SCREEN &&
415 request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE &&
416 loopback_audio_supported);
418 ui = GetDevicesForDesktopCapture(
419 devices, media_id, capture_audio, true,
420 GetApplicationTitle(web_contents, extension),
421 base::UTF8ToUTF16(original_extension_name));
423 callback.Run(devices, content::MEDIA_DEVICE_OK, ui.Pass());
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;
434 DCHECK_EQ(request.video_type, content::MEDIA_DESKTOP_VIDEO_CAPTURE);
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;
442 const bool component_extension =
443 extension && extension->location() == extensions::Manifest::COMPONENT;
445 const bool screen_capture_enabled =
446 CommandLine::ForCurrentProcess()->HasSwitch(
447 switches::kEnableUserMediaScreenCapturing) ||
448 IsOriginForCasting(request.security_origin) ||
449 IsBuiltInExtension(request.security_origin);
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);
457 // Approve request only when the following conditions are met:
458 // 1. Screen capturing is enabled via command line switch or white-listed for
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);
472 gfx::NativeWindow parent_window = NULL;
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,
486 chrome::MessageBoxResult result = chrome::ShowMessageBox(
488 l10n_util::GetStringFUTF16(
489 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name),
491 chrome::MESSAGE_BOX_TYPE_QUESTION);
492 user_approved = (result == chrome::MESSAGE_BOX_RESULT_YES);
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)
502 content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
503 webrtc::kFullDesktopScreenId);
504 #endif // !defined(OS_CHROMEOS)
507 (request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE &&
508 loopback_audio_supported);
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;
514 ui = GetDevicesForDesktopCapture(devices, screen_id, capture_audio,
515 display_notification, application_title,
522 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE :
523 content::MEDIA_DEVICE_OK,
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;
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)
540 Profile::FromBrowserContext(web_contents->GetBrowserContext());
541 extensions::TabCaptureRegistry* tab_capture_registry =
542 extensions::TabCaptureRegistry::Get(profile);
543 if (!tab_capture_registry) {
545 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
548 bool tab_capture_allowed =
549 tab_capture_registry->VerifyRequest(request.render_process_id,
550 request.render_view_id);
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()));
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()));
568 if (!devices.empty()) {
569 ui = media_stream_capture_indicator_->RegisterMediaStream(
570 web_contents, devices);
574 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE :
575 content::MEDIA_DEVICE_OK,
577 #endif // !defined(OS_ANDROID)
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 content::MediaStreamDevices devices;
588 Profile::FromBrowserContext(web_contents->GetBrowserContext());
590 if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
591 extension->permissions_data()->HasAPIPermission(
592 extensions::APIPermission::kAudioCapture)) {
593 GetDefaultDevicesForProfile(profile, true, false, &devices);
596 if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
597 extension->permissions_data()->HasAPIPermission(
598 extensions::APIPermission::kVideoCapture)) {
599 GetDefaultDevicesForProfile(profile, false, true, &devices);
602 scoped_ptr<content::MediaStreamUI> ui;
603 if (!devices.empty()) {
604 ui = media_stream_capture_indicator_->RegisterMediaStream(
605 web_contents, devices);
609 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE :
610 content::MEDIA_DEVICE_OK,
614 void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest(
615 content::WebContents* web_contents,
616 const content::MediaStreamRequest& request,
617 const content::MediaResponseCallback& callback) {
618 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
620 RequestsQueue& queue = pending_requests_[web_contents];
621 queue.push_back(PendingAccessRequest(request, callback));
623 // If this is the only request then show the infobar.
624 if (queue.size() == 1)
625 ProcessQueuedAccessRequest(web_contents);
628 void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(
629 content::WebContents* web_contents) {
630 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
632 std::map<content::WebContents*, RequestsQueue>::iterator it =
633 pending_requests_.find(web_contents);
635 if (it == pending_requests_.end() || it->second.empty()) {
636 // Don't do anything if the tab was closed.
640 DCHECK(!it->second.empty());
642 if (PermissionBubbleManager::Enabled() ||
643 MediaStreamPermissionBubbleExperimentEnabled()) {
644 scoped_ptr<MediaStreamDevicesController> controller(
645 new MediaStreamDevicesController(web_contents,
646 it->second.front().request,
647 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse,
648 base::Unretained(this), web_contents)));
649 if (controller->DismissInfoBarAndTakeActionOnSettings())
651 PermissionBubbleManager* bubble_manager =
652 PermissionBubbleManager::FromWebContents(web_contents);
654 bubble_manager->AddRequest(controller.release());
658 // TODO(gbillock): delete this block and the MediaStreamInfoBarDelegate
659 // when we've transitioned to bubbles. (crbug/337458)
660 MediaStreamInfoBarDelegate::Create(
661 web_contents, it->second.front().request,
662 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse,
663 base::Unretained(this), web_contents));
666 void MediaCaptureDevicesDispatcher::OnAccessRequestResponse(
667 content::WebContents* web_contents,
668 const content::MediaStreamDevices& devices,
669 content::MediaStreamRequestResult result,
670 scoped_ptr<content::MediaStreamUI> ui) {
671 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
673 std::map<content::WebContents*, RequestsQueue>::iterator it =
674 pending_requests_.find(web_contents);
675 if (it == pending_requests_.end()) {
676 // WebContents has been destroyed. Don't need to do anything.
680 RequestsQueue& queue(it->second);
684 content::MediaResponseCallback callback = queue.front().callback;
687 if (!queue.empty()) {
688 // Post a task to process next queued request. It has to be done
689 // asynchronously to make sure that calling infobar is not destroyed until
690 // after this function returns.
691 BrowserThread::PostTask(
692 BrowserThread::UI, FROM_HERE,
693 base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest,
694 base::Unretained(this), web_contents));
697 callback.Run(devices, result, ui.Pass());
700 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile(
704 content::MediaStreamDevices* devices) {
705 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
706 DCHECK(audio || video);
708 PrefService* prefs = profile->GetPrefs();
709 std::string default_device;
711 default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
712 const content::MediaStreamDevice* device =
713 GetRequestedAudioDevice(default_device);
715 device = GetFirstAvailableAudioDevice();
717 devices->push_back(*device);
721 default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
722 const content::MediaStreamDevice* device =
723 GetRequestedVideoDevice(default_device);
725 device = GetFirstAvailableVideoDevice();
727 devices->push_back(*device);
731 const content::MediaStreamDevice*
732 MediaCaptureDevicesDispatcher::GetRequestedAudioDevice(
733 const std::string& requested_audio_device_id) {
734 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
735 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
736 const content::MediaStreamDevice* const device =
737 FindDeviceWithId(audio_devices, requested_audio_device_id);
741 const content::MediaStreamDevice*
742 MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() {
743 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
744 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
745 if (audio_devices.empty())
747 return &(*audio_devices.begin());
750 const content::MediaStreamDevice*
751 MediaCaptureDevicesDispatcher::GetRequestedVideoDevice(
752 const std::string& requested_video_device_id) {
753 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
754 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
755 const content::MediaStreamDevice* const device =
756 FindDeviceWithId(video_devices, requested_video_device_id);
760 const content::MediaStreamDevice*
761 MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() {
762 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
763 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
764 if (video_devices.empty())
766 return &(*video_devices.begin());
769 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
770 is_device_enumeration_disabled_ = true;
773 scoped_refptr<MediaStreamCaptureIndicator>
774 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
775 return media_stream_capture_indicator_;
778 DesktopStreamsRegistry*
779 MediaCaptureDevicesDispatcher::GetDesktopStreamsRegistry() {
780 if (!desktop_streams_registry_)
781 desktop_streams_registry_.reset(new DesktopStreamsRegistry());
782 return desktop_streams_registry_.get();
785 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() {
786 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
787 BrowserThread::PostTask(
788 BrowserThread::UI, FROM_HERE,
790 &MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread,
791 base::Unretained(this)));
794 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() {
795 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
796 BrowserThread::PostTask(
797 BrowserThread::UI, FROM_HERE,
799 &MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread,
800 base::Unretained(this)));
803 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
804 int render_process_id,
807 const GURL& security_origin,
808 const content::MediaStreamDevice& device,
809 content::MediaRequestState state) {
810 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
811 BrowserThread::PostTask(
812 BrowserThread::UI, FROM_HERE,
814 &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread,
815 base::Unretained(this), render_process_id, render_view_id,
816 page_request_id, security_origin, device, state));
819 void MediaCaptureDevicesDispatcher::OnAudioStreamPlaying(
820 int render_process_id,
823 const ReadPowerAndClipCallback& read_power_callback) {
824 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
825 #if defined(AUDIO_STREAM_MONITORING)
826 BrowserThread::PostTask(
829 base::Bind(&StartAudioStreamMonitoringOnUIThread,
833 read_power_callback));
837 void MediaCaptureDevicesDispatcher::OnAudioStreamStopped(
838 int render_process_id,
841 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
842 #if defined(AUDIO_STREAM_MONITORING)
843 BrowserThread::PostTask(
846 base::Bind(&StopAudioStreamMonitoringOnUIThread,
853 void MediaCaptureDevicesDispatcher::OnCreatingAudioStream(
854 int render_process_id,
855 int render_frame_id) {
856 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
857 BrowserThread::PostTask(
858 BrowserThread::UI, FROM_HERE,
860 &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread,
861 base::Unretained(this), render_process_id, render_frame_id));
864 void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() {
865 MediaStreamDevices devices = GetAudioCaptureDevices();
866 FOR_EACH_OBSERVER(Observer, observers_,
867 OnUpdateAudioDevices(devices));
870 void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() {
871 MediaStreamDevices devices = GetVideoCaptureDevices();
872 FOR_EACH_OBSERVER(Observer, observers_,
873 OnUpdateVideoDevices(devices));
876 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
877 int render_process_id,
880 const GURL& security_origin,
881 const content::MediaStreamDevice& device,
882 content::MediaRequestState state) {
883 // Track desktop capture sessions. Tracking is necessary to avoid unbalanced
884 // session counts since not all requests will reach MEDIA_REQUEST_STATE_DONE,
885 // but they will all reach MEDIA_REQUEST_STATE_CLOSING.
886 if (device.type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
887 if (state == content::MEDIA_REQUEST_STATE_DONE) {
888 DesktopCaptureSession session = { render_process_id, render_view_id,
890 desktop_capture_sessions_.push_back(session);
891 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
892 for (DesktopCaptureSessions::iterator it =
893 desktop_capture_sessions_.begin();
894 it != desktop_capture_sessions_.end();
896 if (it->render_process_id == render_process_id &&
897 it->render_view_id == render_view_id &&
898 it->page_request_id == page_request_id) {
899 desktop_capture_sessions_.erase(it);
906 // Cancel the request.
907 if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
909 for (RequestsQueues::iterator rqs_it = pending_requests_.begin();
910 rqs_it != pending_requests_.end(); ++rqs_it) {
911 RequestsQueue& queue = rqs_it->second;
912 for (RequestsQueue::iterator it = queue.begin();
913 it != queue.end(); ++it) {
914 if (it->request.render_process_id == render_process_id &&
915 it->request.render_view_id == render_view_id &&
916 it->request.page_request_id == page_request_id) {
927 #if defined(OS_CHROMEOS)
928 if (IsOriginForCasting(security_origin) && IsVideoMediaType(device.type)) {
929 // Notify ash that casting state has changed.
930 if (state == content::MEDIA_REQUEST_STATE_DONE) {
931 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(true);
932 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
933 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(false);
938 FOR_EACH_OBSERVER(Observer, observers_,
939 OnRequestUpdate(render_process_id,
945 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread(
946 int render_process_id,
947 int render_frame_id) {
948 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
949 FOR_EACH_OBSERVER(Observer, observers_,
950 OnCreatingAudioStream(render_process_id, render_frame_id));
951 #if defined(AUDIO_STREAM_MONITORING)
952 content::WebContents* const web_contents =
953 content::WebContents::FromRenderFrameHost(
954 content::RenderFrameHost::FromID(render_process_id, render_frame_id));
956 // Note: Calling CreateForWebContents() multiple times is valid (see usage
957 // info for content::WebContentsUserData).
958 AudioStreamMonitor::CreateForWebContents(web_contents);
963 bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() {
964 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
965 return desktop_capture_sessions_.size() > 0;
969 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices(
970 const MediaStreamDevices& devices) {
971 test_audio_devices_ = devices;
974 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices(
975 const MediaStreamDevices& devices) {
976 test_video_devices_ = devices;