Chromium Code Reviews| Index: chrome/browser/media/media_capture_devices_dispatcher.cc |
| diff --git a/chrome/browser/media/media_capture_devices_dispatcher.cc b/chrome/browser/media/media_capture_devices_dispatcher.cc |
| index 79c93f5c1124def7b7aa119c90259682a0ae0532..9aefed9dbd7a03ca35088d583ba4c3d78bf28327 100644 |
| --- a/chrome/browser/media/media_capture_devices_dispatcher.cc |
| +++ b/chrome/browser/media/media_capture_devices_dispatcher.cc |
| @@ -13,48 +13,35 @@ |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| +#include "chrome/browser/media/desktop_capture_access_handler.h" |
| #include "chrome/browser/media/desktop_streams_registry.h" |
| #include "chrome/browser/media/media_stream_capture_indicator.h" |
| -#include "chrome/browser/media/media_stream_device_permissions.h" |
| -#include "chrome/browser/media/media_stream_infobar_delegate.h" |
| -#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/media/permission_bubble_media_access_handler.h" |
| +#include "chrome/browser/media/tab_capture_access_handler.h" |
| #include "chrome/browser/ui/browser.h" |
| -#include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/browser_window.h" |
| -#include "chrome/browser/ui/screen_capture_notification_ui.h" |
| -#include "chrome/browser/ui/simple_message_box.h" |
| -#include "chrome/browser/ui/website_settings/permission_bubble_manager.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/chrome_version_info.h" |
| #include "chrome/common/pref_names.h" |
| -#include "chrome/grit/generated_resources.h" |
| -#include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/desktop_media_id.h" |
| #include "content/public/browser/media_capture_devices.h" |
| -#include "content/public/browser/notification_service.h" |
| #include "content/public/browser/notification_source.h" |
| -#include "content/public/browser/notification_types.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/common/media_stream_request.h" |
| #include "extensions/common/constants.h" |
| -#include "media/audio/audio_manager_base.h" |
| #include "media/base/media_switches.h" |
| #include "net/base/net_util.h" |
| -#include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h" |
| -#include "ui/base/l10n/l10n_util.h" |
| #if defined(OS_CHROMEOS) |
| #include "ash/shell.h" |
| #endif // defined(OS_CHROMEOS) |
| #if defined(ENABLE_EXTENSIONS) |
| -#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" |
| -#include "extensions/browser/app_window/app_window.h" |
| -#include "extensions/browser/app_window/app_window_registry.h" |
| +#include "chrome/browser/media/extension_media_access_handler.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| @@ -66,16 +53,6 @@ using content::MediaStreamDevices; |
| namespace { |
| -// A finch experiment to enable the permission bubble for media requests only. |
| -bool MediaStreamPermissionBubbleExperimentEnabled() { |
| - const std::string group = |
| - base::FieldTrialList::FindFullName("MediaStreamPermissionBubble"); |
| - if (group == "enabled") |
| - return true; |
| - |
| - return false; |
| -} |
| - |
| // Finds a device in |devices| that has |device_id|, or NULL if not found. |
| const content::MediaStreamDevice* FindDeviceWithId( |
| const content::MediaStreamDevices& devices, |
| @@ -89,162 +66,12 @@ const content::MediaStreamDevice* FindDeviceWithId( |
| return NULL; |
| } |
| -#if defined(ENABLE_EXTENSIONS) |
| -// This is a short-term solution to grant camera and/or microphone access to |
| -// extensions: |
| -// 1. Virtual keyboard extension. |
| -// 2. Flutter gesture recognition extension. |
| -// 3. TODO(smus): Airbender experiment 1. |
| -// 4. TODO(smus): Airbender experiment 2. |
| -// 5. Hotwording component extension. |
| -// 6. XKB input method component extension. |
| -// 7. M17n/T13n/CJK input method component extension. |
| -// Once http://crbug.com/292856 is fixed, remove this whitelist. |
| -bool IsMediaRequestWhitelistedForExtension( |
| - const extensions::Extension* extension) { |
| - return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" || |
| - extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" || |
| - extension->id() == "clffjmdilanldobdnedchkdbofoimcgb" || |
| - extension->id() == "nnckehldicaciogcbchegobnafnjkcne" || |
| - extension->id() == "nbpagnldghgfoolbancepceaanlmhfmd" || |
| - extension->id() == "jkghodnilhceideoidjikpgommlajknk" || |
| - extension->id() == "gjaehgfemfahhmlgpdfknkhdnemmolop"; |
| -} |
| - |
| -bool IsBuiltInExtension(const GURL& origin) { |
| - return |
| - // Feedback Extension. |
| - origin.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/"; |
| -} |
| - |
| -// Returns true of the security origin is associated with casting. |
| -bool IsOriginForCasting(const GURL& origin) { |
| - // Whitelisted tab casting extensions. |
| - return |
| - // Dev |
| - origin.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" || |
| - // Canary |
| - origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" || |
| - // Beta (internal) |
| - origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" || |
| - // Google Cast Beta |
| - origin.spec() == "chrome-extension://dliochdbjfkdbacpmhlcpmleaejidimm/" || |
| - // Google Cast Stable |
| - origin.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/" || |
| - // http://crbug.com/457908 |
| - origin.spec() == "chrome-extension://ekpaaapppgpmolpcldedioblbkmijaca/" || |
| - origin.spec() == "chrome-extension://fjhoaacokmgbjemoflkofnenfaiekifl/"; |
| -} |
| - |
| -bool IsExtensionWhitelistedForScreenCapture( |
| - const extensions::Extension* extension) { |
| -#if defined(OS_CHROMEOS) |
| - std::string hash = base::SHA1HashString(extension->id()); |
| - std::string hex_hash = base::HexEncode(hash.c_str(), hash.length()); |
| - |
| - // crbug.com/446688 |
| - return hex_hash == "4F25792AF1AA7483936DE29C07806F203C7170A0" || |
| - hex_hash == "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9" || |
| - hex_hash == "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB" || |
| - hex_hash == "81986D4F846CEDDDB962643FA501D1780DD441BB"; |
| -#else |
| - return false; |
| -#endif // defined(OS_CHROMEOS) |
| -} |
| -#endif // defined(ENABLE_EXTENSIONS) |
| - |
| -// Helper to get title of the calling application shown in the screen capture |
| -// notification. |
| -base::string16 GetApplicationTitle(content::WebContents* web_contents, |
| - const extensions::Extension* extension) { |
| - // Use extension name as title for extensions and host/origin for drive-by |
| - // web. |
| - std::string title; |
| -#if defined(ENABLE_EXTENSIONS) |
| - if (extension) { |
| - title = extension->name(); |
| - return base::UTF8ToUTF16(title); |
| - } |
| -#endif |
| - GURL url = web_contents->GetURL(); |
| - title = url.SchemeIsSecure() ? net::GetHostAndOptionalPort(url) |
| - : url.GetOrigin().spec(); |
| - return base::UTF8ToUTF16(title); |
| -} |
| - |
| -// Helper to get list of media stream devices for desktop capture in |devices|. |
| -// Registers to display notification if |display_notification| is true. |
| -// Returns an instance of MediaStreamUI to be passed to content layer. |
| -scoped_ptr<content::MediaStreamUI> GetDevicesForDesktopCapture( |
| - content::MediaStreamDevices* devices, |
| - content::DesktopMediaID media_id, |
| - bool capture_audio, |
| - bool display_notification, |
| - const base::string16& application_title, |
| - const base::string16& registered_extension_name) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - scoped_ptr<content::MediaStreamUI> ui; |
| - |
| - // Add selected desktop source to the list. |
| - devices->push_back(content::MediaStreamDevice( |
| - content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen")); |
| - if (capture_audio) { |
| - // Use the special loopback device ID for system audio capture. |
| - devices->push_back(content::MediaStreamDevice( |
| - content::MEDIA_DESKTOP_AUDIO_CAPTURE, |
| - media::AudioManagerBase::kLoopbackInputDeviceId, "System Audio")); |
| - } |
| - |
| - // If required, register to display the notification for stream capture. |
| - if (display_notification) { |
| - if (application_title == registered_extension_name) { |
| - ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16( |
| - IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT, |
| - application_title)); |
| - } else { |
| - ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16( |
| - IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT_DELEGATED, |
| - registered_extension_name, |
| - application_title)); |
| - } |
| - } |
| - |
| - return ui.Pass(); |
| -} |
| - |
| -#if !defined(OS_ANDROID) |
| -// Find browser or app window from a given |web_contents|. |
| -gfx::NativeWindow FindParentWindowForWebContents( |
| - content::WebContents* web_contents) { |
| - Browser* browser = chrome::FindBrowserWithWebContents(web_contents); |
| - if (browser && browser->window()) |
| - return browser->window()->GetNativeWindow(); |
| - |
| - const extensions::AppWindowRegistry::AppWindowList& window_list = |
| - extensions::AppWindowRegistry::Get( |
| - web_contents->GetBrowserContext())->app_windows(); |
| - for (extensions::AppWindowRegistry::AppWindowList::const_iterator iter = |
| - window_list.begin(); |
| - iter != window_list.end(); ++iter) { |
| - if ((*iter)->web_contents() == web_contents) |
| - return (*iter)->GetNativeWindow(); |
| - } |
| - |
| - return NULL; |
| +inline DesktopCaptureAccessHandler* ToDesktopCaptureAccessHandler( |
| + MediaAccessHandler* handler) { |
| + return static_cast<DesktopCaptureAccessHandler*>(handler); |
| } |
| -#endif |
| - |
| } // namespace |
| -MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest( |
| - const content::MediaStreamRequest& request, |
| - const content::MediaResponseCallback& callback) |
| - : request(request), |
| - callback(callback) { |
| -} |
| - |
| -MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {} |
| - |
| MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() { |
| return Singleton<MediaCaptureDevicesDispatcher>::get(); |
| } |
| @@ -252,14 +79,7 @@ MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() { |
| MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() |
| : is_device_enumeration_disabled_(false), |
| media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) { |
| - // MediaCaptureDevicesDispatcher is a singleton. It should be created on |
| - // UI thread. Otherwise, it will not receive |
| - // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in |
| - // possible use after free. |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - notifications_registrar_.Add( |
| - this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |
| - content::NotificationService::AllSources()); |
| #if defined(OS_MACOSX) |
| // AVFoundation is used for video/audio device monitoring and video capture. |
| @@ -269,6 +89,13 @@ MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() |
| switches::kEnableAVFoundation); |
| } |
| #endif |
| + |
| +#if defined(ENABLE_EXTENSIONS) |
|
Sergey Ulanov
2015/06/01 23:39:17
Only PermissionBubbleMediaAccessHandler should be
changbin
2015/06/02 14:20:09
I was wrongly thought desktop capturing should als
|
| + media_access_handlers_.push_back(new ExtensionMediaAccessHandler()); |
| +#endif |
| + media_access_handlers_.push_back(new DesktopCaptureAccessHandler()); |
| + media_access_handlers_.push_back(new TabCaptureAccessHandler()); |
| + media_access_handlers_.push_back(new PermissionBubbleMediaAccessHandler()); |
| } |
| MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {} |
| @@ -310,18 +137,6 @@ MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() { |
| return MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(); |
| } |
| -void MediaCaptureDevicesDispatcher::Observe( |
| - int type, |
| - const content::NotificationSource& source, |
| - const content::NotificationDetails& details) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) { |
| - content::WebContents* web_contents = |
| - content::Source<content::WebContents>(source).ptr(); |
| - pending_requests_.erase(web_contents); |
| - } |
| -} |
| - |
| void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest( |
| content::WebContents* web_contents, |
| const content::MediaStreamRequest& request, |
| @@ -329,27 +144,11 @@ void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest( |
| const extensions::Extension* extension) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE || |
| - request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE) { |
| - ProcessDesktopCaptureAccessRequest( |
| - web_contents, request, callback, extension); |
| - } else if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE || |
| - request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) { |
| - ProcessTabCaptureAccessRequest( |
| - web_contents, request, callback, extension); |
| - } else { |
| -#if defined(ENABLE_EXTENSIONS) |
| - bool is_whitelisted = |
| - extension && (extension->is_platform_app() || |
| - IsMediaRequestWhitelistedForExtension(extension)); |
| - if (is_whitelisted) { |
| - // For extensions access is approved based on extension permissions. |
| - ProcessMediaAccessRequestFromPlatformAppOrExtension( |
| - web_contents, request, callback, extension); |
| - return; |
| + for (MediaAccessHandler* handler : media_access_handlers_) { |
| + if (handler->SupportsStreamType(request.video_type, extension) || |
| + handler->SupportsStreamType(request.audio_type, extension)) { |
| + handler->HandleRequest(web_contents, request, callback, extension); |
|
Sergey Ulanov
2015/06/01 23:39:17
Return after this call. You don't want more than o
changbin
2015/06/02 14:20:09
Thanks, will remove.
|
| } |
| -#endif |
| - ProcessRegularMediaAccessRequest(web_contents, request, callback); |
| } |
|
Sergey Ulanov
2015/06/01 23:39:17
Reject the request if there is no appropriate hand
|
| } |
| @@ -357,488 +156,22 @@ bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission( |
| content::WebContents* web_contents, |
| const GURL& security_origin, |
| content::MediaStreamType type) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE || |
| - type == content::MEDIA_DEVICE_VIDEO_CAPTURE); |
| - |
| - Profile* profile = |
| - Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| - |
| - ContentSettingsType contentSettingsType = |
| - type == content::MEDIA_DEVICE_AUDIO_CAPTURE |
| - ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC |
| - : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA; |
| - |
| - if (CheckAllowAllMediaStreamContentForOrigin( |
| - profile, security_origin, contentSettingsType)) { |
| - return true; |
| - } |
| - |
| - const char* policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE |
| - ? prefs::kAudioCaptureAllowed |
| - : prefs::kVideoCaptureAllowed; |
| - const char* list_policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE |
| - ? prefs::kAudioCaptureAllowedUrls |
| - : prefs::kVideoCaptureAllowedUrls; |
| - if (GetDevicePolicy( |
| - profile, security_origin, policy_name, list_policy_name) == |
| - ALWAYS_ALLOW) { |
| - return true; |
| - } |
| - |
| - // There's no secondary URL for these content types, hence duplicating |
| - // |security_origin|. |
| - if (profile->GetHostContentSettingsMap()->GetContentSetting( |
| - security_origin, |
| - security_origin, |
| - contentSettingsType, |
| - content_settings::ResourceIdentifier()) == CONTENT_SETTING_ALLOW) { |
| - return true; |
| - } |
| - |
| - return false; |
| + return CheckMediaAccessPermission(web_contents, security_origin, type, NULL); |
|
Sergey Ulanov
2015/06/01 23:39:17
s/NULL/nullptr/
Sergey Ulanov
2015/06/01 23:39:17
Please keep DCHECK_CURRENTLY_ON(BrowserThread::UI)
|
| } |
| -#if defined(ENABLE_EXTENSIONS) |
| bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission( |
| content::WebContents* web_contents, |
| const GURL& security_origin, |
| content::MediaStreamType type, |
| const extensions::Extension* extension) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE || |
| - type == content::MEDIA_DEVICE_VIDEO_CAPTURE); |
| - |
| - if (extension->is_platform_app() || |
| - IsMediaRequestWhitelistedForExtension(extension)) { |
| - return extension->permissions_data()->HasAPIPermission( |
| - type == content::MEDIA_DEVICE_AUDIO_CAPTURE |
| - ? extensions::APIPermission::kAudioCapture |
| - : extensions::APIPermission::kVideoCapture); |
| - } |
| - |
| - return CheckMediaAccessPermission(web_contents, security_origin, type); |
| -} |
| -#endif |
| - |
| -void MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest( |
| - content::WebContents* web_contents, |
| - const content::MediaStreamRequest& request, |
| - const content::MediaResponseCallback& callback, |
| - const extensions::Extension* extension) { |
| - content::MediaStreamDevices devices; |
| - scoped_ptr<content::MediaStreamUI> ui; |
| - |
| - if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) { |
| - callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass()); |
| - return; |
| - } |
| - |
| - // If the device id wasn't specified then this is a screen capture request |
| - // (i.e. chooseDesktopMedia() API wasn't used to generate device id). |
| - if (request.requested_video_device_id.empty()) { |
| - ProcessScreenCaptureAccessRequest( |
| - web_contents, request, callback, extension); |
| - return; |
| - } |
| - |
| - // The extension name that the stream is registered with. |
| - std::string original_extension_name; |
| - // Resolve DesktopMediaID for the specified device id. |
| - content::DesktopMediaID media_id; |
| - // TODO(miu): Replace "main RenderFrame" IDs with the request's actual |
| - // RenderFrame IDs once the desktop capture extension API implementation is |
| - // fixed. http://crbug.com/304341 |
| - content::WebContents* const web_contents_for_stream = |
| - content::WebContents::FromRenderFrameHost( |
| - content::RenderFrameHost::FromID(request.render_process_id, |
| - request.render_frame_id)); |
| - content::RenderFrameHost* const main_frame = web_contents_for_stream ? |
| - web_contents_for_stream->GetMainFrame() : NULL; |
| - if (main_frame) { |
| - media_id = GetDesktopStreamsRegistry()->RequestMediaForStreamId( |
| - request.requested_video_device_id, |
| - main_frame->GetProcess()->GetID(), |
| - main_frame->GetRoutingID(), |
| - request.security_origin, |
| - &original_extension_name); |
| - } |
| - |
| - // Received invalid device id. |
| - if (media_id.type == content::DesktopMediaID::TYPE_NONE) { |
| - callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass()); |
| - return; |
| - } |
| - |
| - bool loopback_audio_supported = false; |
| -#if defined(USE_CRAS) || defined(OS_WIN) |
| - // Currently loopback audio capture is supported only on Windows and ChromeOS. |
| - loopback_audio_supported = true; |
| -#endif |
| - |
| - // Audio is only supported for screen capture streams. |
| - bool capture_audio = |
| - (media_id.type == content::DesktopMediaID::TYPE_SCREEN && |
| - request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE && |
| - loopback_audio_supported); |
| - |
| - ui = GetDevicesForDesktopCapture( |
| - &devices, media_id, capture_audio, true, |
| - GetApplicationTitle(web_contents, extension), |
| - base::UTF8ToUTF16(original_extension_name)); |
| - |
| - callback.Run(devices, content::MEDIA_DEVICE_OK, ui.Pass()); |
| -} |
| - |
| -void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest( |
| - content::WebContents* web_contents, |
| - const content::MediaStreamRequest& request, |
| - const content::MediaResponseCallback& callback, |
| - const extensions::Extension* extension) { |
| - content::MediaStreamDevices devices; |
| - scoped_ptr<content::MediaStreamUI> ui; |
| - |
| - DCHECK_EQ(request.video_type, content::MEDIA_DESKTOP_VIDEO_CAPTURE); |
| - |
| - bool loopback_audio_supported = false; |
| -#if defined(USE_CRAS) || defined(OS_WIN) |
| - // Currently loopback audio capture is supported only on Windows and ChromeOS. |
| - loopback_audio_supported = true; |
| -#endif |
| - |
| - bool component_extension = false; |
| -#if defined(ENABLE_EXTENSIONS) |
| - component_extension = |
| - extension && extension->location() == extensions::Manifest::COMPONENT; |
| -#endif |
| - |
| - bool screen_capture_enabled = |
| - base::CommandLine::ForCurrentProcess()->HasSwitch( |
| - switches::kEnableUserMediaScreenCapturing); |
| -#if defined(ENABLE_EXTENSIONS) |
| - screen_capture_enabled |= |
| - IsOriginForCasting(request.security_origin) || |
| - IsExtensionWhitelistedForScreenCapture(extension) || |
| - IsBuiltInExtension(request.security_origin); |
| -#endif |
| - |
| - const bool origin_is_secure = |
| - request.security_origin.SchemeIsSecure() || |
| - request.security_origin.SchemeIs(extensions::kExtensionScheme) || |
| - base::CommandLine::ForCurrentProcess()->HasSwitch( |
| - switches::kAllowHttpScreenCapture); |
| - |
| - // If basic conditions (screen capturing is enabled and origin is secure) |
| - // aren't fulfilled, we'll use "invalid state" as result. Otherwise, we set |
| - // it after checking permission. |
| - // TODO(grunell): It would be good to change this result for something else, |
| - // probably a new one. |
| - content::MediaStreamRequestResult result = |
| - content::MEDIA_DEVICE_INVALID_STATE; |
| - |
| - // Approve request only when the following conditions are met: |
| - // 1. Screen capturing is enabled via command line switch or white-listed for |
| - // the given origin. |
| - // 2. Request comes from a page with a secure origin or from an extension. |
| - if (screen_capture_enabled && origin_is_secure) { |
| - // Get title of the calling application prior to showing the message box. |
| - // chrome::ShowMessageBox() starts a nested message loop which may allow |
| - // |web_contents| to be destroyed on the UI thread before the message box |
| - // is closed. See http://crbug.com/326690. |
| - base::string16 application_title = |
| - GetApplicationTitle(web_contents, extension); |
| -#if !defined(OS_ANDROID) |
| - gfx::NativeWindow parent_window = |
| - FindParentWindowForWebContents(web_contents); |
| -#else |
| - gfx::NativeWindow parent_window = NULL; |
| -#endif |
| - web_contents = NULL; |
| - |
| - bool whitelisted_extension = false; |
| -#if defined(ENABLE_EXTENSIONS) |
| - whitelisted_extension = IsExtensionWhitelistedForScreenCapture( |
| - extension); |
| -#endif |
| - |
| - // For whitelisted or component extensions, bypass message box. |
| - bool user_approved = false; |
| - if (!whitelisted_extension && !component_extension) { |
| - base::string16 application_name = |
| - base::UTF8ToUTF16(request.security_origin.spec()); |
| -#if defined(ENABLE_EXTENSIONS) |
| - if (extension) |
| - application_name = base::UTF8ToUTF16(extension->name()); |
| -#endif |
| - base::string16 confirmation_text = l10n_util::GetStringFUTF16( |
| - request.audio_type == content::MEDIA_NO_SERVICE ? |
| - IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT : |
| - IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT, |
| - application_name); |
| - chrome::MessageBoxResult result = chrome::ShowMessageBox( |
| - parent_window, |
| - l10n_util::GetStringFUTF16( |
| - IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name), |
| - confirmation_text, |
| - chrome::MESSAGE_BOX_TYPE_QUESTION); |
| - user_approved = (result == chrome::MESSAGE_BOX_RESULT_YES); |
| - } |
| - |
| - if (user_approved || component_extension || whitelisted_extension) { |
| - content::DesktopMediaID screen_id; |
| -#if defined(OS_CHROMEOS) |
| - screen_id = content::DesktopMediaID::RegisterAuraWindow( |
| - ash::Shell::GetInstance()->GetPrimaryRootWindow()); |
| -#else // defined(OS_CHROMEOS) |
| - screen_id = |
| - content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, |
| - webrtc::kFullDesktopScreenId); |
| -#endif // !defined(OS_CHROMEOS) |
| - |
| - bool capture_audio = |
| - (request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE && |
| - loopback_audio_supported); |
| - |
| - // Unless we're being invoked from a component extension, register to |
| - // display the notification for stream capture. |
| - bool display_notification = !component_extension; |
| - |
| - ui = GetDevicesForDesktopCapture(&devices, screen_id, capture_audio, |
| - display_notification, application_title, |
| - application_title); |
| - DCHECK(!devices.empty()); |
| - } |
| - |
| - // The only case when devices can be empty is if the user has denied |
| - // permission. |
| - result = devices.empty() ? content::MEDIA_DEVICE_PERMISSION_DENIED |
| - : content::MEDIA_DEVICE_OK; |
| - } |
| - |
| - callback.Run(devices, result, ui.Pass()); |
| -} |
| - |
| -void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest( |
| - content::WebContents* web_contents, |
| - const content::MediaStreamRequest& request, |
| - const content::MediaResponseCallback& callback, |
| - const extensions::Extension* extension) { |
| - content::MediaStreamDevices devices; |
| - scoped_ptr<content::MediaStreamUI> ui; |
| - |
| -#if defined(ENABLE_EXTENSIONS) |
| - Profile* profile = |
| - Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| - extensions::TabCaptureRegistry* tab_capture_registry = |
| - extensions::TabCaptureRegistry::Get(profile); |
| - if (!tab_capture_registry) { |
| - NOTREACHED(); |
| - callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass()); |
| - return; |
| - } |
| - const bool tab_capture_allowed = tab_capture_registry->VerifyRequest( |
| - request.render_process_id, request.render_frame_id, extension->id()); |
| - |
| - if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE && |
| - tab_capture_allowed && |
| - extension->permissions_data()->HasAPIPermission( |
| - extensions::APIPermission::kTabCapture)) { |
| - devices.push_back(content::MediaStreamDevice( |
| - content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string())); |
| - } |
| - |
| - if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE && |
| - tab_capture_allowed && |
| - extension->permissions_data()->HasAPIPermission( |
| - extensions::APIPermission::kTabCapture)) { |
| - devices.push_back(content::MediaStreamDevice( |
| - content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string())); |
| - } |
| - |
| - if (!devices.empty()) { |
| - ui = media_stream_capture_indicator_->RegisterMediaStream( |
| - web_contents, devices); |
| - } |
| - callback.Run( |
| - devices, |
| - devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE : |
| - content::MEDIA_DEVICE_OK, |
| - ui.Pass()); |
| -#else // defined(ENABLE_EXTENSIONS) |
| - callback.Run(devices, content::MEDIA_DEVICE_TAB_CAPTURE_FAILURE, ui.Pass()); |
| -#endif // defined(ENABLE_EXTENSIONS) |
| -} |
| - |
| -#if defined(ENABLE_EXTENSIONS) |
| -void MediaCaptureDevicesDispatcher:: |
| - ProcessMediaAccessRequestFromPlatformAppOrExtension( |
| - content::WebContents* web_contents, |
| - const content::MediaStreamRequest& request, |
| - const content::MediaResponseCallback& callback, |
| - const extensions::Extension* extension) { |
| - // TODO(vrk): This code is largely duplicated in |
| - // MediaStreamDevicesController::Accept(). Move this code into a shared method |
| - // between the two classes. |
| - |
| - Profile* profile = |
| - Profile::FromBrowserContext(web_contents->GetBrowserContext()); |
| - |
| - bool audio_allowed = |
| - request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE && |
| - extension->permissions_data()->HasAPIPermission( |
| - extensions::APIPermission::kAudioCapture) && |
| - GetDevicePolicy(profile, extension->url(), |
| - prefs::kAudioCaptureAllowed, |
| - prefs::kAudioCaptureAllowedUrls) != ALWAYS_DENY; |
| - bool video_allowed = |
| - request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE && |
| - extension->permissions_data()->HasAPIPermission( |
| - extensions::APIPermission::kVideoCapture) && |
| - GetDevicePolicy(profile, extension->url(), |
| - prefs::kVideoCaptureAllowed, |
| - prefs::kVideoCaptureAllowedUrls) != ALWAYS_DENY; |
| - |
| - bool get_default_audio_device = audio_allowed; |
| - bool get_default_video_device = video_allowed; |
| - |
| - content::MediaStreamDevices devices; |
| - |
| - // Set an initial error result. If neither audio or video is allowed, we'll |
| - // never try to get any device below but will just create |ui| and return an |
| - // empty list with "invalid state" result. If at least one is allowed, we'll |
| - // try to get device(s), and if failure, we want to return "no hardware" |
| - // result. |
| - // TODO(grunell): The invalid state result should be changed to a new denied |
| - // result + a dcheck to ensure at least one of audio or video types is |
| - // capture. |
| - content::MediaStreamRequestResult result = |
| - (audio_allowed || video_allowed) ? content::MEDIA_DEVICE_NO_HARDWARE |
| - : content::MEDIA_DEVICE_INVALID_STATE; |
| - |
| - // Get the exact audio or video device if an id is specified. |
| - // We only set any error result here and before running the callback change |
| - // it to OK if we have any device. |
| - if (audio_allowed && !request.requested_audio_device_id.empty()) { |
| - const content::MediaStreamDevice* audio_device = |
| - GetRequestedAudioDevice(request.requested_audio_device_id); |
| - if (audio_device) { |
| - devices.push_back(*audio_device); |
| - get_default_audio_device = false; |
| - } |
| - } |
| - if (video_allowed && !request.requested_video_device_id.empty()) { |
| - const content::MediaStreamDevice* video_device = |
| - GetRequestedVideoDevice(request.requested_video_device_id); |
| - if (video_device) { |
| - devices.push_back(*video_device); |
| - get_default_video_device = false; |
| + for (MediaAccessHandler* handler : media_access_handlers_) { |
| + if (handler->SupportsStreamType(type, extension)) { |
| + return handler->CheckMediaAccessPermission(web_contents, security_origin, |
| + type, extension); |
| } |
| } |
| - |
| - // If either or both audio and video devices were requested but not |
| - // specified by id, get the default devices. |
| - if (get_default_audio_device || get_default_video_device) { |
| - GetDefaultDevicesForProfile(profile, |
| - get_default_audio_device, |
| - get_default_video_device, |
| - &devices); |
| - } |
| - |
| - scoped_ptr<content::MediaStreamUI> ui; |
| - if (!devices.empty()) { |
| - result = content::MEDIA_DEVICE_OK; |
| - ui = media_stream_capture_indicator_->RegisterMediaStream( |
| - web_contents, devices); |
| - } |
| - |
| - callback.Run(devices, result, ui.Pass()); |
| -} |
| -#endif |
| - |
| -void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest( |
| - content::WebContents* web_contents, |
| - const content::MediaStreamRequest& request, |
| - const content::MediaResponseCallback& callback) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - |
| - RequestsQueue& queue = pending_requests_[web_contents]; |
| - queue.push_back(PendingAccessRequest(request, callback)); |
| - |
| - // If this is the only request then show the infobar. |
| - if (queue.size() == 1) |
| - ProcessQueuedAccessRequest(web_contents); |
| -} |
| - |
| -void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest( |
| - content::WebContents* web_contents) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - |
| - std::map<content::WebContents*, RequestsQueue>::iterator it = |
| - pending_requests_.find(web_contents); |
| - |
| - if (it == pending_requests_.end() || it->second.empty()) { |
| - // Don't do anything if the tab was closed. |
| - return; |
| - } |
| - |
| - DCHECK(!it->second.empty()); |
| - |
| - if (PermissionBubbleManager::Enabled() || |
| - MediaStreamPermissionBubbleExperimentEnabled()) { |
| - scoped_ptr<MediaStreamDevicesController> controller( |
| - new MediaStreamDevicesController(web_contents, |
| - it->second.front().request, |
| - base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse, |
| - base::Unretained(this), web_contents))); |
| - if (controller->DismissInfoBarAndTakeActionOnSettings()) |
| - return; |
| - PermissionBubbleManager* bubble_manager = |
| - PermissionBubbleManager::FromWebContents(web_contents); |
| - if (bubble_manager) |
| - bubble_manager->AddRequest(controller.release()); |
| - return; |
| - } |
| - |
| - // TODO(gbillock): delete this block and the MediaStreamInfoBarDelegate |
| - // when we've transitioned to bubbles. (crbug/337458) |
| - MediaStreamInfoBarDelegate::Create( |
| - web_contents, it->second.front().request, |
| - base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse, |
| - base::Unretained(this), web_contents)); |
| -} |
| - |
| -void MediaCaptureDevicesDispatcher::OnAccessRequestResponse( |
| - content::WebContents* web_contents, |
| - const content::MediaStreamDevices& devices, |
| - content::MediaStreamRequestResult result, |
| - scoped_ptr<content::MediaStreamUI> ui) { |
| - DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - |
| - std::map<content::WebContents*, RequestsQueue>::iterator it = |
| - pending_requests_.find(web_contents); |
| - if (it == pending_requests_.end()) { |
| - // WebContents has been destroyed. Don't need to do anything. |
| - return; |
| - } |
| - |
| - RequestsQueue& queue(it->second); |
| - if (queue.empty()) |
| - return; |
| - |
| - content::MediaResponseCallback callback = queue.front().callback; |
| - queue.pop_front(); |
| - |
| - if (!queue.empty()) { |
| - // Post a task to process next queued request. It has to be done |
| - // asynchronously to make sure that calling infobar is not destroyed until |
| - // after this function returns. |
| - BrowserThread::PostTask( |
| - BrowserThread::UI, FROM_HERE, |
| - base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest, |
| - base::Unretained(this), web_contents)); |
| - } |
| - |
| - callback.Run(devices, result, ui.Pass()); |
| + return false; |
| } |
| void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile( |
| @@ -990,47 +323,10 @@ void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread( |
| const GURL& security_origin, |
| content::MediaStreamType stream_type, |
| content::MediaRequestState state) { |
| - // Track desktop capture sessions. Tracking is necessary to avoid unbalanced |
| - // session counts since not all requests will reach MEDIA_REQUEST_STATE_DONE, |
| - // but they will all reach MEDIA_REQUEST_STATE_CLOSING. |
| - if (stream_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) { |
| - if (state == content::MEDIA_REQUEST_STATE_DONE) { |
| - DesktopCaptureSession session = { render_process_id, render_frame_id, |
| - page_request_id }; |
| - desktop_capture_sessions_.push_back(session); |
| - } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) { |
| - for (DesktopCaptureSessions::iterator it = |
| - desktop_capture_sessions_.begin(); |
| - it != desktop_capture_sessions_.end(); |
| - ++it) { |
| - if (it->render_process_id == render_process_id && |
| - it->render_frame_id == render_frame_id && |
| - it->page_request_id == page_request_id) { |
| - desktop_capture_sessions_.erase(it); |
| - break; |
| - } |
| - } |
| - } |
| - } |
| - |
| - // Cancel the request. |
| - if (state == content::MEDIA_REQUEST_STATE_CLOSING) { |
| - bool found = false; |
| - for (RequestsQueues::iterator rqs_it = pending_requests_.begin(); |
| - rqs_it != pending_requests_.end(); ++rqs_it) { |
| - RequestsQueue& queue = rqs_it->second; |
| - for (RequestsQueue::iterator it = queue.begin(); |
| - it != queue.end(); ++it) { |
| - if (it->request.render_process_id == render_process_id && |
| - it->request.render_frame_id == render_frame_id && |
| - it->request.page_request_id == page_request_id) { |
| - queue.erase(it); |
| - found = true; |
| - break; |
| - } |
| - } |
| - if (found) |
| - break; |
| + for (MediaAccessHandler* handler : media_access_handlers_) { |
| + if (handler->SupportsStreamType(stream_type, NULL)) { |
| + handler->UpdateMediaRequest(render_process_id, render_frame_id, |
|
Sergey Ulanov
2015/06/01 23:39:17
break after this call.
changbin
2015/06/02 14:20:09
Done.
|
| + page_request_id, stream_type, state); |
| } |
| } |
| @@ -1062,7 +358,13 @@ void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread( |
| bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| - return desktop_capture_sessions_.size() > 0; |
| + for (MediaAccessHandler* handler : media_access_handlers_) { |
| + if (handler->SupportsStreamType(content::MEDIA_DESKTOP_VIDEO_CAPTURE, |
| + NULL)) { |
| + return ToDesktopCaptureAccessHandler(handler)->IsCaptureInProgress(); |
| + } |
| + } |
| + return false; |
| } |
| void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices( |