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