Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2197)

Unified Diff: chrome/browser/media/media_capture_devices_dispatcher.cc

Issue 1095393004: Refactor: Make MediaCaptureDevicesDispatcher have pluggable handlers. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix tryjob error. Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..b64a2c7ce6c9d7f0dbb63414b01feb1ceef63059 100644
--- a/chrome/browser/media/media_capture_devices_dispatcher.cc
+++ b/chrome/browser/media/media_capture_devices_dispatcher.cc
@@ -9,52 +9,38 @@
#include "base/metrics/field_trial.h"
#include "base/prefs/pref_service.h"
#include "base/prefs/scoped_user_pref_update.h"
-#include "base/sha1.h"
#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_streams_registry.h"
+#include "chrome/browser/media/media_access_handler.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/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/desktop_capture_access_handler.h"
+#include "chrome/browser/media/extension_media_access_handler.h"
+#include "chrome/browser/media/tab_capture_access_handler.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/common/extension.h"
#include "extensions/common/permissions/permissions_data.h"
@@ -66,16 +52,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,
@@ -90,161 +66,13 @@ const content::MediaStreamDevice* FindDeviceWithId(
}
#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 +80,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 +90,13 @@ MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
switches::kEnableAVFoundation);
}
#endif
+
+#if defined(ENABLE_EXTENSIONS)
+ media_access_handlers_.push_back(new ExtensionMediaAccessHandler());
+ media_access_handlers_.push_back(new DesktopCaptureAccessHandler());
+ media_access_handlers_.push_back(new TabCaptureAccessHandler());
+#endif
+ media_access_handlers_.push_back(new PermissionBubbleMediaAccessHandler());
}
MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
@@ -281,6 +109,24 @@ void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
std::string());
}
+bool MediaCaptureDevicesDispatcher::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/";
+}
+
void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!observers_.HasObserver(observer))
@@ -310,18 +156,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,28 +163,15 @@ 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);
+ 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);
return;
}
-#endif
- ProcessRegularMediaAccessRequest(web_contents, request, callback);
}
+ callback.Run(content::MediaStreamDevices(),
+ content::MEDIA_DEVICE_NOT_SUPPORTED, nullptr);
}
bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
@@ -358,487 +179,23 @@ bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
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,
+ nullptr);
}
-#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;
+ for (MediaAccessHandler* handler : media_access_handlers_) {
+ if (handler->SupportsStreamType(type, extension)) {
+ return handler->CheckMediaAccessPermission(web_contents, security_origin,
+ type, extension);
}
}
- 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;
- }
- }
-
- // 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 +347,11 @@ 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, nullptr)) {
+ handler->UpdateMediaRequestState(render_process_id, render_frame_id,
+ page_request_id, stream_type, state);
+ break;
}
}
@@ -1062,7 +383,15 @@ void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread(
bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
- return desktop_capture_sessions_.size() > 0;
+#if defined(ENABLE_EXTENSIONS)
+ for (MediaAccessHandler* handler : media_access_handlers_) {
+ if (handler->SupportsStreamType(content::MEDIA_DESKTOP_VIDEO_CAPTURE,
+ NULL)) {
+ return ToDesktopCaptureAccessHandler(handler)->IsCaptureInProgress();
+ }
+ }
+#endif
+ return false;
}
void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices(
« no previous file with comments | « chrome/browser/media/media_capture_devices_dispatcher.h ('k') | chrome/browser/media/permission_bubble_media_access_handler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698