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"
64 using content::BrowserThread
;
65 using content::MediaCaptureDevices
;
66 using content::MediaStreamDevices
;
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")
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
) {
93 #if defined(ENABLE_EXTENSIONS)
94 // This is a short-term solution to grant camera and/or microphone access to
96 // 1. Virtual keyboard extension.
97 // 2. Google Voice Search Hotword extension.
98 // 3. Flutter gesture recognition extension.
99 // 4. TODO(smus): Airbender experiment 1.
100 // 5. TODO(smus): Airbender experiment 2.
101 // 6. Hotwording component extension.
102 // 7. XKB input method component extension.
103 // 8. M17n/T13n/CJK input method 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" ||
113 extension
->id() == "jkghodnilhceideoidjikpgommlajknk" ||
114 extension
->id() == "gjaehgfemfahhmlgpdfknkhdnemmolop";
117 bool IsBuiltInExtension(const GURL
& origin
) {
119 // Feedback Extension.
120 origin
.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/";
123 // Returns true of the security origin is associated with casting.
124 bool IsOriginForCasting(const GURL
& origin
) {
125 // Whitelisted tab casting extensions.
128 origin
.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" ||
130 origin
.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
132 origin
.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
134 origin
.spec() == "chrome-extension://dliochdbjfkdbacpmhlcpmleaejidimm/" ||
135 // Google Cast Stable
136 origin
.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/" ||
137 // http://crbug.com/457908
138 origin
.spec() == "chrome-extension://ekpaaapppgpmolpcldedioblbkmijaca/" ||
139 origin
.spec() == "chrome-extension://fjhoaacokmgbjemoflkofnenfaiekifl/";
142 bool IsExtensionWhitelistedForScreenCapture(
143 const extensions::Extension
* extension
) {
144 #if defined(OS_CHROMEOS)
145 std::string hash
= base::SHA1HashString(extension
->id());
146 std::string hex_hash
= base::HexEncode(hash
.c_str(), hash
.length());
149 return hex_hash
== "4F25792AF1AA7483936DE29C07806F203C7170A0" ||
150 hex_hash
== "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9" ||
151 hex_hash
== "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB" ||
152 hex_hash
== "81986D4F846CEDDDB962643FA501D1780DD441BB";
155 #endif // defined(OS_CHROMEOS)
157 #endif // defined(ENABLE_EXTENSIONS)
159 // Helper to get title of the calling application shown in the screen capture
161 base::string16
GetApplicationTitle(content::WebContents
* web_contents
,
162 const extensions::Extension
* extension
) {
163 // Use extension name as title for extensions and host/origin for drive-by
166 #if defined(ENABLE_EXTENSIONS)
168 title
= extension
->name();
169 return base::UTF8ToUTF16(title
);
172 GURL url
= web_contents
->GetURL();
173 title
= url
.SchemeIsSecure() ? net::GetHostAndOptionalPort(url
)
174 : url
.GetOrigin().spec();
175 return base::UTF8ToUTF16(title
);
178 // Helper to get list of media stream devices for desktop capture in |devices|.
179 // Registers to display notification if |display_notification| is true.
180 // Returns an instance of MediaStreamUI to be passed to content layer.
181 scoped_ptr
<content::MediaStreamUI
> GetDevicesForDesktopCapture(
182 content::MediaStreamDevices
* devices
,
183 content::DesktopMediaID media_id
,
185 bool display_notification
,
186 const base::string16
& application_title
,
187 const base::string16
& registered_extension_name
) {
188 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
189 scoped_ptr
<content::MediaStreamUI
> ui
;
191 // Add selected desktop source to the list.
192 devices
->push_back(content::MediaStreamDevice(
193 content::MEDIA_DESKTOP_VIDEO_CAPTURE
, media_id
.ToString(), "Screen"));
195 // Use the special loopback device ID for system audio capture.
196 devices
->push_back(content::MediaStreamDevice(
197 content::MEDIA_DESKTOP_AUDIO_CAPTURE
,
198 media::AudioManagerBase::kLoopbackInputDeviceId
, "System Audio"));
201 // If required, register to display the notification for stream capture.
202 if (display_notification
) {
203 if (application_title
== registered_extension_name
) {
204 ui
= ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
205 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT
,
208 ui
= ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
209 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT_DELEGATED
,
210 registered_extension_name
,
218 #if !defined(OS_ANDROID)
219 // Find browser or app window from a given |web_contents|.
220 gfx::NativeWindow
FindParentWindowForWebContents(
221 content::WebContents
* web_contents
) {
222 Browser
* browser
= chrome::FindBrowserWithWebContents(web_contents
);
223 if (browser
&& browser
->window())
224 return browser
->window()->GetNativeWindow();
226 const extensions::AppWindowRegistry::AppWindowList
& window_list
=
227 extensions::AppWindowRegistry::Get(
228 web_contents
->GetBrowserContext())->app_windows();
229 for (extensions::AppWindowRegistry::AppWindowList::const_iterator iter
=
231 iter
!= window_list
.end(); ++iter
) {
232 if ((*iter
)->web_contents() == web_contents
)
233 return (*iter
)->GetNativeWindow();
240 #if defined(ENABLE_EXTENSIONS)
241 const extensions::Extension
* GetExtensionForOrigin(
243 const GURL
& security_origin
) {
244 if (!security_origin
.SchemeIs(extensions::kExtensionScheme
))
247 const extensions::Extension
* extension
=
248 extensions::ExtensionRegistry::Get(profile
)->enabled_extensions().GetByID(
249 security_origin
.host());
257 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
258 const content::MediaStreamRequest
& request
,
259 const content::MediaResponseCallback
& callback
)
264 MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {}
266 MediaCaptureDevicesDispatcher
* MediaCaptureDevicesDispatcher::GetInstance() {
267 return Singleton
<MediaCaptureDevicesDispatcher
>::get();
270 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
271 : is_device_enumeration_disabled_(false),
272 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) {
273 // MediaCaptureDevicesDispatcher is a singleton. It should be created on
274 // UI thread. Otherwise, it will not receive
275 // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in
276 // possible use after free.
277 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
278 notifications_registrar_
.Add(
279 this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
280 content::NotificationService::AllSources());
282 #if defined(OS_MACOSX)
283 // AVFoundation is used for video/audio device monitoring and video capture.
284 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
285 switches::kForceQTKit
)) {
286 base::CommandLine::ForCurrentProcess()->AppendSwitch(
287 switches::kEnableAVFoundation
);
292 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
294 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
295 user_prefs::PrefRegistrySyncable
* registry
) {
296 registry
->RegisterStringPref(
297 prefs::kDefaultAudioCaptureDevice
,
299 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
300 registry
->RegisterStringPref(
301 prefs::kDefaultVideoCaptureDevice
,
303 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
306 void MediaCaptureDevicesDispatcher::AddObserver(Observer
* observer
) {
307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
308 if (!observers_
.HasObserver(observer
))
309 observers_
.AddObserver(observer
);
312 void MediaCaptureDevicesDispatcher::RemoveObserver(Observer
* observer
) {
313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
314 observers_
.RemoveObserver(observer
);
317 const MediaStreamDevices
&
318 MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() {
319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
320 if (is_device_enumeration_disabled_
|| !test_audio_devices_
.empty())
321 return test_audio_devices_
;
323 return MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices();
326 const MediaStreamDevices
&
327 MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() {
328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
329 if (is_device_enumeration_disabled_
|| !test_video_devices_
.empty())
330 return test_video_devices_
;
332 return MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices();
335 void MediaCaptureDevicesDispatcher::Observe(
337 const content::NotificationSource
& source
,
338 const content::NotificationDetails
& details
) {
339 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
340 if (type
== content::NOTIFICATION_WEB_CONTENTS_DESTROYED
) {
341 content::WebContents
* web_contents
=
342 content::Source
<content::WebContents
>(source
).ptr();
343 pending_requests_
.erase(web_contents
);
347 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest(
348 content::WebContents
* web_contents
,
349 const content::MediaStreamRequest
& request
,
350 const content::MediaResponseCallback
& callback
,
351 const extensions::Extension
* extension
) {
352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
354 if (request
.video_type
== content::MEDIA_DESKTOP_VIDEO_CAPTURE
||
355 request
.audio_type
== content::MEDIA_DESKTOP_AUDIO_CAPTURE
) {
356 ProcessDesktopCaptureAccessRequest(
357 web_contents
, request
, callback
, extension
);
358 } else if (request
.video_type
== content::MEDIA_TAB_VIDEO_CAPTURE
||
359 request
.audio_type
== content::MEDIA_TAB_AUDIO_CAPTURE
) {
360 ProcessTabCaptureAccessRequest(
361 web_contents
, request
, callback
, extension
);
363 #if defined(ENABLE_EXTENSIONS)
364 bool is_whitelisted
=
365 extension
&& (extension
->is_platform_app() ||
366 IsMediaRequestWhitelistedForExtension(extension
));
367 if (is_whitelisted
) {
368 // For extensions access is approved based on extension permissions.
369 ProcessMediaAccessRequestFromPlatformAppOrExtension(
370 web_contents
, request
, callback
, extension
);
374 ProcessRegularMediaAccessRequest(web_contents
, request
, callback
);
378 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
379 content::BrowserContext
* browser_context
,
380 const GURL
& security_origin
,
381 content::MediaStreamType type
) {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
383 DCHECK(type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
||
384 type
== content::MEDIA_DEVICE_VIDEO_CAPTURE
);
386 Profile
* profile
= Profile::FromBrowserContext(browser_context
);
387 #if defined(ENABLE_EXTENSIONS)
388 const extensions::Extension
* extension
=
389 GetExtensionForOrigin(profile
, security_origin
);
391 if (extension
&& (extension
->is_platform_app() ||
392 IsMediaRequestWhitelistedForExtension(extension
))) {
393 return extension
->permissions_data()->HasAPIPermission(
394 type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
395 ? extensions::APIPermission::kAudioCapture
396 : extensions::APIPermission::kVideoCapture
);
400 if (CheckAllowAllMediaStreamContentForOrigin(profile
, security_origin
))
403 const char* policy_name
= type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
404 ? prefs::kAudioCaptureAllowed
405 : prefs::kVideoCaptureAllowed
;
406 const char* list_policy_name
= type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
407 ? prefs::kAudioCaptureAllowedUrls
408 : prefs::kVideoCaptureAllowedUrls
;
410 profile
, security_origin
, policy_name
, list_policy_name
) ==
415 // There's no secondary URL for these content types, hence duplicating
416 // |security_origin|.
417 if (profile
->GetHostContentSettingsMap()->GetContentSetting(
420 type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
421 ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
422 : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
,
423 NO_RESOURCE_IDENTIFIER
) == CONTENT_SETTING_ALLOW
) {
430 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
431 content::WebContents
* web_contents
,
432 const GURL
& security_origin
,
433 content::MediaStreamType type
) {
434 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
435 DCHECK(type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
||
436 type
== content::MEDIA_DEVICE_VIDEO_CAPTURE
);
439 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
441 if (CheckAllowAllMediaStreamContentForOrigin(profile
, security_origin
))
444 const char* policy_name
= type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
445 ? prefs::kAudioCaptureAllowed
446 : prefs::kVideoCaptureAllowed
;
447 const char* list_policy_name
= type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
448 ? prefs::kAudioCaptureAllowedUrls
449 : prefs::kVideoCaptureAllowedUrls
;
451 profile
, security_origin
, policy_name
, list_policy_name
) ==
456 // There's no secondary URL for these content types, hence duplicating
457 // |security_origin|.
458 if (profile
->GetHostContentSettingsMap()->GetContentSetting(
461 type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
462 ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
463 : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
,
464 NO_RESOURCE_IDENTIFIER
) == CONTENT_SETTING_ALLOW
) {
471 #if defined(ENABLE_EXTENSIONS)
472 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
473 content::WebContents
* web_contents
,
474 const GURL
& security_origin
,
475 content::MediaStreamType type
,
476 const extensions::Extension
* extension
) {
477 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
478 DCHECK(type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
||
479 type
== content::MEDIA_DEVICE_VIDEO_CAPTURE
);
481 if (extension
->is_platform_app() ||
482 IsMediaRequestWhitelistedForExtension(extension
)) {
483 return extension
->permissions_data()->HasAPIPermission(
484 type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
485 ? extensions::APIPermission::kAudioCapture
486 : extensions::APIPermission::kVideoCapture
);
489 return CheckMediaAccessPermission(web_contents
, security_origin
, type
);
493 void MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest(
494 content::WebContents
* web_contents
,
495 const content::MediaStreamRequest
& request
,
496 const content::MediaResponseCallback
& callback
,
497 const extensions::Extension
* extension
) {
498 content::MediaStreamDevices devices
;
499 scoped_ptr
<content::MediaStreamUI
> ui
;
501 if (request
.video_type
!= content::MEDIA_DESKTOP_VIDEO_CAPTURE
) {
502 callback
.Run(devices
, content::MEDIA_DEVICE_INVALID_STATE
, ui
.Pass());
506 // If the device id wasn't specified then this is a screen capture request
507 // (i.e. chooseDesktopMedia() API wasn't used to generate device id).
508 if (request
.requested_video_device_id
.empty()) {
509 ProcessScreenCaptureAccessRequest(
510 web_contents
, request
, callback
, extension
);
514 // The extension name that the stream is registered with.
515 std::string original_extension_name
;
516 // Resolve DesktopMediaID for the specified device id.
517 content::DesktopMediaID media_id
;
518 // TODO(miu): Replace "main RenderFrame" IDs with the request's actual
519 // RenderFrame IDs once the desktop capture extension API implementation is
520 // fixed. http://crbug.com/304341
521 content::WebContents
* const web_contents_for_stream
=
522 content::WebContents::FromRenderFrameHost(
523 content::RenderFrameHost::FromID(request
.render_process_id
,
524 request
.render_frame_id
));
525 content::RenderFrameHost
* const main_frame
= web_contents_for_stream
?
526 web_contents_for_stream
->GetMainFrame() : NULL
;
528 media_id
= GetDesktopStreamsRegistry()->RequestMediaForStreamId(
529 request
.requested_video_device_id
,
530 main_frame
->GetProcess()->GetID(),
531 main_frame
->GetRoutingID(),
532 request
.security_origin
,
533 &original_extension_name
);
536 // Received invalid device id.
537 if (media_id
.type
== content::DesktopMediaID::TYPE_NONE
) {
538 callback
.Run(devices
, content::MEDIA_DEVICE_INVALID_STATE
, ui
.Pass());
542 bool loopback_audio_supported
= false;
543 #if defined(USE_CRAS) || defined(OS_WIN)
544 // Currently loopback audio capture is supported only on Windows and ChromeOS.
545 loopback_audio_supported
= true;
548 // Audio is only supported for screen capture streams.
550 (media_id
.type
== content::DesktopMediaID::TYPE_SCREEN
&&
551 request
.audio_type
== content::MEDIA_DESKTOP_AUDIO_CAPTURE
&&
552 loopback_audio_supported
);
554 ui
= GetDevicesForDesktopCapture(
555 &devices
, media_id
, capture_audio
, true,
556 GetApplicationTitle(web_contents
, extension
),
557 base::UTF8ToUTF16(original_extension_name
));
559 callback
.Run(devices
, content::MEDIA_DEVICE_OK
, ui
.Pass());
562 void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest(
563 content::WebContents
* web_contents
,
564 const content::MediaStreamRequest
& request
,
565 const content::MediaResponseCallback
& callback
,
566 const extensions::Extension
* extension
) {
567 content::MediaStreamDevices devices
;
568 scoped_ptr
<content::MediaStreamUI
> ui
;
570 DCHECK_EQ(request
.video_type
, content::MEDIA_DESKTOP_VIDEO_CAPTURE
);
572 bool loopback_audio_supported
= false;
573 #if defined(USE_CRAS) || defined(OS_WIN)
574 // Currently loopback audio capture is supported only on Windows and ChromeOS.
575 loopback_audio_supported
= true;
578 bool component_extension
= false;
579 #if defined(ENABLE_EXTENSIONS)
580 component_extension
=
581 extension
&& extension
->location() == extensions::Manifest::COMPONENT
;
584 bool screen_capture_enabled
=
585 base::CommandLine::ForCurrentProcess()->HasSwitch(
586 switches::kEnableUserMediaScreenCapturing
);
587 #if defined(ENABLE_EXTENSIONS)
588 screen_capture_enabled
|=
589 IsOriginForCasting(request
.security_origin
) ||
590 IsExtensionWhitelistedForScreenCapture(extension
) ||
591 IsBuiltInExtension(request
.security_origin
);
594 const bool origin_is_secure
=
595 request
.security_origin
.SchemeIsSecure() ||
596 request
.security_origin
.SchemeIs(extensions::kExtensionScheme
) ||
597 base::CommandLine::ForCurrentProcess()->HasSwitch(
598 switches::kAllowHttpScreenCapture
);
600 // If basic conditions (screen capturing is enabled and origin is secure)
601 // aren't fulfilled, we'll use "invalid state" as result. Otherwise, we set
602 // it after checking permission.
603 // TODO(grunell): It would be good to change this result for something else,
604 // probably a new one.
605 content::MediaStreamRequestResult result
=
606 content::MEDIA_DEVICE_INVALID_STATE
;
608 // Approve request only when the following conditions are met:
609 // 1. Screen capturing is enabled via command line switch or white-listed for
611 // 2. Request comes from a page with a secure origin or from an extension.
612 if (screen_capture_enabled
&& origin_is_secure
) {
613 // Get title of the calling application prior to showing the message box.
614 // chrome::ShowMessageBox() starts a nested message loop which may allow
615 // |web_contents| to be destroyed on the UI thread before the message box
616 // is closed. See http://crbug.com/326690.
617 base::string16 application_title
=
618 GetApplicationTitle(web_contents
, extension
);
619 #if !defined(OS_ANDROID)
620 gfx::NativeWindow parent_window
=
621 FindParentWindowForWebContents(web_contents
);
623 gfx::NativeWindow parent_window
= NULL
;
627 bool whitelisted_extension
= false;
628 #if defined(ENABLE_EXTENSIONS)
629 whitelisted_extension
= IsExtensionWhitelistedForScreenCapture(
633 // For whitelisted or component extensions, bypass message box.
634 bool user_approved
= false;
635 if (!whitelisted_extension
&& !component_extension
) {
636 base::string16 application_name
=
637 base::UTF8ToUTF16(request
.security_origin
.spec());
638 #if defined(ENABLE_EXTENSIONS)
640 application_name
= base::UTF8ToUTF16(extension
->name());
642 base::string16 confirmation_text
= l10n_util::GetStringFUTF16(
643 request
.audio_type
== content::MEDIA_NO_SERVICE
?
644 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT
:
645 IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT
,
647 chrome::MessageBoxResult result
= chrome::ShowMessageBox(
649 l10n_util::GetStringFUTF16(
650 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE
, application_name
),
652 chrome::MESSAGE_BOX_TYPE_QUESTION
);
653 user_approved
= (result
== chrome::MESSAGE_BOX_RESULT_YES
);
656 if (user_approved
|| component_extension
|| whitelisted_extension
) {
657 content::DesktopMediaID screen_id
;
658 #if defined(OS_CHROMEOS)
659 screen_id
= content::DesktopMediaID::RegisterAuraWindow(
660 ash::Shell::GetInstance()->GetPrimaryRootWindow());
661 #else // defined(OS_CHROMEOS)
663 content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN
,
664 webrtc::kFullDesktopScreenId
);
665 #endif // !defined(OS_CHROMEOS)
668 (request
.audio_type
== content::MEDIA_DESKTOP_AUDIO_CAPTURE
&&
669 loopback_audio_supported
);
671 // Unless we're being invoked from a component extension, register to
672 // display the notification for stream capture.
673 bool display_notification
= !component_extension
;
675 ui
= GetDevicesForDesktopCapture(&devices
, screen_id
, capture_audio
,
676 display_notification
, application_title
,
678 DCHECK(!devices
.empty());
681 // The only case when devices can be empty is if the user has denied
683 result
= devices
.empty() ? content::MEDIA_DEVICE_PERMISSION_DENIED
684 : content::MEDIA_DEVICE_OK
;
687 callback
.Run(devices
, result
, ui
.Pass());
690 void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest(
691 content::WebContents
* web_contents
,
692 const content::MediaStreamRequest
& request
,
693 const content::MediaResponseCallback
& callback
,
694 const extensions::Extension
* extension
) {
695 content::MediaStreamDevices devices
;
696 scoped_ptr
<content::MediaStreamUI
> ui
;
698 #if defined(ENABLE_EXTENSIONS)
700 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
701 extensions::TabCaptureRegistry
* tab_capture_registry
=
702 extensions::TabCaptureRegistry::Get(profile
);
703 if (!tab_capture_registry
) {
705 callback
.Run(devices
, content::MEDIA_DEVICE_INVALID_STATE
, ui
.Pass());
708 const bool tab_capture_allowed
= tab_capture_registry
->VerifyRequest(
709 request
.render_process_id
, request
.render_frame_id
, extension
->id());
711 if (request
.audio_type
== content::MEDIA_TAB_AUDIO_CAPTURE
&&
712 tab_capture_allowed
&&
713 extension
->permissions_data()->HasAPIPermission(
714 extensions::APIPermission::kTabCapture
)) {
715 devices
.push_back(content::MediaStreamDevice(
716 content::MEDIA_TAB_AUDIO_CAPTURE
, std::string(), std::string()));
719 if (request
.video_type
== content::MEDIA_TAB_VIDEO_CAPTURE
&&
720 tab_capture_allowed
&&
721 extension
->permissions_data()->HasAPIPermission(
722 extensions::APIPermission::kTabCapture
)) {
723 devices
.push_back(content::MediaStreamDevice(
724 content::MEDIA_TAB_VIDEO_CAPTURE
, std::string(), std::string()));
727 if (!devices
.empty()) {
728 ui
= media_stream_capture_indicator_
->RegisterMediaStream(
729 web_contents
, devices
);
733 devices
.empty() ? content::MEDIA_DEVICE_INVALID_STATE
:
734 content::MEDIA_DEVICE_OK
,
736 #else // defined(ENABLE_EXTENSIONS)
737 callback
.Run(devices
, content::MEDIA_DEVICE_TAB_CAPTURE_FAILURE
, ui
.Pass());
738 #endif // defined(ENABLE_EXTENSIONS)
741 #if defined(ENABLE_EXTENSIONS)
742 void MediaCaptureDevicesDispatcher::
743 ProcessMediaAccessRequestFromPlatformAppOrExtension(
744 content::WebContents
* web_contents
,
745 const content::MediaStreamRequest
& request
,
746 const content::MediaResponseCallback
& callback
,
747 const extensions::Extension
* extension
) {
748 // TODO(vrk): This code is largely duplicated in
749 // MediaStreamDevicesController::Accept(). Move this code into a shared method
750 // between the two classes.
753 Profile::FromBrowserContext(web_contents
->GetBrowserContext());
756 request
.audio_type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
&&
757 extension
->permissions_data()->HasAPIPermission(
758 extensions::APIPermission::kAudioCapture
) &&
759 GetDevicePolicy(profile
, extension
->url(),
760 prefs::kAudioCaptureAllowed
,
761 prefs::kAudioCaptureAllowedUrls
) != ALWAYS_DENY
;
763 request
.video_type
== content::MEDIA_DEVICE_VIDEO_CAPTURE
&&
764 extension
->permissions_data()->HasAPIPermission(
765 extensions::APIPermission::kVideoCapture
) &&
766 GetDevicePolicy(profile
, extension
->url(),
767 prefs::kVideoCaptureAllowed
,
768 prefs::kVideoCaptureAllowedUrls
) != ALWAYS_DENY
;
770 bool get_default_audio_device
= audio_allowed
;
771 bool get_default_video_device
= video_allowed
;
773 content::MediaStreamDevices devices
;
775 // Set an initial error result. If neither audio or video is allowed, we'll
776 // never try to get any device below but will just create |ui| and return an
777 // empty list with "invalid state" result. If at least one is allowed, we'll
778 // try to get device(s), and if failure, we want to return "no hardware"
780 // TODO(grunell): The invalid state result should be changed to a new denied
781 // result + a dcheck to ensure at least one of audio or video types is
783 content::MediaStreamRequestResult result
=
784 (audio_allowed
|| video_allowed
) ? content::MEDIA_DEVICE_NO_HARDWARE
785 : content::MEDIA_DEVICE_INVALID_STATE
;
787 // Get the exact audio or video device if an id is specified.
788 // We only set any error result here and before running the callback change
789 // it to OK if we have any device.
790 if (audio_allowed
&& !request
.requested_audio_device_id
.empty()) {
791 const content::MediaStreamDevice
* audio_device
=
792 GetRequestedAudioDevice(request
.requested_audio_device_id
);
794 devices
.push_back(*audio_device
);
795 get_default_audio_device
= false;
798 if (video_allowed
&& !request
.requested_video_device_id
.empty()) {
799 const content::MediaStreamDevice
* video_device
=
800 GetRequestedVideoDevice(request
.requested_video_device_id
);
802 devices
.push_back(*video_device
);
803 get_default_video_device
= false;
807 // If either or both audio and video devices were requested but not
808 // specified by id, get the default devices.
809 if (get_default_audio_device
|| get_default_video_device
) {
810 GetDefaultDevicesForProfile(profile
,
811 get_default_audio_device
,
812 get_default_video_device
,
816 scoped_ptr
<content::MediaStreamUI
> ui
;
817 if (!devices
.empty()) {
818 result
= content::MEDIA_DEVICE_OK
;
819 ui
= media_stream_capture_indicator_
->RegisterMediaStream(
820 web_contents
, devices
);
823 callback
.Run(devices
, result
, ui
.Pass());
827 void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest(
828 content::WebContents
* web_contents
,
829 const content::MediaStreamRequest
& request
,
830 const content::MediaResponseCallback
& callback
) {
831 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
833 RequestsQueue
& queue
= pending_requests_
[web_contents
];
834 queue
.push_back(PendingAccessRequest(request
, callback
));
836 // If this is the only request then show the infobar.
837 if (queue
.size() == 1)
838 ProcessQueuedAccessRequest(web_contents
);
841 void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(
842 content::WebContents
* web_contents
) {
843 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
845 std::map
<content::WebContents
*, RequestsQueue
>::iterator it
=
846 pending_requests_
.find(web_contents
);
848 if (it
== pending_requests_
.end() || it
->second
.empty()) {
849 // Don't do anything if the tab was closed.
853 DCHECK(!it
->second
.empty());
855 if (PermissionBubbleManager::Enabled() ||
856 MediaStreamPermissionBubbleExperimentEnabled()) {
857 scoped_ptr
<MediaStreamDevicesController
> controller(
858 new MediaStreamDevicesController(web_contents
,
859 it
->second
.front().request
,
860 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse
,
861 base::Unretained(this), web_contents
)));
862 if (controller
->DismissInfoBarAndTakeActionOnSettings())
864 PermissionBubbleManager
* bubble_manager
=
865 PermissionBubbleManager::FromWebContents(web_contents
);
867 bubble_manager
->AddRequest(controller
.release());
871 // TODO(gbillock): delete this block and the MediaStreamInfoBarDelegate
872 // when we've transitioned to bubbles. (crbug/337458)
873 MediaStreamInfoBarDelegate::Create(
874 web_contents
, it
->second
.front().request
,
875 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse
,
876 base::Unretained(this), web_contents
));
879 void MediaCaptureDevicesDispatcher::OnAccessRequestResponse(
880 content::WebContents
* web_contents
,
881 const content::MediaStreamDevices
& devices
,
882 content::MediaStreamRequestResult result
,
883 scoped_ptr
<content::MediaStreamUI
> ui
) {
884 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
886 std::map
<content::WebContents
*, RequestsQueue
>::iterator it
=
887 pending_requests_
.find(web_contents
);
888 if (it
== pending_requests_
.end()) {
889 // WebContents has been destroyed. Don't need to do anything.
893 RequestsQueue
& queue(it
->second
);
897 content::MediaResponseCallback callback
= queue
.front().callback
;
900 if (!queue
.empty()) {
901 // Post a task to process next queued request. It has to be done
902 // asynchronously to make sure that calling infobar is not destroyed until
903 // after this function returns.
904 BrowserThread::PostTask(
905 BrowserThread::UI
, FROM_HERE
,
906 base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest
,
907 base::Unretained(this), web_contents
));
910 callback
.Run(devices
, result
, ui
.Pass());
913 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile(
917 content::MediaStreamDevices
* devices
) {
918 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
919 DCHECK(audio
|| video
);
921 PrefService
* prefs
= profile
->GetPrefs();
922 std::string default_device
;
924 default_device
= prefs
->GetString(prefs::kDefaultAudioCaptureDevice
);
925 const content::MediaStreamDevice
* device
=
926 GetRequestedAudioDevice(default_device
);
928 device
= GetFirstAvailableAudioDevice();
930 devices
->push_back(*device
);
934 default_device
= prefs
->GetString(prefs::kDefaultVideoCaptureDevice
);
935 const content::MediaStreamDevice
* device
=
936 GetRequestedVideoDevice(default_device
);
938 device
= GetFirstAvailableVideoDevice();
940 devices
->push_back(*device
);
944 const content::MediaStreamDevice
*
945 MediaCaptureDevicesDispatcher::GetRequestedAudioDevice(
946 const std::string
& requested_audio_device_id
) {
947 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
948 const content::MediaStreamDevices
& audio_devices
= GetAudioCaptureDevices();
949 const content::MediaStreamDevice
* const device
=
950 FindDeviceWithId(audio_devices
, requested_audio_device_id
);
954 const content::MediaStreamDevice
*
955 MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() {
956 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
957 const content::MediaStreamDevices
& audio_devices
= GetAudioCaptureDevices();
958 if (audio_devices
.empty())
960 return &(*audio_devices
.begin());
963 const content::MediaStreamDevice
*
964 MediaCaptureDevicesDispatcher::GetRequestedVideoDevice(
965 const std::string
& requested_video_device_id
) {
966 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
967 const content::MediaStreamDevices
& video_devices
= GetVideoCaptureDevices();
968 const content::MediaStreamDevice
* const device
=
969 FindDeviceWithId(video_devices
, requested_video_device_id
);
973 const content::MediaStreamDevice
*
974 MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() {
975 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
976 const content::MediaStreamDevices
& video_devices
= GetVideoCaptureDevices();
977 if (video_devices
.empty())
979 return &(*video_devices
.begin());
982 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
983 is_device_enumeration_disabled_
= true;
986 scoped_refptr
<MediaStreamCaptureIndicator
>
987 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
988 return media_stream_capture_indicator_
;
991 DesktopStreamsRegistry
*
992 MediaCaptureDevicesDispatcher::GetDesktopStreamsRegistry() {
993 if (!desktop_streams_registry_
)
994 desktop_streams_registry_
.reset(new DesktopStreamsRegistry());
995 return desktop_streams_registry_
.get();
998 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() {
999 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1000 BrowserThread::PostTask(
1001 BrowserThread::UI
, FROM_HERE
,
1003 &MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread
,
1004 base::Unretained(this)));
1007 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() {
1008 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1009 BrowserThread::PostTask(
1010 BrowserThread::UI
, FROM_HERE
,
1012 &MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread
,
1013 base::Unretained(this)));
1016 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
1017 int render_process_id
,
1018 int render_frame_id
,
1019 int page_request_id
,
1020 const GURL
& security_origin
,
1021 content::MediaStreamType stream_type
,
1022 content::MediaRequestState state
) {
1023 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1024 BrowserThread::PostTask(
1025 BrowserThread::UI
, FROM_HERE
,
1027 &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread
,
1028 base::Unretained(this), render_process_id
, render_frame_id
,
1029 page_request_id
, security_origin
, stream_type
, state
));
1032 void MediaCaptureDevicesDispatcher::OnCreatingAudioStream(
1033 int render_process_id
,
1034 int render_frame_id
) {
1035 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
1036 BrowserThread::PostTask(
1037 BrowserThread::UI
, FROM_HERE
,
1039 &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread
,
1040 base::Unretained(this), render_process_id
, render_frame_id
));
1043 void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() {
1044 MediaStreamDevices devices
= GetAudioCaptureDevices();
1045 FOR_EACH_OBSERVER(Observer
, observers_
,
1046 OnUpdateAudioDevices(devices
));
1049 void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() {
1050 MediaStreamDevices devices
= GetVideoCaptureDevices();
1051 FOR_EACH_OBSERVER(Observer
, observers_
,
1052 OnUpdateVideoDevices(devices
));
1055 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
1056 int render_process_id
,
1057 int render_frame_id
,
1058 int page_request_id
,
1059 const GURL
& security_origin
,
1060 content::MediaStreamType stream_type
,
1061 content::MediaRequestState state
) {
1062 // Track desktop capture sessions. Tracking is necessary to avoid unbalanced
1063 // session counts since not all requests will reach MEDIA_REQUEST_STATE_DONE,
1064 // but they will all reach MEDIA_REQUEST_STATE_CLOSING.
1065 if (stream_type
== content::MEDIA_DESKTOP_VIDEO_CAPTURE
) {
1066 if (state
== content::MEDIA_REQUEST_STATE_DONE
) {
1067 DesktopCaptureSession session
= { render_process_id
, render_frame_id
,
1069 desktop_capture_sessions_
.push_back(session
);
1070 } else if (state
== content::MEDIA_REQUEST_STATE_CLOSING
) {
1071 for (DesktopCaptureSessions::iterator it
=
1072 desktop_capture_sessions_
.begin();
1073 it
!= desktop_capture_sessions_
.end();
1075 if (it
->render_process_id
== render_process_id
&&
1076 it
->render_frame_id
== render_frame_id
&&
1077 it
->page_request_id
== page_request_id
) {
1078 desktop_capture_sessions_
.erase(it
);
1085 // Cancel the request.
1086 if (state
== content::MEDIA_REQUEST_STATE_CLOSING
) {
1088 for (RequestsQueues::iterator rqs_it
= pending_requests_
.begin();
1089 rqs_it
!= pending_requests_
.end(); ++rqs_it
) {
1090 RequestsQueue
& queue
= rqs_it
->second
;
1091 for (RequestsQueue::iterator it
= queue
.begin();
1092 it
!= queue
.end(); ++it
) {
1093 if (it
->request
.render_process_id
== render_process_id
&&
1094 it
->request
.render_frame_id
== render_frame_id
&&
1095 it
->request
.page_request_id
== page_request_id
) {
1106 #if defined(OS_CHROMEOS)
1107 if (IsOriginForCasting(security_origin
) && IsVideoMediaType(stream_type
)) {
1108 // Notify ash that casting state has changed.
1109 if (state
== content::MEDIA_REQUEST_STATE_DONE
) {
1110 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(true);
1111 } else if (state
== content::MEDIA_REQUEST_STATE_CLOSING
) {
1112 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(false);
1117 FOR_EACH_OBSERVER(Observer
, observers_
,
1118 OnRequestUpdate(render_process_id
,
1124 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread(
1125 int render_process_id
,
1126 int render_frame_id
) {
1127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1128 FOR_EACH_OBSERVER(Observer
, observers_
,
1129 OnCreatingAudioStream(render_process_id
, render_frame_id
));
1132 bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() {
1133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1134 return desktop_capture_sessions_
.size() > 0;
1137 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices(
1138 const MediaStreamDevices
& devices
) {
1139 test_audio_devices_
= devices
;
1142 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices(
1143 const MediaStreamDevices
& devices
) {
1144 test_video_devices_
= devices
;