Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / chrome / browser / media / media_capture_devices_dispatcher.cc
blob1db0f885648aa47237340cd8d58ce54dea3f901d
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"
65 #endif
67 using content::BrowserThread;
68 using content::MediaCaptureDevices;
69 using content::MediaStreamDevices;
71 namespace {
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")
78 return true;
80 return false;
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) {
90 return &(*iter);
93 return NULL;
96 // This is a short-term solution to grant camera and/or microphone access to
97 // extensions:
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) {
116 return
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.
124 return
125 // Dev
126 origin.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" ||
127 // Canary
128 origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
129 // Beta (internal)
130 origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
131 // Google Cast Beta
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
138 // notification.
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
142 // web.
143 std::string title;
144 if (extension) {
145 title = extension->name();
146 } else {
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,
160 bool capture_audio,
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"));
170 if (capture_audio) {
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,
182 application_title));
183 } else {
184 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
185 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT_DELEGATED,
186 registered_extension_name,
187 application_title));
191 return ui.Pass();
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));
203 if (!web_contents)
204 return NULL;
205 return AudioStreamMonitor::FromWebContents(web_contents);
208 void StartAudioStreamMonitoringOnUIThread(
209 int render_process_id,
210 int render_frame_id,
211 int stream_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,
221 int render_frame_id,
222 int stream_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 =
243 window_list.begin();
244 iter != window_list.end(); ++iter) {
245 if ((*iter)->web_contents() == web_contents)
246 return (*iter)->GetNativeWindow();
249 return NULL;
251 #endif
253 } // namespace
255 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
256 const content::MediaStreamRequest& request,
257 const content::MediaResponseCallback& callback)
258 : request(request),
259 callback(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);
291 #endif
294 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
296 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
297 user_prefs::PrefRegistrySyncable* registry) {
298 registry->RegisterStringPref(
299 prefs::kDefaultAudioCaptureDevice,
300 std::string(),
301 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
302 registry->RegisterStringPref(
303 prefs::kDefaultVideoCaptureDevice,
304 std::string(),
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(
338 int type,
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);
369 } else {
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());
384 return;
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);
392 return;
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;
408 if (main_frame) {
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());
420 return;
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;
427 #endif
429 // Audio is only supported for screen capture streams.
430 bool capture_audio =
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;
457 #endif
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
476 // the given origin.
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);
488 #else
489 gfx::NativeWindow parent_window = NULL;
490 #endif
491 web_contents = 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,
502 application_name);
503 chrome::MessageBoxResult result = chrome::ShowMessageBox(
504 parent_window,
505 l10n_util::GetStringFUTF16(
506 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name),
507 confirmation_text,
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)
518 screen_id =
519 content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
520 webrtc::kFullDesktopScreenId);
521 #endif // !defined(OS_CHROMEOS)
523 bool capture_audio =
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,
533 application_title);
537 callback.Run(
538 devices,
539 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE :
540 content::MEDIA_DEVICE_OK,
541 ui.Pass());
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)
553 Profile* profile =
554 Profile::FromBrowserContext(web_contents->GetBrowserContext());
555 extensions::TabCaptureRegistry* tab_capture_registry =
556 extensions::TabCaptureRegistry::Get(profile);
557 if (!tab_capture_registry) {
558 NOTREACHED();
559 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
560 return;
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);
585 callback.Run(
586 devices,
587 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE :
588 content::MEDIA_DEVICE_OK,
589 ui.Pass());
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.
606 bool audio_allowed =
607 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
608 extension->permissions_data()->HasAPIPermission(
609 extensions::APIPermission::kAudioCapture);
610 bool video_allowed =
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);
624 if (audio_device) {
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);
632 if (video_device) {
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) {
641 Profile* profile =
642 Profile::FromBrowserContext(web_contents->GetBrowserContext());
643 GetDefaultDevicesForProfile(profile,
644 get_default_audio_device,
645 get_default_video_device,
646 &devices);
649 scoped_ptr<content::MediaStreamUI> ui;
650 if (!devices.empty()) {
651 ui = media_stream_capture_indicator_->RegisterMediaStream(
652 web_contents, devices);
654 callback.Run(
655 devices,
656 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE :
657 content::MEDIA_DEVICE_OK,
658 ui.Pass());
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.
684 return;
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())
697 return;
698 PermissionBubbleManager* bubble_manager =
699 PermissionBubbleManager::FromWebContents(web_contents);
700 if (bubble_manager)
701 bubble_manager->AddRequest(controller.release());
702 return;
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.
724 return;
727 RequestsQueue& queue(it->second);
728 if (queue.empty())
729 return;
731 content::MediaResponseCallback callback = queue.front().callback;
732 queue.pop_front();
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(
748 Profile* profile,
749 bool audio,
750 bool video,
751 content::MediaStreamDevices* devices) {
752 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
753 DCHECK(audio || video);
755 PrefService* prefs = profile->GetPrefs();
756 std::string default_device;
757 if (audio) {
758 default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
759 const content::MediaStreamDevice* device =
760 GetRequestedAudioDevice(default_device);
761 if (!device)
762 device = GetFirstAvailableAudioDevice();
763 if (device)
764 devices->push_back(*device);
767 if (video) {
768 default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
769 const content::MediaStreamDevice* device =
770 GetRequestedVideoDevice(default_device);
771 if (!device)
772 device = GetFirstAvailableVideoDevice();
773 if (device)
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);
785 return device;
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())
793 return NULL;
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);
804 return device;
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())
812 return NULL;
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,
836 base::Bind(
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,
845 base::Bind(
846 &MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread,
847 base::Unretained(this)));
850 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
851 int render_process_id,
852 int render_frame_id,
853 int page_request_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,
860 base::Bind(
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,
868 int render_frame_id,
869 int stream_id,
870 const ReadPowerAndClipCallback& read_power_callback) {
871 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
872 #if defined(AUDIO_STREAM_MONITORING)
873 BrowserThread::PostTask(
874 BrowserThread::UI,
875 FROM_HERE,
876 base::Bind(&StartAudioStreamMonitoringOnUIThread,
877 render_process_id,
878 render_frame_id,
879 stream_id,
880 read_power_callback));
881 #endif
884 void MediaCaptureDevicesDispatcher::OnAudioStreamStopped(
885 int render_process_id,
886 int render_frame_id,
887 int stream_id) {
888 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
889 #if defined(AUDIO_STREAM_MONITORING)
890 BrowserThread::PostTask(
891 BrowserThread::UI,
892 FROM_HERE,
893 base::Bind(&StopAudioStreamMonitoringOnUIThread,
894 render_process_id,
895 render_frame_id,
896 stream_id));
897 #endif
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,
906 base::Bind(
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,
925 int render_frame_id,
926 int page_request_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,
936 page_request_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();
942 ++it) {
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);
947 break;
953 // Cancel the request.
954 if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
955 bool found = false;
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) {
964 queue.erase(it);
965 found = true;
966 break;
969 if (found)
970 break;
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);
983 #endif
985 FOR_EACH_OBSERVER(Observer, observers_,
986 OnRequestUpdate(render_process_id,
987 render_frame_id,
988 stream_type,
989 state));
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));
1002 if (web_contents) {
1003 // Note: Calling CreateForWebContents() multiple times is valid (see usage
1004 // info for content::WebContentsUserData).
1005 AudioStreamMonitor::CreateForWebContents(web_contents);
1007 #endif
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;