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

Side by Side 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: Update Created 5 years, 7 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/media/media_capture_devices_dispatcher.h" 5 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/metrics/field_trial.h" 9 #include "base/metrics/field_trial.h"
10 #include "base/prefs/pref_service.h" 10 #include "base/prefs/pref_service.h"
11 #include "base/prefs/scoped_user_pref_update.h" 11 #include "base/prefs/scoped_user_pref_update.h"
12 #include "base/sha1.h" 12 #include "base/sha1.h"
13 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h" 14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/media/desktop_capture_access_handler.h"
16 #include "chrome/browser/media/desktop_streams_registry.h" 17 #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_capture_indicator.h"
18 #include "chrome/browser/media/media_stream_device_permissions.h" 19 #include "chrome/browser/media/media_stream_device_permissions.h"
19 #include "chrome/browser/media/media_stream_infobar_delegate.h" 20 #include "chrome/browser/media/permission_bubble_media_access_handler.h"
20 #include "chrome/browser/profiles/profile.h" 21 #include "chrome/browser/media/tab_capture_access_handler.h"
21 #include "chrome/browser/ui/browser.h" 22 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_finder.h"
23 #include "chrome/browser/ui/browser_window.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" 24 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/chrome_version_info.h" 25 #include "chrome/common/chrome_version_info.h"
29 #include "chrome/common/pref_names.h" 26 #include "chrome/common/pref_names.h"
30 #include "chrome/grit/generated_resources.h"
31 #include "components/content_settings/core/browser/host_content_settings_map.h" 27 #include "components/content_settings/core/browser/host_content_settings_map.h"
32 #include "components/pref_registry/pref_registry_syncable.h" 28 #include "components/pref_registry/pref_registry_syncable.h"
33 #include "content/public/browser/browser_thread.h" 29 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/desktop_media_id.h" 30 #include "content/public/browser/desktop_media_id.h"
35 #include "content/public/browser/media_capture_devices.h" 31 #include "content/public/browser/media_capture_devices.h"
36 #include "content/public/browser/notification_service.h" 32 #include "content/public/browser/notification_service.h"
37 #include "content/public/browser/notification_source.h" 33 #include "content/public/browser/notification_source.h"
38 #include "content/public/browser/notification_types.h" 34 #include "content/public/browser/notification_types.h"
39 #include "content/public/browser/render_frame_host.h" 35 #include "content/public/browser/render_frame_host.h"
40 #include "content/public/browser/render_process_host.h" 36 #include "content/public/browser/render_process_host.h"
41 #include "content/public/browser/web_contents.h" 37 #include "content/public/browser/web_contents.h"
42 #include "content/public/common/media_stream_request.h" 38 #include "content/public/common/media_stream_request.h"
43 #include "extensions/common/constants.h" 39 #include "extensions/common/constants.h"
44 #include "media/audio/audio_manager_base.h"
45 #include "media/base/media_switches.h" 40 #include "media/base/media_switches.h"
46 #include "net/base/net_util.h" 41 #include "net/base/net_util.h"
47 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"
48 #include "ui/base/l10n/l10n_util.h"
49 42
50 #if defined(OS_CHROMEOS) 43 #if defined(OS_CHROMEOS)
51 #include "ash/shell.h" 44 #include "ash/shell.h"
52 #endif // defined(OS_CHROMEOS) 45 #endif // defined(OS_CHROMEOS)
53 46
54 #if defined(ENABLE_EXTENSIONS) 47 #if defined(ENABLE_EXTENSIONS)
55 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" 48 #include "chrome/browser/media/extension_media_access_handler.h"
56 #include "extensions/browser/app_window/app_window.h"
57 #include "extensions/browser/app_window/app_window_registry.h"
58 #include "extensions/browser/extension_registry.h" 49 #include "extensions/browser/extension_registry.h"
59 #include "extensions/common/extension.h" 50 #include "extensions/common/extension.h"
60 #include "extensions/common/permissions/permissions_data.h" 51 #include "extensions/common/permissions/permissions_data.h"
61 #endif 52 #endif
62 53
63 using content::BrowserThread; 54 using content::BrowserThread;
64 using content::MediaCaptureDevices; 55 using content::MediaCaptureDevices;
65 using content::MediaStreamDevices; 56 using content::MediaStreamDevices;
66 57
67 namespace { 58 namespace {
68 59
69 // A finch experiment to enable the permission bubble for media requests only.
70 bool MediaStreamPermissionBubbleExperimentEnabled() {
71 const std::string group =
72 base::FieldTrialList::FindFullName("MediaStreamPermissionBubble");
73 if (group == "enabled")
74 return true;
75
76 return false;
77 }
78
79 // Finds a device in |devices| that has |device_id|, or NULL if not found. 60 // Finds a device in |devices| that has |device_id|, or NULL if not found.
80 const content::MediaStreamDevice* FindDeviceWithId( 61 const content::MediaStreamDevice* FindDeviceWithId(
81 const content::MediaStreamDevices& devices, 62 const content::MediaStreamDevices& devices,
82 const std::string& device_id) { 63 const std::string& device_id) {
83 content::MediaStreamDevices::const_iterator iter = devices.begin(); 64 content::MediaStreamDevices::const_iterator iter = devices.begin();
84 for (; iter != devices.end(); ++iter) { 65 for (; iter != devices.end(); ++iter) {
85 if (iter->id == device_id) { 66 if (iter->id == device_id) {
86 return &(*iter); 67 return &(*iter);
87 } 68 }
88 } 69 }
89 return NULL; 70 return NULL;
90 } 71 }
91 72
92 #if defined(ENABLE_EXTENSIONS) 73 #if defined(ENABLE_EXTENSIONS)
93 // This is a short-term solution to grant camera and/or microphone access to
94 // extensions:
95 // 1. Virtual keyboard extension.
96 // 2. Flutter gesture recognition extension.
97 // 3. TODO(smus): Airbender experiment 1.
98 // 4. TODO(smus): Airbender experiment 2.
99 // 5. Hotwording component extension.
100 // 6. XKB input method component extension.
101 // 7. M17n/T13n/CJK input method component extension.
102 // Once http://crbug.com/292856 is fixed, remove this whitelist.
103 bool IsMediaRequestWhitelistedForExtension(
104 const extensions::Extension* extension) {
105 return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" ||
106 extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" ||
107 extension->id() == "clffjmdilanldobdnedchkdbofoimcgb" ||
108 extension->id() == "nnckehldicaciogcbchegobnafnjkcne" ||
109 extension->id() == "nbpagnldghgfoolbancepceaanlmhfmd" ||
110 extension->id() == "jkghodnilhceideoidjikpgommlajknk" ||
111 extension->id() == "gjaehgfemfahhmlgpdfknkhdnemmolop";
112 }
113
114 bool IsBuiltInExtension(const GURL& origin) {
115 return
116 // Feedback Extension.
117 origin.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/";
118 }
119
120 // Returns true of the security origin is associated with casting.
121 bool IsOriginForCasting(const GURL& origin) {
122 // Whitelisted tab casting extensions.
123 return
124 // Dev
125 origin.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" ||
126 // Canary
127 origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
128 // Beta (internal)
129 origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
130 // Google Cast Beta
131 origin.spec() == "chrome-extension://dliochdbjfkdbacpmhlcpmleaejidimm/" ||
132 // Google Cast Stable
133 origin.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/" ||
134 // http://crbug.com/457908
135 origin.spec() == "chrome-extension://ekpaaapppgpmolpcldedioblbkmijaca/" ||
136 origin.spec() == "chrome-extension://fjhoaacokmgbjemoflkofnenfaiekifl/";
137 }
138
139 bool IsExtensionWhitelistedForScreenCapture(
140 const extensions::Extension* extension) {
141 #if defined(OS_CHROMEOS)
142 std::string hash = base::SHA1HashString(extension->id());
143 std::string hex_hash = base::HexEncode(hash.c_str(), hash.length());
144
145 // crbug.com/446688
146 return hex_hash == "4F25792AF1AA7483936DE29C07806F203C7170A0" ||
147 hex_hash == "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9" ||
148 hex_hash == "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB" ||
149 hex_hash == "81986D4F846CEDDDB962643FA501D1780DD441BB";
150 #else
151 return false;
152 #endif // defined(OS_CHROMEOS)
153 }
154 #endif // defined(ENABLE_EXTENSIONS)
155
156 // Helper to get title of the calling application shown in the screen capture
157 // notification.
158 base::string16 GetApplicationTitle(content::WebContents* web_contents,
159 const extensions::Extension* extension) {
160 // Use extension name as title for extensions and host/origin for drive-by
161 // web.
162 std::string title;
163 #if defined(ENABLE_EXTENSIONS)
164 if (extension) {
165 title = extension->name();
166 return base::UTF8ToUTF16(title);
167 }
168 #endif
169 GURL url = web_contents->GetURL();
170 title = url.SchemeIsSecure() ? net::GetHostAndOptionalPort(url)
171 : url.GetOrigin().spec();
172 return base::UTF8ToUTF16(title);
173 }
174
175 // Helper to get list of media stream devices for desktop capture in |devices|.
176 // Registers to display notification if |display_notification| is true.
177 // Returns an instance of MediaStreamUI to be passed to content layer.
178 scoped_ptr<content::MediaStreamUI> GetDevicesForDesktopCapture(
179 content::MediaStreamDevices* devices,
180 content::DesktopMediaID media_id,
181 bool capture_audio,
182 bool display_notification,
183 const base::string16& application_title,
184 const base::string16& registered_extension_name) {
185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
186 scoped_ptr<content::MediaStreamUI> ui;
187
188 // Add selected desktop source to the list.
189 devices->push_back(content::MediaStreamDevice(
190 content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen"));
191 if (capture_audio) {
192 // Use the special loopback device ID for system audio capture.
193 devices->push_back(content::MediaStreamDevice(
194 content::MEDIA_DESKTOP_AUDIO_CAPTURE,
195 media::AudioManagerBase::kLoopbackInputDeviceId, "System Audio"));
196 }
197
198 // If required, register to display the notification for stream capture.
199 if (display_notification) {
200 if (application_title == registered_extension_name) {
201 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
202 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT,
203 application_title));
204 } else {
205 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
206 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT_DELEGATED,
207 registered_extension_name,
208 application_title));
209 }
210 }
211
212 return ui.Pass();
213 }
214
215 #if !defined(OS_ANDROID)
216 // Find browser or app window from a given |web_contents|.
217 gfx::NativeWindow FindParentWindowForWebContents(
218 content::WebContents* web_contents) {
219 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
220 if (browser && browser->window())
221 return browser->window()->GetNativeWindow();
222
223 const extensions::AppWindowRegistry::AppWindowList& window_list =
224 extensions::AppWindowRegistry::Get(
225 web_contents->GetBrowserContext())->app_windows();
226 for (extensions::AppWindowRegistry::AppWindowList::const_iterator iter =
227 window_list.begin();
228 iter != window_list.end(); ++iter) {
229 if ((*iter)->web_contents() == web_contents)
230 return (*iter)->GetNativeWindow();
231 }
232
233 return NULL;
234 }
235 #endif
236
237 #if defined(ENABLE_EXTENSIONS)
238 const extensions::Extension* GetExtensionForOrigin( 74 const extensions::Extension* GetExtensionForOrigin(
239 Profile* profile, 75 Profile* profile,
240 const GURL& security_origin) { 76 const GURL& security_origin) {
241 if (!security_origin.SchemeIs(extensions::kExtensionScheme)) 77 if (!security_origin.SchemeIs(extensions::kExtensionScheme))
242 return NULL; 78 return NULL;
243 79
244 const extensions::Extension* extension = 80 const extensions::Extension* extension =
245 extensions::ExtensionRegistry::Get(profile)->enabled_extensions().GetByID( 81 extensions::ExtensionRegistry::Get(profile)->enabled_extensions().GetByID(
246 security_origin.host()); 82 security_origin.host());
247 DCHECK(extension); 83 DCHECK(extension);
248 return extension; 84 return extension;
249 } 85 }
250 #endif 86 #endif
251 87
252 } // namespace 88 } // namespace
253 89
254 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
255 const content::MediaStreamRequest& request,
256 const content::MediaResponseCallback& callback)
257 : request(request),
258 callback(callback) {
259 }
260
261 MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {}
262
263 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() { 90 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
264 return Singleton<MediaCaptureDevicesDispatcher>::get(); 91 return Singleton<MediaCaptureDevicesDispatcher>::get();
265 } 92 }
266 93
267 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() 94 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
268 : is_device_enumeration_disabled_(false), 95 : is_device_enumeration_disabled_(false),
269 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) { 96 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) {
270 // MediaCaptureDevicesDispatcher is a singleton. It should be created on 97 // MediaCaptureDevicesDispatcher is a singleton. It should be created on
271 // UI thread. Otherwise, it will not receive 98 // UI thread. Otherwise, it will not receive
272 // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in 99 // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in
273 // possible use after free. 100 // possible use after free.
274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
275 notifications_registrar_.Add( 102 notifications_registrar_.Add(
276 this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, 103 this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
277 content::NotificationService::AllSources()); 104 content::NotificationService::AllSources());
278 105
279 #if defined(OS_MACOSX) 106 #if defined(OS_MACOSX)
280 // AVFoundation is used for video/audio device monitoring and video capture. 107 // AVFoundation is used for video/audio device monitoring and video capture.
281 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( 108 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
282 switches::kForceQTKit)) { 109 switches::kForceQTKit)) {
283 base::CommandLine::ForCurrentProcess()->AppendSwitch( 110 base::CommandLine::ForCurrentProcess()->AppendSwitch(
284 switches::kEnableAVFoundation); 111 switches::kEnableAVFoundation);
285 } 112 }
286 #endif 113 #endif
114
115 media_access_handlers_.push_back(new DesktopCaptureAccessHandler());
116 media_access_handlers_.push_back(new TabCaptureAccessHandler());
117 media_access_handlers_.push_back(
118 new PermissionBubbleMediaAccessHandler(&pending_requests_));
119
120 #if defined(ENABLE_EXTENSIONS)
121 media_access_handlers_.push_back(new ExtensionMediaAccessHandler());
122 #endif
287 } 123 }
288 124
289 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {} 125 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
290 126
291 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs( 127 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
292 user_prefs::PrefRegistrySyncable* registry) { 128 user_prefs::PrefRegistrySyncable* registry) {
293 registry->RegisterStringPref( 129 registry->RegisterStringPref(
294 prefs::kDefaultAudioCaptureDevice, 130 prefs::kDefaultAudioCaptureDevice,
295 std::string(), 131 std::string(),
296 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 132 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
341 } 177 }
342 } 178 }
343 179
344 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest( 180 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest(
345 content::WebContents* web_contents, 181 content::WebContents* web_contents,
346 const content::MediaStreamRequest& request, 182 const content::MediaStreamRequest& request,
347 const content::MediaResponseCallback& callback, 183 const content::MediaResponseCallback& callback,
348 const extensions::Extension* extension) { 184 const extensions::Extension* extension) {
349 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
350 186
351 if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE || 187 for (MediaAccessHandler* handler : media_access_handlers_) {
352 request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE) { 188 if (handler->SupportsRequest(request, extension)) {
353 ProcessDesktopCaptureAccessRequest( 189 handler->HandleRequest(web_contents, request, callback, extension);
354 web_contents, request, callback, extension);
355 } else if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE ||
356 request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) {
357 ProcessTabCaptureAccessRequest(
358 web_contents, request, callback, extension);
359 } else {
360 #if defined(ENABLE_EXTENSIONS)
361 bool is_whitelisted =
362 extension && (extension->is_platform_app() ||
363 IsMediaRequestWhitelistedForExtension(extension));
364 if (is_whitelisted) {
365 // For extensions access is approved based on extension permissions.
366 ProcessMediaAccessRequestFromPlatformAppOrExtension(
367 web_contents, request, callback, extension);
368 return;
369 } 190 }
370 #endif
371 ProcessRegularMediaAccessRequest(web_contents, request, callback);
372 } 191 }
373 } 192 }
374 193
375 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission( 194 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
376 content::BrowserContext* browser_context, 195 content::BrowserContext* browser_context,
377 const GURL& security_origin, 196 const GURL& security_origin,
378 content::MediaStreamType type) { 197 content::MediaStreamType type) {
379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
380 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE || 199 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
381 type == content::MEDIA_DEVICE_VIDEO_CAPTURE); 200 type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
382 201
383 Profile* profile = Profile::FromBrowserContext(browser_context); 202 Profile* profile = Profile::FromBrowserContext(browser_context);
384 #if defined(ENABLE_EXTENSIONS) 203 #if defined(ENABLE_EXTENSIONS)
385 const extensions::Extension* extension = 204 const extensions::Extension* extension =
386 GetExtensionForOrigin(profile, security_origin); 205 GetExtensionForOrigin(profile, security_origin);
387 206
388 if (extension && (extension->is_platform_app() || 207 if (extension && (extension->is_platform_app() ||
389 IsMediaRequestWhitelistedForExtension(extension))) { 208 extension->is_media_request_white_listed())) {
390 return extension->permissions_data()->HasAPIPermission( 209 return extension->permissions_data()->HasAPIPermission(
391 type == content::MEDIA_DEVICE_AUDIO_CAPTURE 210 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
392 ? extensions::APIPermission::kAudioCapture 211 ? extensions::APIPermission::kAudioCapture
393 : extensions::APIPermission::kVideoCapture); 212 : extensions::APIPermission::kVideoCapture);
394 } 213 }
395 #endif 214 #endif
396 215
397 ContentSettingsType contentSettingsType = 216 ContentSettingsType contentSettingsType =
398 type == content::MEDIA_DEVICE_AUDIO_CAPTURE 217 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
399 ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC 218 ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
469 security_origin, 288 security_origin,
470 contentSettingsType, 289 contentSettingsType,
471 content_settings::ResourceIdentifier()) == CONTENT_SETTING_ALLOW) { 290 content_settings::ResourceIdentifier()) == CONTENT_SETTING_ALLOW) {
472 return true; 291 return true;
473 } 292 }
474 293
475 return false; 294 return false;
476 } 295 }
477 296
478 #if defined(ENABLE_EXTENSIONS) 297 #if defined(ENABLE_EXTENSIONS)
479 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission( 298 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
Sergey Ulanov 2015/05/07 01:05:48 This logic needs to be moved to MediaAccessHandler
changbin 2015/05/22 05:10:58 Done.
480 content::WebContents* web_contents, 299 content::WebContents* web_contents,
481 const GURL& security_origin, 300 const GURL& security_origin,
482 content::MediaStreamType type, 301 content::MediaStreamType type,
483 const extensions::Extension* extension) { 302 const extensions::Extension* extension) {
484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
485 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE || 304 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
486 type == content::MEDIA_DEVICE_VIDEO_CAPTURE); 305 type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
487 306
488 if (extension->is_platform_app() || 307 if (extension->is_platform_app() ||
489 IsMediaRequestWhitelistedForExtension(extension)) { 308 extension->is_media_request_white_listed()) {
490 return extension->permissions_data()->HasAPIPermission( 309 return extension->permissions_data()->HasAPIPermission(
491 type == content::MEDIA_DEVICE_AUDIO_CAPTURE 310 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
492 ? extensions::APIPermission::kAudioCapture 311 ? extensions::APIPermission::kAudioCapture
493 : extensions::APIPermission::kVideoCapture); 312 : extensions::APIPermission::kVideoCapture);
494 } 313 }
495 314
496 return CheckMediaAccessPermission(web_contents, security_origin, type); 315 return CheckMediaAccessPermission(web_contents, security_origin, type);
497 } 316 }
498 #endif 317 #endif
499 318
500 void MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest(
501 content::WebContents* web_contents,
502 const content::MediaStreamRequest& request,
503 const content::MediaResponseCallback& callback,
504 const extensions::Extension* extension) {
505 content::MediaStreamDevices devices;
506 scoped_ptr<content::MediaStreamUI> ui;
507
508 if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
509 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
510 return;
511 }
512
513 // If the device id wasn't specified then this is a screen capture request
514 // (i.e. chooseDesktopMedia() API wasn't used to generate device id).
515 if (request.requested_video_device_id.empty()) {
516 ProcessScreenCaptureAccessRequest(
517 web_contents, request, callback, extension);
518 return;
519 }
520
521 // The extension name that the stream is registered with.
522 std::string original_extension_name;
523 // Resolve DesktopMediaID for the specified device id.
524 content::DesktopMediaID media_id;
525 // TODO(miu): Replace "main RenderFrame" IDs with the request's actual
526 // RenderFrame IDs once the desktop capture extension API implementation is
527 // fixed. http://crbug.com/304341
528 content::WebContents* const web_contents_for_stream =
529 content::WebContents::FromRenderFrameHost(
530 content::RenderFrameHost::FromID(request.render_process_id,
531 request.render_frame_id));
532 content::RenderFrameHost* const main_frame = web_contents_for_stream ?
533 web_contents_for_stream->GetMainFrame() : NULL;
534 if (main_frame) {
535 media_id = GetDesktopStreamsRegistry()->RequestMediaForStreamId(
536 request.requested_video_device_id,
537 main_frame->GetProcess()->GetID(),
538 main_frame->GetRoutingID(),
539 request.security_origin,
540 &original_extension_name);
541 }
542
543 // Received invalid device id.
544 if (media_id.type == content::DesktopMediaID::TYPE_NONE) {
545 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
546 return;
547 }
548
549 bool loopback_audio_supported = false;
550 #if defined(USE_CRAS) || defined(OS_WIN)
551 // Currently loopback audio capture is supported only on Windows and ChromeOS.
552 loopback_audio_supported = true;
553 #endif
554
555 // Audio is only supported for screen capture streams.
556 bool capture_audio =
557 (media_id.type == content::DesktopMediaID::TYPE_SCREEN &&
558 request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE &&
559 loopback_audio_supported);
560
561 ui = GetDevicesForDesktopCapture(
562 &devices, media_id, capture_audio, true,
563 GetApplicationTitle(web_contents, extension),
564 base::UTF8ToUTF16(original_extension_name));
565
566 callback.Run(devices, content::MEDIA_DEVICE_OK, ui.Pass());
567 }
568
569 void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest(
570 content::WebContents* web_contents,
571 const content::MediaStreamRequest& request,
572 const content::MediaResponseCallback& callback,
573 const extensions::Extension* extension) {
574 content::MediaStreamDevices devices;
575 scoped_ptr<content::MediaStreamUI> ui;
576
577 DCHECK_EQ(request.video_type, content::MEDIA_DESKTOP_VIDEO_CAPTURE);
578
579 bool loopback_audio_supported = false;
580 #if defined(USE_CRAS) || defined(OS_WIN)
581 // Currently loopback audio capture is supported only on Windows and ChromeOS.
582 loopback_audio_supported = true;
583 #endif
584
585 bool component_extension = false;
586 #if defined(ENABLE_EXTENSIONS)
587 component_extension =
588 extension && extension->location() == extensions::Manifest::COMPONENT;
589 #endif
590
591 bool screen_capture_enabled =
592 base::CommandLine::ForCurrentProcess()->HasSwitch(
593 switches::kEnableUserMediaScreenCapturing);
594 #if defined(ENABLE_EXTENSIONS)
595 screen_capture_enabled |=
596 IsOriginForCasting(request.security_origin) ||
597 IsExtensionWhitelistedForScreenCapture(extension) ||
598 IsBuiltInExtension(request.security_origin);
599 #endif
600
601 const bool origin_is_secure =
602 request.security_origin.SchemeIsSecure() ||
603 request.security_origin.SchemeIs(extensions::kExtensionScheme) ||
604 base::CommandLine::ForCurrentProcess()->HasSwitch(
605 switches::kAllowHttpScreenCapture);
606
607 // If basic conditions (screen capturing is enabled and origin is secure)
608 // aren't fulfilled, we'll use "invalid state" as result. Otherwise, we set
609 // it after checking permission.
610 // TODO(grunell): It would be good to change this result for something else,
611 // probably a new one.
612 content::MediaStreamRequestResult result =
613 content::MEDIA_DEVICE_INVALID_STATE;
614
615 // Approve request only when the following conditions are met:
616 // 1. Screen capturing is enabled via command line switch or white-listed for
617 // the given origin.
618 // 2. Request comes from a page with a secure origin or from an extension.
619 if (screen_capture_enabled && origin_is_secure) {
620 // Get title of the calling application prior to showing the message box.
621 // chrome::ShowMessageBox() starts a nested message loop which may allow
622 // |web_contents| to be destroyed on the UI thread before the message box
623 // is closed. See http://crbug.com/326690.
624 base::string16 application_title =
625 GetApplicationTitle(web_contents, extension);
626 #if !defined(OS_ANDROID)
627 gfx::NativeWindow parent_window =
628 FindParentWindowForWebContents(web_contents);
629 #else
630 gfx::NativeWindow parent_window = NULL;
631 #endif
632 web_contents = NULL;
633
634 bool whitelisted_extension = false;
635 #if defined(ENABLE_EXTENSIONS)
636 whitelisted_extension = IsExtensionWhitelistedForScreenCapture(
637 extension);
638 #endif
639
640 // For whitelisted or component extensions, bypass message box.
641 bool user_approved = false;
642 if (!whitelisted_extension && !component_extension) {
643 base::string16 application_name =
644 base::UTF8ToUTF16(request.security_origin.spec());
645 #if defined(ENABLE_EXTENSIONS)
646 if (extension)
647 application_name = base::UTF8ToUTF16(extension->name());
648 #endif
649 base::string16 confirmation_text = l10n_util::GetStringFUTF16(
650 request.audio_type == content::MEDIA_NO_SERVICE ?
651 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT :
652 IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT,
653 application_name);
654 chrome::MessageBoxResult result = chrome::ShowMessageBox(
655 parent_window,
656 l10n_util::GetStringFUTF16(
657 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name),
658 confirmation_text,
659 chrome::MESSAGE_BOX_TYPE_QUESTION);
660 user_approved = (result == chrome::MESSAGE_BOX_RESULT_YES);
661 }
662
663 if (user_approved || component_extension || whitelisted_extension) {
664 content::DesktopMediaID screen_id;
665 #if defined(OS_CHROMEOS)
666 screen_id = content::DesktopMediaID::RegisterAuraWindow(
667 ash::Shell::GetInstance()->GetPrimaryRootWindow());
668 #else // defined(OS_CHROMEOS)
669 screen_id =
670 content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
671 webrtc::kFullDesktopScreenId);
672 #endif // !defined(OS_CHROMEOS)
673
674 bool capture_audio =
675 (request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE &&
676 loopback_audio_supported);
677
678 // Unless we're being invoked from a component extension, register to
679 // display the notification for stream capture.
680 bool display_notification = !component_extension;
681
682 ui = GetDevicesForDesktopCapture(&devices, screen_id, capture_audio,
683 display_notification, application_title,
684 application_title);
685 DCHECK(!devices.empty());
686 }
687
688 // The only case when devices can be empty is if the user has denied
689 // permission.
690 result = devices.empty() ? content::MEDIA_DEVICE_PERMISSION_DENIED
691 : content::MEDIA_DEVICE_OK;
692 }
693
694 callback.Run(devices, result, ui.Pass());
695 }
696
697 void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest(
698 content::WebContents* web_contents,
699 const content::MediaStreamRequest& request,
700 const content::MediaResponseCallback& callback,
701 const extensions::Extension* extension) {
702 content::MediaStreamDevices devices;
703 scoped_ptr<content::MediaStreamUI> ui;
704
705 #if defined(ENABLE_EXTENSIONS)
706 Profile* profile =
707 Profile::FromBrowserContext(web_contents->GetBrowserContext());
708 extensions::TabCaptureRegistry* tab_capture_registry =
709 extensions::TabCaptureRegistry::Get(profile);
710 if (!tab_capture_registry) {
711 NOTREACHED();
712 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
713 return;
714 }
715 const bool tab_capture_allowed = tab_capture_registry->VerifyRequest(
716 request.render_process_id, request.render_frame_id, extension->id());
717
718 if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE &&
719 tab_capture_allowed &&
720 extension->permissions_data()->HasAPIPermission(
721 extensions::APIPermission::kTabCapture)) {
722 devices.push_back(content::MediaStreamDevice(
723 content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string()));
724 }
725
726 if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE &&
727 tab_capture_allowed &&
728 extension->permissions_data()->HasAPIPermission(
729 extensions::APIPermission::kTabCapture)) {
730 devices.push_back(content::MediaStreamDevice(
731 content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string()));
732 }
733
734 if (!devices.empty()) {
735 ui = media_stream_capture_indicator_->RegisterMediaStream(
736 web_contents, devices);
737 }
738 callback.Run(
739 devices,
740 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE :
741 content::MEDIA_DEVICE_OK,
742 ui.Pass());
743 #else // defined(ENABLE_EXTENSIONS)
744 callback.Run(devices, content::MEDIA_DEVICE_TAB_CAPTURE_FAILURE, ui.Pass());
745 #endif // defined(ENABLE_EXTENSIONS)
746 }
747
748 #if defined(ENABLE_EXTENSIONS)
749 void MediaCaptureDevicesDispatcher::
750 ProcessMediaAccessRequestFromPlatformAppOrExtension(
751 content::WebContents* web_contents,
752 const content::MediaStreamRequest& request,
753 const content::MediaResponseCallback& callback,
754 const extensions::Extension* extension) {
755 // TODO(vrk): This code is largely duplicated in
756 // MediaStreamDevicesController::Accept(). Move this code into a shared method
757 // between the two classes.
758
759 Profile* profile =
760 Profile::FromBrowserContext(web_contents->GetBrowserContext());
761
762 bool audio_allowed =
763 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
764 extension->permissions_data()->HasAPIPermission(
765 extensions::APIPermission::kAudioCapture) &&
766 GetDevicePolicy(profile, extension->url(),
767 prefs::kAudioCaptureAllowed,
768 prefs::kAudioCaptureAllowedUrls) != ALWAYS_DENY;
769 bool video_allowed =
770 request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
771 extension->permissions_data()->HasAPIPermission(
772 extensions::APIPermission::kVideoCapture) &&
773 GetDevicePolicy(profile, extension->url(),
774 prefs::kVideoCaptureAllowed,
775 prefs::kVideoCaptureAllowedUrls) != ALWAYS_DENY;
776
777 bool get_default_audio_device = audio_allowed;
778 bool get_default_video_device = video_allowed;
779
780 content::MediaStreamDevices devices;
781
782 // Set an initial error result. If neither audio or video is allowed, we'll
783 // never try to get any device below but will just create |ui| and return an
784 // empty list with "invalid state" result. If at least one is allowed, we'll
785 // try to get device(s), and if failure, we want to return "no hardware"
786 // result.
787 // TODO(grunell): The invalid state result should be changed to a new denied
788 // result + a dcheck to ensure at least one of audio or video types is
789 // capture.
790 content::MediaStreamRequestResult result =
791 (audio_allowed || video_allowed) ? content::MEDIA_DEVICE_NO_HARDWARE
792 : content::MEDIA_DEVICE_INVALID_STATE;
793
794 // Get the exact audio or video device if an id is specified.
795 // We only set any error result here and before running the callback change
796 // it to OK if we have any device.
797 if (audio_allowed && !request.requested_audio_device_id.empty()) {
798 const content::MediaStreamDevice* audio_device =
799 GetRequestedAudioDevice(request.requested_audio_device_id);
800 if (audio_device) {
801 devices.push_back(*audio_device);
802 get_default_audio_device = false;
803 }
804 }
805 if (video_allowed && !request.requested_video_device_id.empty()) {
806 const content::MediaStreamDevice* video_device =
807 GetRequestedVideoDevice(request.requested_video_device_id);
808 if (video_device) {
809 devices.push_back(*video_device);
810 get_default_video_device = false;
811 }
812 }
813
814 // If either or both audio and video devices were requested but not
815 // specified by id, get the default devices.
816 if (get_default_audio_device || get_default_video_device) {
817 GetDefaultDevicesForProfile(profile,
818 get_default_audio_device,
819 get_default_video_device,
820 &devices);
821 }
822
823 scoped_ptr<content::MediaStreamUI> ui;
824 if (!devices.empty()) {
825 result = content::MEDIA_DEVICE_OK;
826 ui = media_stream_capture_indicator_->RegisterMediaStream(
827 web_contents, devices);
828 }
829
830 callback.Run(devices, result, ui.Pass());
831 }
832 #endif
833
834 void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest(
835 content::WebContents* web_contents,
836 const content::MediaStreamRequest& request,
837 const content::MediaResponseCallback& callback) {
838 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
839
840 RequestsQueue& queue = pending_requests_[web_contents];
841 queue.push_back(PendingAccessRequest(request, callback));
842
843 // If this is the only request then show the infobar.
844 if (queue.size() == 1)
845 ProcessQueuedAccessRequest(web_contents);
846 }
847
848 void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(
849 content::WebContents* web_contents) {
850 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
851
852 std::map<content::WebContents*, RequestsQueue>::iterator it =
853 pending_requests_.find(web_contents);
854
855 if (it == pending_requests_.end() || it->second.empty()) {
856 // Don't do anything if the tab was closed.
857 return;
858 }
859
860 DCHECK(!it->second.empty());
861
862 if (PermissionBubbleManager::Enabled() ||
863 MediaStreamPermissionBubbleExperimentEnabled()) {
864 scoped_ptr<MediaStreamDevicesController> controller(
865 new MediaStreamDevicesController(web_contents,
866 it->second.front().request,
867 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse,
868 base::Unretained(this), web_contents)));
869 if (controller->DismissInfoBarAndTakeActionOnSettings())
870 return;
871 PermissionBubbleManager* bubble_manager =
872 PermissionBubbleManager::FromWebContents(web_contents);
873 if (bubble_manager)
874 bubble_manager->AddRequest(controller.release());
875 return;
876 }
877
878 // TODO(gbillock): delete this block and the MediaStreamInfoBarDelegate
879 // when we've transitioned to bubbles. (crbug/337458)
880 MediaStreamInfoBarDelegate::Create(
881 web_contents, it->second.front().request,
882 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse,
883 base::Unretained(this), web_contents));
884 }
885
886 void MediaCaptureDevicesDispatcher::OnAccessRequestResponse(
887 content::WebContents* web_contents,
888 const content::MediaStreamDevices& devices,
889 content::MediaStreamRequestResult result,
890 scoped_ptr<content::MediaStreamUI> ui) {
891 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
892
893 std::map<content::WebContents*, RequestsQueue>::iterator it =
894 pending_requests_.find(web_contents);
895 if (it == pending_requests_.end()) {
896 // WebContents has been destroyed. Don't need to do anything.
897 return;
898 }
899
900 RequestsQueue& queue(it->second);
901 if (queue.empty())
902 return;
903
904 content::MediaResponseCallback callback = queue.front().callback;
905 queue.pop_front();
906
907 if (!queue.empty()) {
908 // Post a task to process next queued request. It has to be done
909 // asynchronously to make sure that calling infobar is not destroyed until
910 // after this function returns.
911 BrowserThread::PostTask(
912 BrowserThread::UI, FROM_HERE,
913 base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest,
914 base::Unretained(this), web_contents));
915 }
916
917 callback.Run(devices, result, ui.Pass());
918 }
919
920 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile( 319 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile(
921 Profile* profile, 320 Profile* profile,
922 bool audio, 321 bool audio,
923 bool video, 322 bool video,
924 content::MediaStreamDevices* devices) { 323 content::MediaStreamDevices* devices) {
925 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 324 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
926 DCHECK(audio || video); 325 DCHECK(audio || video);
927 326
928 PrefService* prefs = profile->GetPrefs(); 327 PrefService* prefs = profile->GetPrefs();
929 std::string default_device; 328 std::string default_device;
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
1143 542
1144 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices( 543 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices(
1145 const MediaStreamDevices& devices) { 544 const MediaStreamDevices& devices) {
1146 test_audio_devices_ = devices; 545 test_audio_devices_ = devices;
1147 } 546 }
1148 547
1149 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices( 548 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices(
1150 const MediaStreamDevices& devices) { 549 const MediaStreamDevices& devices) {
1151 test_video_devices_ = devices; 550 test_video_devices_ = devices;
1152 } 551 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698