[Cronet] Delay StartNetLog and StopNetLog until native request context is initialized
[chromium-blink-merge.git] / chrome / browser / media / media_capture_devices_dispatcher.cc
blobcba2d992c2e7fa046ffe656267104bfd8046c1f5
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 "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/metrics/field_trial.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/prefs/scoped_user_pref_update.h"
12 #include "base/sha1.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/media/desktop_streams_registry.h"
17 #include "chrome/browser/media/media_stream_capture_indicator.h"
18 #include "chrome/browser/media/media_stream_device_permissions.h"
19 #include "chrome/browser/media/media_stream_infobar_delegate.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_finder.h"
23 #include "chrome/browser/ui/browser_window.h"
24 #include "chrome/browser/ui/screen_capture_notification_ui.h"
25 #include "chrome/browser/ui/simple_message_box.h"
26 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/chrome_version_info.h"
29 #include "chrome/common/pref_names.h"
30 #include "chrome/grit/generated_resources.h"
31 #include "components/content_settings/core/browser/content_settings_provider.h"
32 #include "components/content_settings/core/browser/host_content_settings_map.h"
33 #include "components/pref_registry/pref_registry_syncable.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/desktop_media_id.h"
36 #include "content/public/browser/media_capture_devices.h"
37 #include "content/public/browser/notification_service.h"
38 #include "content/public/browser/notification_source.h"
39 #include "content/public/browser/notification_types.h"
40 #include "content/public/browser/render_frame_host.h"
41 #include "content/public/browser/render_process_host.h"
42 #include "content/public/browser/web_contents.h"
43 #include "content/public/common/media_stream_request.h"
44 #include "extensions/common/constants.h"
45 #include "media/audio/audio_manager_base.h"
46 #include "media/base/media_switches.h"
47 #include "net/base/net_util.h"
48 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"
49 #include "ui/base/l10n/l10n_util.h"
51 #if defined(OS_CHROMEOS)
52 #include "ash/shell.h"
53 #endif // defined(OS_CHROMEOS)
55 #if defined(ENABLE_EXTENSIONS)
56 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
57 #include "extensions/browser/app_window/app_window.h"
58 #include "extensions/browser/app_window/app_window_registry.h"
59 #include "extensions/browser/extension_registry.h"
60 #include "extensions/common/extension.h"
61 #include "extensions/common/permissions/permissions_data.h"
62 #endif
64 using content::BrowserThread;
65 using content::MediaCaptureDevices;
66 using content::MediaStreamDevices;
68 namespace {
70 // A finch experiment to enable the permission bubble for media requests only.
71 bool MediaStreamPermissionBubbleExperimentEnabled() {
72 const std::string group =
73 base::FieldTrialList::FindFullName("MediaStreamPermissionBubble");
74 if (group == "enabled")
75 return true;
77 return false;
80 // Finds a device in |devices| that has |device_id|, or NULL if not found.
81 const content::MediaStreamDevice* FindDeviceWithId(
82 const content::MediaStreamDevices& devices,
83 const std::string& device_id) {
84 content::MediaStreamDevices::const_iterator iter = devices.begin();
85 for (; iter != devices.end(); ++iter) {
86 if (iter->id == device_id) {
87 return &(*iter);
90 return NULL;
93 #if defined(ENABLE_EXTENSIONS)
94 // This is a short-term solution to grant camera and/or microphone access to
95 // extensions:
96 // 1. Virtual keyboard extension.
97 // 2. Flutter gesture recognition extension.
98 // 3. TODO(smus): Airbender experiment 1.
99 // 4. TODO(smus): Airbender experiment 2.
100 // 5. Hotwording component extension.
101 // 6. XKB input method component extension.
102 // 7. M17n/T13n/CJK input method component extension.
103 // Once http://crbug.com/292856 is fixed, remove this whitelist.
104 bool IsMediaRequestWhitelistedForExtension(
105 const extensions::Extension* extension) {
106 return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" ||
107 extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" ||
108 extension->id() == "clffjmdilanldobdnedchkdbofoimcgb" ||
109 extension->id() == "nnckehldicaciogcbchegobnafnjkcne" ||
110 extension->id() == "nbpagnldghgfoolbancepceaanlmhfmd" ||
111 extension->id() == "jkghodnilhceideoidjikpgommlajknk" ||
112 extension->id() == "gjaehgfemfahhmlgpdfknkhdnemmolop";
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/" ||
135 // http://crbug.com/457908
136 origin.spec() == "chrome-extension://ekpaaapppgpmolpcldedioblbkmijaca/" ||
137 origin.spec() == "chrome-extension://fjhoaacokmgbjemoflkofnenfaiekifl/";
140 bool IsExtensionWhitelistedForScreenCapture(
141 const extensions::Extension* extension) {
142 #if defined(OS_CHROMEOS)
143 std::string hash = base::SHA1HashString(extension->id());
144 std::string hex_hash = base::HexEncode(hash.c_str(), hash.length());
146 // crbug.com/446688
147 return hex_hash == "4F25792AF1AA7483936DE29C07806F203C7170A0" ||
148 hex_hash == "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9" ||
149 hex_hash == "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB" ||
150 hex_hash == "81986D4F846CEDDDB962643FA501D1780DD441BB";
151 #else
152 return false;
153 #endif // defined(OS_CHROMEOS)
155 #endif // defined(ENABLE_EXTENSIONS)
157 // Helper to get title of the calling application shown in the screen capture
158 // notification.
159 base::string16 GetApplicationTitle(content::WebContents* web_contents,
160 const extensions::Extension* extension) {
161 // Use extension name as title for extensions and host/origin for drive-by
162 // web.
163 std::string title;
164 #if defined(ENABLE_EXTENSIONS)
165 if (extension) {
166 title = extension->name();
167 return base::UTF8ToUTF16(title);
169 #endif
170 GURL url = web_contents->GetURL();
171 title = url.SchemeIsSecure() ? net::GetHostAndOptionalPort(url)
172 : url.GetOrigin().spec();
173 return base::UTF8ToUTF16(title);
176 // Helper to get list of media stream devices for desktop capture in |devices|.
177 // Registers to display notification if |display_notification| is true.
178 // Returns an instance of MediaStreamUI to be passed to content layer.
179 scoped_ptr<content::MediaStreamUI> GetDevicesForDesktopCapture(
180 content::MediaStreamDevices* devices,
181 content::DesktopMediaID media_id,
182 bool capture_audio,
183 bool display_notification,
184 const base::string16& application_title,
185 const base::string16& registered_extension_name) {
186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
187 scoped_ptr<content::MediaStreamUI> ui;
189 // Add selected desktop source to the list.
190 devices->push_back(content::MediaStreamDevice(
191 content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen"));
192 if (capture_audio) {
193 // Use the special loopback device ID for system audio capture.
194 devices->push_back(content::MediaStreamDevice(
195 content::MEDIA_DESKTOP_AUDIO_CAPTURE,
196 media::AudioManagerBase::kLoopbackInputDeviceId, "System Audio"));
199 // If required, register to display the notification for stream capture.
200 if (display_notification) {
201 if (application_title == registered_extension_name) {
202 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
203 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT,
204 application_title));
205 } else {
206 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
207 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT_DELEGATED,
208 registered_extension_name,
209 application_title));
213 return ui.Pass();
216 #if !defined(OS_ANDROID)
217 // Find browser or app window from a given |web_contents|.
218 gfx::NativeWindow FindParentWindowForWebContents(
219 content::WebContents* web_contents) {
220 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
221 if (browser && browser->window())
222 return browser->window()->GetNativeWindow();
224 const extensions::AppWindowRegistry::AppWindowList& window_list =
225 extensions::AppWindowRegistry::Get(
226 web_contents->GetBrowserContext())->app_windows();
227 for (extensions::AppWindowRegistry::AppWindowList::const_iterator iter =
228 window_list.begin();
229 iter != window_list.end(); ++iter) {
230 if ((*iter)->web_contents() == web_contents)
231 return (*iter)->GetNativeWindow();
234 return NULL;
236 #endif
238 #if defined(ENABLE_EXTENSIONS)
239 const extensions::Extension* GetExtensionForOrigin(
240 Profile* profile,
241 const GURL& security_origin) {
242 if (!security_origin.SchemeIs(extensions::kExtensionScheme))
243 return NULL;
245 const extensions::Extension* extension =
246 extensions::ExtensionRegistry::Get(profile)->enabled_extensions().GetByID(
247 security_origin.host());
248 DCHECK(extension);
249 return extension;
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 #if defined(OS_MACOSX)
281 // AVFoundation is used for video/audio device monitoring and video capture.
282 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
283 switches::kForceQTKit)) {
284 base::CommandLine::ForCurrentProcess()->AppendSwitch(
285 switches::kEnableAVFoundation);
287 #endif
290 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
292 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
293 user_prefs::PrefRegistrySyncable* registry) {
294 registry->RegisterStringPref(
295 prefs::kDefaultAudioCaptureDevice,
296 std::string(),
297 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
298 registry->RegisterStringPref(
299 prefs::kDefaultVideoCaptureDevice,
300 std::string(),
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(
334 int type,
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_DESKTOP_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 {
361 #if defined(ENABLE_EXTENSIONS)
362 bool is_whitelisted =
363 extension && (extension->is_platform_app() ||
364 IsMediaRequestWhitelistedForExtension(extension));
365 if (is_whitelisted) {
366 // For extensions access is approved based on extension permissions.
367 ProcessMediaAccessRequestFromPlatformAppOrExtension(
368 web_contents, request, callback, extension);
369 return;
371 #endif
372 ProcessRegularMediaAccessRequest(web_contents, request, callback);
376 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
377 content::BrowserContext* browser_context,
378 const GURL& security_origin,
379 content::MediaStreamType type) {
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
381 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
382 type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
384 Profile* profile = Profile::FromBrowserContext(browser_context);
385 #if defined(ENABLE_EXTENSIONS)
386 const extensions::Extension* extension =
387 GetExtensionForOrigin(profile, security_origin);
389 if (extension && (extension->is_platform_app() ||
390 IsMediaRequestWhitelistedForExtension(extension))) {
391 return extension->permissions_data()->HasAPIPermission(
392 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
393 ? extensions::APIPermission::kAudioCapture
394 : extensions::APIPermission::kVideoCapture);
396 #endif
398 ContentSettingsType contentSettingsType =
399 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
400 ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
401 : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA;
403 if (CheckAllowAllMediaStreamContentForOrigin(
404 profile, security_origin, contentSettingsType)) {
405 return true;
408 const char* policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE
409 ? prefs::kAudioCaptureAllowed
410 : prefs::kVideoCaptureAllowed;
411 const char* list_policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE
412 ? prefs::kAudioCaptureAllowedUrls
413 : prefs::kVideoCaptureAllowedUrls;
414 if (GetDevicePolicy(
415 profile, security_origin, policy_name, list_policy_name) ==
416 ALWAYS_ALLOW) {
417 return true;
420 // There's no secondary URL for these content types, hence duplicating
421 // |security_origin|.
422 if (profile->GetHostContentSettingsMap()->GetContentSetting(
423 security_origin,
424 security_origin,
425 contentSettingsType,
426 NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_ALLOW) {
427 return true;
430 return false;
433 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
434 content::WebContents* web_contents,
435 const GURL& security_origin,
436 content::MediaStreamType type) {
437 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
438 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
439 type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
441 Profile* profile =
442 Profile::FromBrowserContext(web_contents->GetBrowserContext());
444 ContentSettingsType contentSettingsType =
445 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
446 ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
447 : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA;
449 if (CheckAllowAllMediaStreamContentForOrigin(
450 profile, security_origin, contentSettingsType)) {
451 return true;
454 const char* policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE
455 ? prefs::kAudioCaptureAllowed
456 : prefs::kVideoCaptureAllowed;
457 const char* list_policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE
458 ? prefs::kAudioCaptureAllowedUrls
459 : prefs::kVideoCaptureAllowedUrls;
460 if (GetDevicePolicy(
461 profile, security_origin, policy_name, list_policy_name) ==
462 ALWAYS_ALLOW) {
463 return true;
466 // There's no secondary URL for these content types, hence duplicating
467 // |security_origin|.
468 if (profile->GetHostContentSettingsMap()->GetContentSetting(
469 security_origin,
470 security_origin,
471 contentSettingsType,
472 NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_ALLOW) {
473 return true;
476 return false;
479 #if defined(ENABLE_EXTENSIONS)
480 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
481 content::WebContents* web_contents,
482 const GURL& security_origin,
483 content::MediaStreamType type,
484 const extensions::Extension* extension) {
485 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
486 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
487 type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
489 if (extension->is_platform_app() ||
490 IsMediaRequestWhitelistedForExtension(extension)) {
491 return extension->permissions_data()->HasAPIPermission(
492 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
493 ? extensions::APIPermission::kAudioCapture
494 : extensions::APIPermission::kVideoCapture);
497 return CheckMediaAccessPermission(web_contents, security_origin, type);
499 #endif
501 void MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest(
502 content::WebContents* web_contents,
503 const content::MediaStreamRequest& request,
504 const content::MediaResponseCallback& callback,
505 const extensions::Extension* extension) {
506 content::MediaStreamDevices devices;
507 scoped_ptr<content::MediaStreamUI> ui;
509 if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
510 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
511 return;
514 // If the device id wasn't specified then this is a screen capture request
515 // (i.e. chooseDesktopMedia() API wasn't used to generate device id).
516 if (request.requested_video_device_id.empty()) {
517 ProcessScreenCaptureAccessRequest(
518 web_contents, request, callback, extension);
519 return;
522 // The extension name that the stream is registered with.
523 std::string original_extension_name;
524 // Resolve DesktopMediaID for the specified device id.
525 content::DesktopMediaID media_id;
526 // TODO(miu): Replace "main RenderFrame" IDs with the request's actual
527 // RenderFrame IDs once the desktop capture extension API implementation is
528 // fixed. http://crbug.com/304341
529 content::WebContents* const web_contents_for_stream =
530 content::WebContents::FromRenderFrameHost(
531 content::RenderFrameHost::FromID(request.render_process_id,
532 request.render_frame_id));
533 content::RenderFrameHost* const main_frame = web_contents_for_stream ?
534 web_contents_for_stream->GetMainFrame() : NULL;
535 if (main_frame) {
536 media_id = GetDesktopStreamsRegistry()->RequestMediaForStreamId(
537 request.requested_video_device_id,
538 main_frame->GetProcess()->GetID(),
539 main_frame->GetRoutingID(),
540 request.security_origin,
541 &original_extension_name);
544 // Received invalid device id.
545 if (media_id.type == content::DesktopMediaID::TYPE_NONE) {
546 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
547 return;
550 bool loopback_audio_supported = false;
551 #if defined(USE_CRAS) || defined(OS_WIN)
552 // Currently loopback audio capture is supported only on Windows and ChromeOS.
553 loopback_audio_supported = true;
554 #endif
556 // Audio is only supported for screen capture streams.
557 bool capture_audio =
558 (media_id.type == content::DesktopMediaID::TYPE_SCREEN &&
559 request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE &&
560 loopback_audio_supported);
562 ui = GetDevicesForDesktopCapture(
563 &devices, media_id, capture_audio, true,
564 GetApplicationTitle(web_contents, extension),
565 base::UTF8ToUTF16(original_extension_name));
567 callback.Run(devices, content::MEDIA_DEVICE_OK, ui.Pass());
570 void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest(
571 content::WebContents* web_contents,
572 const content::MediaStreamRequest& request,
573 const content::MediaResponseCallback& callback,
574 const extensions::Extension* extension) {
575 content::MediaStreamDevices devices;
576 scoped_ptr<content::MediaStreamUI> ui;
578 DCHECK_EQ(request.video_type, content::MEDIA_DESKTOP_VIDEO_CAPTURE);
580 bool loopback_audio_supported = false;
581 #if defined(USE_CRAS) || defined(OS_WIN)
582 // Currently loopback audio capture is supported only on Windows and ChromeOS.
583 loopback_audio_supported = true;
584 #endif
586 bool component_extension = false;
587 #if defined(ENABLE_EXTENSIONS)
588 component_extension =
589 extension && extension->location() == extensions::Manifest::COMPONENT;
590 #endif
592 bool screen_capture_enabled =
593 base::CommandLine::ForCurrentProcess()->HasSwitch(
594 switches::kEnableUserMediaScreenCapturing);
595 #if defined(ENABLE_EXTENSIONS)
596 screen_capture_enabled |=
597 IsOriginForCasting(request.security_origin) ||
598 IsExtensionWhitelistedForScreenCapture(extension) ||
599 IsBuiltInExtension(request.security_origin);
600 #endif
602 const bool origin_is_secure =
603 request.security_origin.SchemeIsSecure() ||
604 request.security_origin.SchemeIs(extensions::kExtensionScheme) ||
605 base::CommandLine::ForCurrentProcess()->HasSwitch(
606 switches::kAllowHttpScreenCapture);
608 // If basic conditions (screen capturing is enabled and origin is secure)
609 // aren't fulfilled, we'll use "invalid state" as result. Otherwise, we set
610 // it after checking permission.
611 // TODO(grunell): It would be good to change this result for something else,
612 // probably a new one.
613 content::MediaStreamRequestResult result =
614 content::MEDIA_DEVICE_INVALID_STATE;
616 // Approve request only when the following conditions are met:
617 // 1. Screen capturing is enabled via command line switch or white-listed for
618 // the given origin.
619 // 2. Request comes from a page with a secure origin or from an extension.
620 if (screen_capture_enabled && origin_is_secure) {
621 // Get title of the calling application prior to showing the message box.
622 // chrome::ShowMessageBox() starts a nested message loop which may allow
623 // |web_contents| to be destroyed on the UI thread before the message box
624 // is closed. See http://crbug.com/326690.
625 base::string16 application_title =
626 GetApplicationTitle(web_contents, extension);
627 #if !defined(OS_ANDROID)
628 gfx::NativeWindow parent_window =
629 FindParentWindowForWebContents(web_contents);
630 #else
631 gfx::NativeWindow parent_window = NULL;
632 #endif
633 web_contents = NULL;
635 bool whitelisted_extension = false;
636 #if defined(ENABLE_EXTENSIONS)
637 whitelisted_extension = IsExtensionWhitelistedForScreenCapture(
638 extension);
639 #endif
641 // For whitelisted or component extensions, bypass message box.
642 bool user_approved = false;
643 if (!whitelisted_extension && !component_extension) {
644 base::string16 application_name =
645 base::UTF8ToUTF16(request.security_origin.spec());
646 #if defined(ENABLE_EXTENSIONS)
647 if (extension)
648 application_name = base::UTF8ToUTF16(extension->name());
649 #endif
650 base::string16 confirmation_text = l10n_util::GetStringFUTF16(
651 request.audio_type == content::MEDIA_NO_SERVICE ?
652 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT :
653 IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT,
654 application_name);
655 chrome::MessageBoxResult result = chrome::ShowMessageBox(
656 parent_window,
657 l10n_util::GetStringFUTF16(
658 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name),
659 confirmation_text,
660 chrome::MESSAGE_BOX_TYPE_QUESTION);
661 user_approved = (result == chrome::MESSAGE_BOX_RESULT_YES);
664 if (user_approved || component_extension || whitelisted_extension) {
665 content::DesktopMediaID screen_id;
666 #if defined(OS_CHROMEOS)
667 screen_id = content::DesktopMediaID::RegisterAuraWindow(
668 ash::Shell::GetInstance()->GetPrimaryRootWindow());
669 #else // defined(OS_CHROMEOS)
670 screen_id =
671 content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
672 webrtc::kFullDesktopScreenId);
673 #endif // !defined(OS_CHROMEOS)
675 bool capture_audio =
676 (request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE &&
677 loopback_audio_supported);
679 // Unless we're being invoked from a component extension, register to
680 // display the notification for stream capture.
681 bool display_notification = !component_extension;
683 ui = GetDevicesForDesktopCapture(&devices, screen_id, capture_audio,
684 display_notification, application_title,
685 application_title);
686 DCHECK(!devices.empty());
689 // The only case when devices can be empty is if the user has denied
690 // permission.
691 result = devices.empty() ? content::MEDIA_DEVICE_PERMISSION_DENIED
692 : content::MEDIA_DEVICE_OK;
695 callback.Run(devices, result, ui.Pass());
698 void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest(
699 content::WebContents* web_contents,
700 const content::MediaStreamRequest& request,
701 const content::MediaResponseCallback& callback,
702 const extensions::Extension* extension) {
703 content::MediaStreamDevices devices;
704 scoped_ptr<content::MediaStreamUI> ui;
706 #if defined(ENABLE_EXTENSIONS)
707 Profile* profile =
708 Profile::FromBrowserContext(web_contents->GetBrowserContext());
709 extensions::TabCaptureRegistry* tab_capture_registry =
710 extensions::TabCaptureRegistry::Get(profile);
711 if (!tab_capture_registry) {
712 NOTREACHED();
713 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
714 return;
716 const bool tab_capture_allowed = tab_capture_registry->VerifyRequest(
717 request.render_process_id, request.render_frame_id, extension->id());
719 if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE &&
720 tab_capture_allowed &&
721 extension->permissions_data()->HasAPIPermission(
722 extensions::APIPermission::kTabCapture)) {
723 devices.push_back(content::MediaStreamDevice(
724 content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string()));
727 if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE &&
728 tab_capture_allowed &&
729 extension->permissions_data()->HasAPIPermission(
730 extensions::APIPermission::kTabCapture)) {
731 devices.push_back(content::MediaStreamDevice(
732 content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string()));
735 if (!devices.empty()) {
736 ui = media_stream_capture_indicator_->RegisterMediaStream(
737 web_contents, devices);
739 callback.Run(
740 devices,
741 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE :
742 content::MEDIA_DEVICE_OK,
743 ui.Pass());
744 #else // defined(ENABLE_EXTENSIONS)
745 callback.Run(devices, content::MEDIA_DEVICE_TAB_CAPTURE_FAILURE, ui.Pass());
746 #endif // defined(ENABLE_EXTENSIONS)
749 #if defined(ENABLE_EXTENSIONS)
750 void MediaCaptureDevicesDispatcher::
751 ProcessMediaAccessRequestFromPlatformAppOrExtension(
752 content::WebContents* web_contents,
753 const content::MediaStreamRequest& request,
754 const content::MediaResponseCallback& callback,
755 const extensions::Extension* extension) {
756 // TODO(vrk): This code is largely duplicated in
757 // MediaStreamDevicesController::Accept(). Move this code into a shared method
758 // between the two classes.
760 Profile* profile =
761 Profile::FromBrowserContext(web_contents->GetBrowserContext());
763 bool audio_allowed =
764 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
765 extension->permissions_data()->HasAPIPermission(
766 extensions::APIPermission::kAudioCapture) &&
767 GetDevicePolicy(profile, extension->url(),
768 prefs::kAudioCaptureAllowed,
769 prefs::kAudioCaptureAllowedUrls) != ALWAYS_DENY;
770 bool video_allowed =
771 request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
772 extension->permissions_data()->HasAPIPermission(
773 extensions::APIPermission::kVideoCapture) &&
774 GetDevicePolicy(profile, extension->url(),
775 prefs::kVideoCaptureAllowed,
776 prefs::kVideoCaptureAllowedUrls) != ALWAYS_DENY;
778 bool get_default_audio_device = audio_allowed;
779 bool get_default_video_device = video_allowed;
781 content::MediaStreamDevices devices;
783 // Set an initial error result. If neither audio or video is allowed, we'll
784 // never try to get any device below but will just create |ui| and return an
785 // empty list with "invalid state" result. If at least one is allowed, we'll
786 // try to get device(s), and if failure, we want to return "no hardware"
787 // result.
788 // TODO(grunell): The invalid state result should be changed to a new denied
789 // result + a dcheck to ensure at least one of audio or video types is
790 // capture.
791 content::MediaStreamRequestResult result =
792 (audio_allowed || video_allowed) ? content::MEDIA_DEVICE_NO_HARDWARE
793 : content::MEDIA_DEVICE_INVALID_STATE;
795 // Get the exact audio or video device if an id is specified.
796 // We only set any error result here and before running the callback change
797 // it to OK if we have any device.
798 if (audio_allowed && !request.requested_audio_device_id.empty()) {
799 const content::MediaStreamDevice* audio_device =
800 GetRequestedAudioDevice(request.requested_audio_device_id);
801 if (audio_device) {
802 devices.push_back(*audio_device);
803 get_default_audio_device = false;
806 if (video_allowed && !request.requested_video_device_id.empty()) {
807 const content::MediaStreamDevice* video_device =
808 GetRequestedVideoDevice(request.requested_video_device_id);
809 if (video_device) {
810 devices.push_back(*video_device);
811 get_default_video_device = false;
815 // If either or both audio and video devices were requested but not
816 // specified by id, get the default devices.
817 if (get_default_audio_device || get_default_video_device) {
818 GetDefaultDevicesForProfile(profile,
819 get_default_audio_device,
820 get_default_video_device,
821 &devices);
824 scoped_ptr<content::MediaStreamUI> ui;
825 if (!devices.empty()) {
826 result = content::MEDIA_DEVICE_OK;
827 ui = media_stream_capture_indicator_->RegisterMediaStream(
828 web_contents, devices);
831 callback.Run(devices, result, ui.Pass());
833 #endif
835 void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest(
836 content::WebContents* web_contents,
837 const content::MediaStreamRequest& request,
838 const content::MediaResponseCallback& callback) {
839 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
841 RequestsQueue& queue = pending_requests_[web_contents];
842 queue.push_back(PendingAccessRequest(request, callback));
844 // If this is the only request then show the infobar.
845 if (queue.size() == 1)
846 ProcessQueuedAccessRequest(web_contents);
849 void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(
850 content::WebContents* web_contents) {
851 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
853 std::map<content::WebContents*, RequestsQueue>::iterator it =
854 pending_requests_.find(web_contents);
856 if (it == pending_requests_.end() || it->second.empty()) {
857 // Don't do anything if the tab was closed.
858 return;
861 DCHECK(!it->second.empty());
863 if (PermissionBubbleManager::Enabled() ||
864 MediaStreamPermissionBubbleExperimentEnabled()) {
865 scoped_ptr<MediaStreamDevicesController> controller(
866 new MediaStreamDevicesController(web_contents,
867 it->second.front().request,
868 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse,
869 base::Unretained(this), web_contents)));
870 if (controller->DismissInfoBarAndTakeActionOnSettings())
871 return;
872 PermissionBubbleManager* bubble_manager =
873 PermissionBubbleManager::FromWebContents(web_contents);
874 if (bubble_manager)
875 bubble_manager->AddRequest(controller.release());
876 return;
879 // TODO(gbillock): delete this block and the MediaStreamInfoBarDelegate
880 // when we've transitioned to bubbles. (crbug/337458)
881 MediaStreamInfoBarDelegate::Create(
882 web_contents, it->second.front().request,
883 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse,
884 base::Unretained(this), web_contents));
887 void MediaCaptureDevicesDispatcher::OnAccessRequestResponse(
888 content::WebContents* web_contents,
889 const content::MediaStreamDevices& devices,
890 content::MediaStreamRequestResult result,
891 scoped_ptr<content::MediaStreamUI> ui) {
892 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
894 std::map<content::WebContents*, RequestsQueue>::iterator it =
895 pending_requests_.find(web_contents);
896 if (it == pending_requests_.end()) {
897 // WebContents has been destroyed. Don't need to do anything.
898 return;
901 RequestsQueue& queue(it->second);
902 if (queue.empty())
903 return;
905 content::MediaResponseCallback callback = queue.front().callback;
906 queue.pop_front();
908 if (!queue.empty()) {
909 // Post a task to process next queued request. It has to be done
910 // asynchronously to make sure that calling infobar is not destroyed until
911 // after this function returns.
912 BrowserThread::PostTask(
913 BrowserThread::UI, FROM_HERE,
914 base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest,
915 base::Unretained(this), web_contents));
918 callback.Run(devices, result, ui.Pass());
921 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile(
922 Profile* profile,
923 bool audio,
924 bool video,
925 content::MediaStreamDevices* devices) {
926 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
927 DCHECK(audio || video);
929 PrefService* prefs = profile->GetPrefs();
930 std::string default_device;
931 if (audio) {
932 default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
933 const content::MediaStreamDevice* device =
934 GetRequestedAudioDevice(default_device);
935 if (!device)
936 device = GetFirstAvailableAudioDevice();
937 if (device)
938 devices->push_back(*device);
941 if (video) {
942 default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
943 const content::MediaStreamDevice* device =
944 GetRequestedVideoDevice(default_device);
945 if (!device)
946 device = GetFirstAvailableVideoDevice();
947 if (device)
948 devices->push_back(*device);
952 const content::MediaStreamDevice*
953 MediaCaptureDevicesDispatcher::GetRequestedAudioDevice(
954 const std::string& requested_audio_device_id) {
955 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
956 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
957 const content::MediaStreamDevice* const device =
958 FindDeviceWithId(audio_devices, requested_audio_device_id);
959 return device;
962 const content::MediaStreamDevice*
963 MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() {
964 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
965 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
966 if (audio_devices.empty())
967 return NULL;
968 return &(*audio_devices.begin());
971 const content::MediaStreamDevice*
972 MediaCaptureDevicesDispatcher::GetRequestedVideoDevice(
973 const std::string& requested_video_device_id) {
974 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
975 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
976 const content::MediaStreamDevice* const device =
977 FindDeviceWithId(video_devices, requested_video_device_id);
978 return device;
981 const content::MediaStreamDevice*
982 MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() {
983 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
984 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
985 if (video_devices.empty())
986 return NULL;
987 return &(*video_devices.begin());
990 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
991 is_device_enumeration_disabled_ = true;
994 scoped_refptr<MediaStreamCaptureIndicator>
995 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
996 return media_stream_capture_indicator_;
999 DesktopStreamsRegistry*
1000 MediaCaptureDevicesDispatcher::GetDesktopStreamsRegistry() {
1001 if (!desktop_streams_registry_)
1002 desktop_streams_registry_.reset(new DesktopStreamsRegistry());
1003 return desktop_streams_registry_.get();
1006 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() {
1007 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1008 BrowserThread::PostTask(
1009 BrowserThread::UI, FROM_HERE,
1010 base::Bind(
1011 &MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread,
1012 base::Unretained(this)));
1015 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() {
1016 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1017 BrowserThread::PostTask(
1018 BrowserThread::UI, FROM_HERE,
1019 base::Bind(
1020 &MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread,
1021 base::Unretained(this)));
1024 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
1025 int render_process_id,
1026 int render_frame_id,
1027 int page_request_id,
1028 const GURL& security_origin,
1029 content::MediaStreamType stream_type,
1030 content::MediaRequestState state) {
1031 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1032 BrowserThread::PostTask(
1033 BrowserThread::UI, FROM_HERE,
1034 base::Bind(
1035 &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread,
1036 base::Unretained(this), render_process_id, render_frame_id,
1037 page_request_id, security_origin, stream_type, state));
1040 void MediaCaptureDevicesDispatcher::OnCreatingAudioStream(
1041 int render_process_id,
1042 int render_frame_id) {
1043 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1044 BrowserThread::PostTask(
1045 BrowserThread::UI, FROM_HERE,
1046 base::Bind(
1047 &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread,
1048 base::Unretained(this), render_process_id, render_frame_id));
1051 void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() {
1052 MediaStreamDevices devices = GetAudioCaptureDevices();
1053 FOR_EACH_OBSERVER(Observer, observers_,
1054 OnUpdateAudioDevices(devices));
1057 void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() {
1058 MediaStreamDevices devices = GetVideoCaptureDevices();
1059 FOR_EACH_OBSERVER(Observer, observers_,
1060 OnUpdateVideoDevices(devices));
1063 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
1064 int render_process_id,
1065 int render_frame_id,
1066 int page_request_id,
1067 const GURL& security_origin,
1068 content::MediaStreamType stream_type,
1069 content::MediaRequestState state) {
1070 // Track desktop capture sessions. Tracking is necessary to avoid unbalanced
1071 // session counts since not all requests will reach MEDIA_REQUEST_STATE_DONE,
1072 // but they will all reach MEDIA_REQUEST_STATE_CLOSING.
1073 if (stream_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
1074 if (state == content::MEDIA_REQUEST_STATE_DONE) {
1075 DesktopCaptureSession session = { render_process_id, render_frame_id,
1076 page_request_id };
1077 desktop_capture_sessions_.push_back(session);
1078 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
1079 for (DesktopCaptureSessions::iterator it =
1080 desktop_capture_sessions_.begin();
1081 it != desktop_capture_sessions_.end();
1082 ++it) {
1083 if (it->render_process_id == render_process_id &&
1084 it->render_frame_id == render_frame_id &&
1085 it->page_request_id == page_request_id) {
1086 desktop_capture_sessions_.erase(it);
1087 break;
1093 // Cancel the request.
1094 if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
1095 bool found = false;
1096 for (RequestsQueues::iterator rqs_it = pending_requests_.begin();
1097 rqs_it != pending_requests_.end(); ++rqs_it) {
1098 RequestsQueue& queue = rqs_it->second;
1099 for (RequestsQueue::iterator it = queue.begin();
1100 it != queue.end(); ++it) {
1101 if (it->request.render_process_id == render_process_id &&
1102 it->request.render_frame_id == render_frame_id &&
1103 it->request.page_request_id == page_request_id) {
1104 queue.erase(it);
1105 found = true;
1106 break;
1109 if (found)
1110 break;
1114 #if defined(OS_CHROMEOS)
1115 if (IsOriginForCasting(security_origin) && IsVideoMediaType(stream_type)) {
1116 // Notify ash that casting state has changed.
1117 if (state == content::MEDIA_REQUEST_STATE_DONE) {
1118 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(true);
1119 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
1120 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(false);
1123 #endif
1125 FOR_EACH_OBSERVER(Observer, observers_,
1126 OnRequestUpdate(render_process_id,
1127 render_frame_id,
1128 stream_type,
1129 state));
1132 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread(
1133 int render_process_id,
1134 int render_frame_id) {
1135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1136 FOR_EACH_OBSERVER(Observer, observers_,
1137 OnCreatingAudioStream(render_process_id, render_frame_id));
1140 bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() {
1141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1142 return desktop_capture_sessions_.size() > 0;
1145 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices(
1146 const MediaStreamDevices& devices) {
1147 test_audio_devices_ = devices;
1150 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices(
1151 const MediaStreamDevices& devices) {
1152 test_video_devices_ = devices;