1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
6
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/metrics/field_trial.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/prefs/scoped_user_pref_update.h"
12 #include "base/sha1.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/content_settings/host_content_settings_map.h"
17 #include "chrome/browser/media/desktop_streams_registry.h"
18 #include "chrome/browser/media/media_stream_capture_indicator.h"
19 #include "chrome/browser/media/media_stream_device_permissions.h"
20 #include "chrome/browser/media/media_stream_infobar_delegate.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/browser_finder.h"
24 #include "chrome/browser/ui/browser_window.h"
25 #include "chrome/browser/ui/screen_capture_notification_ui.h"
26 #include "chrome/browser/ui/simple_message_box.h"
27 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
28 #include "chrome/common/chrome_switches.h"
29 #include "chrome/common/chrome_version_info.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/grit/generated_resources.h"
32 #include "components/content_settings/core/browser/content_settings_provider.h"
33 #include "components/pref_registry/pref_registry_syncable.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/desktop_media_id.h"
36 #include "content/public/browser/media_capture_devices.h"
37 #include "content/public/browser/notification_service.h"
38 #include "content/public/browser/notification_source.h"
39 #include "content/public/browser/notification_types.h"
40 #include "content/public/browser/render_frame_host.h"
41 #include "content/public/browser/render_process_host.h"
42 #include "content/public/browser/web_contents.h"
43 #include "content/public/common/media_stream_request.h"
44 #include "extensions/common/constants.h"
45 #include "extensions/common/extension.h"
46 #include "extensions/common/permissions/permissions_data.h"
47 #include "media/audio/audio_manager_base.h"
48 #include "media/base/media_switches.h"
49 #include "net/base/net_util.h"
50 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"
51 #include "ui/base/l10n/l10n_util.h"
52
53 #if defined(OS_CHROMEOS)
54 #include "ash/shell.h"
55 #endif // defined(OS_CHROMEOS)
56
57
58 #if defined(ENABLE_EXTENSIONS)
59 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
60 #include "chrome/browser/extensions/extension_service.h"
61 #include "extensions/browser/app_window/app_window.h"
62 #include "extensions/browser/app_window/app_window_registry.h"
63 #include "extensions/browser/extension_system.h"
64 #endif
65
66 using content::BrowserThread;
67 using content::MediaCaptureDevices;
68 using content::MediaStreamDevices;
69
70 namespace {
71
72 // A finch experiment to enable the permission bubble for media requests only.
MediaStreamPermissionBubbleExperimentEnabled()73 bool MediaStreamPermissionBubbleExperimentEnabled() {
74 const std::string group =
75 base::FieldTrialList::FindFullName("MediaStreamPermissionBubble");
76 if (group == "enabled")
77 return true;
78
79 return false;
80 }
81
82 // Finds a device in |devices| that has |device_id|, or NULL if not found.
FindDeviceWithId(const content::MediaStreamDevices & devices,const std::string & device_id)83 const content::MediaStreamDevice* FindDeviceWithId(
84 const content::MediaStreamDevices& devices,
85 const std::string& device_id) {
86 content::MediaStreamDevices::const_iterator iter = devices.begin();
87 for (; iter != devices.end(); ++iter) {
88 if (iter->id == device_id) {
89 return &(*iter);
90 }
91 }
92 return NULL;
93 }
94
95 // This is a short-term solution to grant camera and/or microphone access to
96 // extensions:
97 // 1. Virtual keyboard extension.
98 // 2. Google Voice Search Hotword extension.
99 // 3. Flutter gesture recognition extension.
100 // 4. TODO(smus): Airbender experiment 1.
101 // 5. TODO(smus): Airbender experiment 2.
102 // 6. Hotwording component extension.
103 // Once http://crbug.com/292856 is fixed, remove this whitelist.
IsMediaRequestWhitelistedForExtension(const extensions::Extension * extension)104 bool IsMediaRequestWhitelistedForExtension(
105 const extensions::Extension* extension) {
106 return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" ||
107 extension->id() == "bepbmhgboaologfdajaanbcjmnhjmhfn" ||
108 extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" ||
109 extension->id() == "clffjmdilanldobdnedchkdbofoimcgb" ||
110 extension->id() == "nnckehldicaciogcbchegobnafnjkcne" ||
111 extension->id() == "nbpagnldghgfoolbancepceaanlmhfmd";
112 }
113
IsBuiltInExtension(const GURL & origin)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.
IsOriginForCasting(const GURL & origin)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 }
135
136 // Helper to get title of the calling application shown in the screen capture
137 // notification.
GetApplicationTitle(content::WebContents * web_contents,const extensions::Extension * extension)138 base::string16 GetApplicationTitle(content::WebContents* web_contents,
139 const extensions::Extension* extension) {
140 // Use extension name as title for extensions and host/origin for drive-by
141 // web.
142 std::string title;
143 if (extension) {
144 title = extension->name();
145 } else {
146 GURL url = web_contents->GetURL();
147 title = url.SchemeIsSecure() ? net::GetHostAndOptionalPort(url)
148 : url.GetOrigin().spec();
149 }
150 return base::UTF8ToUTF16(title);
151 }
152
153 // Helper to get list of media stream devices for desktop capture in |devices|.
154 // Registers to display notification if |display_notification| is true.
155 // Returns an instance of MediaStreamUI to be passed to content layer.
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)156 scoped_ptr<content::MediaStreamUI> GetDevicesForDesktopCapture(
157 content::MediaStreamDevices& devices,
158 content::DesktopMediaID media_id,
159 bool capture_audio,
160 bool display_notification,
161 const base::string16& application_title,
162 const base::string16& registered_extension_name) {
163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
164 scoped_ptr<content::MediaStreamUI> ui;
165
166 // Add selected desktop source to the list.
167 devices.push_back(content::MediaStreamDevice(
168 content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen"));
169 if (capture_audio) {
170 // Use the special loopback device ID for system audio capture.
171 devices.push_back(content::MediaStreamDevice(
172 content::MEDIA_LOOPBACK_AUDIO_CAPTURE,
173 media::AudioManagerBase::kLoopbackInputDeviceId, "System Audio"));
174 }
175
176 // If required, register to display the notification for stream capture.
177 if (display_notification) {
178 if (application_title == registered_extension_name) {
179 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
180 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT,
181 application_title));
182 } else {
183 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
184 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT_DELEGATED,
185 registered_extension_name,
186 application_title));
187 }
188 }
189
190 return ui.Pass();
191 }
192
193 #if !defined(OS_ANDROID)
194 // Find browser or app window from a given |web_contents|.
FindParentWindowForWebContents(content::WebContents * web_contents)195 gfx::NativeWindow FindParentWindowForWebContents(
196 content::WebContents* web_contents) {
197 Browser* browser = chrome::FindBrowserWithWebContents(web_contents);
198 if (browser && browser->window())
199 return browser->window()->GetNativeWindow();
200
201 const extensions::AppWindowRegistry::AppWindowList& window_list =
202 extensions::AppWindowRegistry::Get(
203 web_contents->GetBrowserContext())->app_windows();
204 for (extensions::AppWindowRegistry::AppWindowList::const_iterator iter =
205 window_list.begin();
206 iter != window_list.end(); ++iter) {
207 if ((*iter)->web_contents() == web_contents)
208 return (*iter)->GetNativeWindow();
209 }
210
211 return NULL;
212 }
213 #endif
214
GetExtensionForOrigin(Profile * profile,const GURL & security_origin)215 const extensions::Extension* GetExtensionForOrigin(
216 Profile* profile,
217 const GURL& security_origin) {
218 #if defined(ENABLE_EXTENSIONS)
219 if (!security_origin.SchemeIs(extensions::kExtensionScheme))
220 return NULL;
221
222 ExtensionService* extensions_service =
223 extensions::ExtensionSystem::Get(profile)->extension_service();
224 const extensions::Extension* extension =
225 extensions_service->extensions()->GetByID(security_origin.host());
226 DCHECK(extension);
227 return extension;
228 #else
229 return NULL;
230 #endif
231 }
232
233 } // namespace
234
PendingAccessRequest(const content::MediaStreamRequest & request,const content::MediaResponseCallback & callback)235 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
236 const content::MediaStreamRequest& request,
237 const content::MediaResponseCallback& callback)
238 : request(request),
239 callback(callback) {
240 }
241
~PendingAccessRequest()242 MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {}
243
GetInstance()244 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
245 return Singleton<MediaCaptureDevicesDispatcher>::get();
246 }
247
MediaCaptureDevicesDispatcher()248 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
249 : is_device_enumeration_disabled_(false),
250 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) {
251 // MediaCaptureDevicesDispatcher is a singleton. It should be created on
252 // UI thread. Otherwise, it will not receive
253 // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in
254 // possible use after free.
255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
256 notifications_registrar_.Add(
257 this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
258 content::NotificationService::AllSources());
259
260 // AVFoundation is used for video/audio device monitoring and video capture in
261 // Mac. Experimentally, connect it in Dev, Canary and Unknown (developer
262 // builds).
263 #if defined(OS_MACOSX)
264 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
265 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kForceQTKit)) {
266 if (channel == chrome::VersionInfo::CHANNEL_DEV ||
267 channel == chrome::VersionInfo::CHANNEL_CANARY ||
268 channel == chrome::VersionInfo::CHANNEL_UNKNOWN) {
269 CommandLine::ForCurrentProcess()->AppendSwitch(
270 switches::kEnableAVFoundation);
271 }
272 }
273 #endif
274 }
275
~MediaCaptureDevicesDispatcher()276 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
277
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)278 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
279 user_prefs::PrefRegistrySyncable* registry) {
280 registry->RegisterStringPref(
281 prefs::kDefaultAudioCaptureDevice,
282 std::string(),
283 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
284 registry->RegisterStringPref(
285 prefs::kDefaultVideoCaptureDevice,
286 std::string(),
287 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
288 }
289
AddObserver(Observer * observer)290 void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) {
291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
292 if (!observers_.HasObserver(observer))
293 observers_.AddObserver(observer);
294 }
295
RemoveObserver(Observer * observer)296 void MediaCaptureDevicesDispatcher::RemoveObserver(Observer* observer) {
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
298 observers_.RemoveObserver(observer);
299 }
300
301 const MediaStreamDevices&
GetAudioCaptureDevices()302 MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() {
303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
304 if (is_device_enumeration_disabled_ || !test_audio_devices_.empty())
305 return test_audio_devices_;
306
307 return MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices();
308 }
309
310 const MediaStreamDevices&
GetVideoCaptureDevices()311 MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() {
312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
313 if (is_device_enumeration_disabled_ || !test_video_devices_.empty())
314 return test_video_devices_;
315
316 return MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices();
317 }
318
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)319 void MediaCaptureDevicesDispatcher::Observe(
320 int type,
321 const content::NotificationSource& source,
322 const content::NotificationDetails& details) {
323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324 if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
325 content::WebContents* web_contents =
326 content::Source<content::WebContents>(source).ptr();
327 pending_requests_.erase(web_contents);
328 }
329 }
330
ProcessMediaAccessRequest(content::WebContents * web_contents,const content::MediaStreamRequest & request,const content::MediaResponseCallback & callback,const extensions::Extension * extension)331 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest(
332 content::WebContents* web_contents,
333 const content::MediaStreamRequest& request,
334 const content::MediaResponseCallback& callback,
335 const extensions::Extension* extension) {
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337
338 if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE ||
339 request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE) {
340 ProcessDesktopCaptureAccessRequest(
341 web_contents, request, callback, extension);
342 } else if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE ||
343 request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) {
344 ProcessTabCaptureAccessRequest(
345 web_contents, request, callback, extension);
346 } else if (extension && (extension->is_platform_app() ||
347 IsMediaRequestWhitelistedForExtension(extension))) {
348 // For extensions access is approved based on extension permissions.
349 ProcessMediaAccessRequestFromPlatformAppOrExtension(
350 web_contents, request, callback, extension);
351 } else {
352 ProcessRegularMediaAccessRequest(web_contents, request, callback);
353 }
354 }
355
CheckMediaAccessPermission(content::BrowserContext * browser_context,const GURL & security_origin,content::MediaStreamType type)356 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
357 content::BrowserContext* browser_context,
358 const GURL& security_origin,
359 content::MediaStreamType type) {
360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
361 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
362 type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
363
364 Profile* profile = Profile::FromBrowserContext(browser_context);
365 const extensions::Extension* extension =
366 GetExtensionForOrigin(profile, security_origin);
367
368 if (extension && (extension->is_platform_app() ||
369 IsMediaRequestWhitelistedForExtension(extension))) {
370 return extension->permissions_data()->HasAPIPermission(
371 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
372 ? extensions::APIPermission::kAudioCapture
373 : extensions::APIPermission::kVideoCapture);
374 }
375
376 if (CheckAllowAllMediaStreamContentForOrigin(profile, security_origin))
377 return true;
378
379 const char* policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE
380 ? prefs::kAudioCaptureAllowed
381 : prefs::kVideoCaptureAllowed;
382 const char* list_policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE
383 ? prefs::kAudioCaptureAllowedUrls
384 : prefs::kVideoCaptureAllowedUrls;
385 if (GetDevicePolicy(
386 profile, security_origin, policy_name, list_policy_name) ==
387 ALWAYS_ALLOW) {
388 return true;
389 }
390
391 // There's no secondary URL for these content types, hence duplicating
392 // |security_origin|.
393 if (profile->GetHostContentSettingsMap()->GetContentSetting(
394 security_origin,
395 security_origin,
396 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
397 ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
398 : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
399 NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_ALLOW) {
400 return true;
401 }
402
403 return false;
404 }
405
CheckMediaAccessPermission(content::WebContents * web_contents,const GURL & security_origin,content::MediaStreamType type)406 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
407 content::WebContents* web_contents,
408 const GURL& security_origin,
409 content::MediaStreamType type) {
410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
411 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
412 type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
413
414 Profile* profile =
415 Profile::FromBrowserContext(web_contents->GetBrowserContext());
416
417 if (CheckAllowAllMediaStreamContentForOrigin(profile, security_origin))
418 return true;
419
420 const char* policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE
421 ? prefs::kAudioCaptureAllowed
422 : prefs::kVideoCaptureAllowed;
423 const char* list_policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE
424 ? prefs::kAudioCaptureAllowedUrls
425 : prefs::kVideoCaptureAllowedUrls;
426 if (GetDevicePolicy(
427 profile, security_origin, policy_name, list_policy_name) ==
428 ALWAYS_ALLOW) {
429 return true;
430 }
431
432 // There's no secondary URL for these content types, hence duplicating
433 // |security_origin|.
434 if (profile->GetHostContentSettingsMap()->GetContentSetting(
435 security_origin,
436 security_origin,
437 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
438 ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
439 : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
440 NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_ALLOW) {
441 return true;
442 }
443
444 return false;
445 }
446
447 #if defined(ENABLE_EXTENSIONS)
CheckMediaAccessPermission(content::WebContents * web_contents,const GURL & security_origin,content::MediaStreamType type,const extensions::Extension * extension)448 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission(
449 content::WebContents* web_contents,
450 const GURL& security_origin,
451 content::MediaStreamType type,
452 const extensions::Extension* extension) {
453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
454 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
455 type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
456
457 if (extension->is_platform_app() ||
458 IsMediaRequestWhitelistedForExtension(extension)) {
459 return extension->permissions_data()->HasAPIPermission(
460 type == content::MEDIA_DEVICE_AUDIO_CAPTURE
461 ? extensions::APIPermission::kAudioCapture
462 : extensions::APIPermission::kVideoCapture);
463 }
464
465 return CheckMediaAccessPermission(web_contents, security_origin, type);
466 }
467 #endif
468
ProcessDesktopCaptureAccessRequest(content::WebContents * web_contents,const content::MediaStreamRequest & request,const content::MediaResponseCallback & callback,const extensions::Extension * extension)469 void MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest(
470 content::WebContents* web_contents,
471 const content::MediaStreamRequest& request,
472 const content::MediaResponseCallback& callback,
473 const extensions::Extension* extension) {
474 content::MediaStreamDevices devices;
475 scoped_ptr<content::MediaStreamUI> ui;
476
477 if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
478 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
479 return;
480 }
481
482 // If the device id wasn't specified then this is a screen capture request
483 // (i.e. chooseDesktopMedia() API wasn't used to generate device id).
484 if (request.requested_video_device_id.empty()) {
485 ProcessScreenCaptureAccessRequest(
486 web_contents, request, callback, extension);
487 return;
488 }
489
490 // The extension name that the stream is registered with.
491 std::string original_extension_name;
492 // Resolve DesktopMediaID for the specified device id.
493 content::DesktopMediaID media_id;
494 // TODO(miu): Replace "main RenderFrame" IDs with the request's actual
495 // RenderFrame IDs once the desktop capture extension API implementation is
496 // fixed. http://crbug.com/304341
497 content::WebContents* const web_contents_for_stream =
498 content::WebContents::FromRenderFrameHost(
499 content::RenderFrameHost::FromID(request.render_process_id,
500 request.render_frame_id));
501 content::RenderFrameHost* const main_frame = web_contents_for_stream ?
502 web_contents_for_stream->GetMainFrame() : NULL;
503 if (main_frame) {
504 media_id = GetDesktopStreamsRegistry()->RequestMediaForStreamId(
505 request.requested_video_device_id,
506 main_frame->GetProcess()->GetID(),
507 main_frame->GetRoutingID(),
508 request.security_origin,
509 &original_extension_name);
510 }
511
512 // Received invalid device id.
513 if (media_id.type == content::DesktopMediaID::TYPE_NONE) {
514 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
515 return;
516 }
517
518 bool loopback_audio_supported = false;
519 #if defined(USE_CRAS) || defined(OS_WIN)
520 // Currently loopback audio capture is supported only on Windows and ChromeOS.
521 loopback_audio_supported = true;
522 #endif
523
524 // Audio is only supported for screen capture streams.
525 bool capture_audio =
526 (media_id.type == content::DesktopMediaID::TYPE_SCREEN &&
527 request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE &&
528 loopback_audio_supported);
529
530 ui = GetDevicesForDesktopCapture(
531 devices, media_id, capture_audio, true,
532 GetApplicationTitle(web_contents, extension),
533 base::UTF8ToUTF16(original_extension_name));
534
535 callback.Run(devices, content::MEDIA_DEVICE_OK, ui.Pass());
536 }
537
ProcessScreenCaptureAccessRequest(content::WebContents * web_contents,const content::MediaStreamRequest & request,const content::MediaResponseCallback & callback,const extensions::Extension * extension)538 void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest(
539 content::WebContents* web_contents,
540 const content::MediaStreamRequest& request,
541 const content::MediaResponseCallback& callback,
542 const extensions::Extension* extension) {
543 content::MediaStreamDevices devices;
544 scoped_ptr<content::MediaStreamUI> ui;
545
546 DCHECK_EQ(request.video_type, content::MEDIA_DESKTOP_VIDEO_CAPTURE);
547
548 bool loopback_audio_supported = false;
549 #if defined(USE_CRAS) || defined(OS_WIN)
550 // Currently loopback audio capture is supported only on Windows and ChromeOS.
551 loopback_audio_supported = true;
552 #endif
553
554 const bool component_extension =
555 extension && extension->location() == extensions::Manifest::COMPONENT;
556
557 const bool screen_capture_enabled =
558 CommandLine::ForCurrentProcess()->HasSwitch(
559 switches::kEnableUserMediaScreenCapturing) ||
560 IsOriginForCasting(request.security_origin) ||
561 IsBuiltInExtension(request.security_origin);
562
563 const bool origin_is_secure =
564 request.security_origin.SchemeIsSecure() ||
565 request.security_origin.SchemeIs(extensions::kExtensionScheme) ||
566 CommandLine::ForCurrentProcess()->HasSwitch(
567 switches::kAllowHttpScreenCapture);
568
569 // If basic conditions (screen capturing is enabled and origin is secure)
570 // aren't fulfilled, we'll use "invalid state" as result. Otherwise, we set
571 // it after checking permission.
572 // TODO(grunell): It would be good to change this result for something else,
573 // probably a new one.
574 content::MediaStreamRequestResult result =
575 content::MEDIA_DEVICE_INVALID_STATE;
576
577 // Approve request only when the following conditions are met:
578 // 1. Screen capturing is enabled via command line switch or white-listed for
579 // the given origin.
580 // 2. Request comes from a page with a secure origin or from an extension.
581 if (screen_capture_enabled && origin_is_secure) {
582 // Get title of the calling application prior to showing the message box.
583 // chrome::ShowMessageBox() starts a nested message loop which may allow
584 // |web_contents| to be destroyed on the UI thread before the message box
585 // is closed. See http://crbug.com/326690.
586 base::string16 application_title =
587 GetApplicationTitle(web_contents, extension);
588 #if !defined(OS_ANDROID)
589 gfx::NativeWindow parent_window =
590 FindParentWindowForWebContents(web_contents);
591 #else
592 gfx::NativeWindow parent_window = NULL;
593 #endif
594 web_contents = NULL;
595
596 // For component extensions, bypass message box.
597 bool user_approved = false;
598 if (!component_extension) {
599 base::string16 application_name = base::UTF8ToUTF16(
600 extension ? extension->name() : request.security_origin.spec());
601 base::string16 confirmation_text = l10n_util::GetStringFUTF16(
602 request.audio_type == content::MEDIA_NO_SERVICE ?
603 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT :
604 IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT,
605 application_name);
606 chrome::MessageBoxResult result = chrome::ShowMessageBox(
607 parent_window,
608 l10n_util::GetStringFUTF16(
609 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name),
610 confirmation_text,
611 chrome::MESSAGE_BOX_TYPE_QUESTION);
612 user_approved = (result == chrome::MESSAGE_BOX_RESULT_YES);
613 }
614
615 if (user_approved || component_extension) {
616 content::DesktopMediaID screen_id;
617 #if defined(OS_CHROMEOS)
618 screen_id = content::DesktopMediaID::RegisterAuraWindow(
619 ash::Shell::GetInstance()->GetPrimaryRootWindow());
620 #else // defined(OS_CHROMEOS)
621 screen_id =
622 content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN,
623 webrtc::kFullDesktopScreenId);
624 #endif // !defined(OS_CHROMEOS)
625
626 bool capture_audio =
627 (request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE &&
628 loopback_audio_supported);
629
630 // Unless we're being invoked from a component extension, register to
631 // display the notification for stream capture.
632 bool display_notification = !component_extension;
633
634 ui = GetDevicesForDesktopCapture(devices, screen_id, capture_audio,
635 display_notification, application_title,
636 application_title);
637 DCHECK(!devices.empty());
638 }
639
640 // The only case when devices can be empty is if the user has denied
641 // permission.
642 result = devices.empty() ? content::MEDIA_DEVICE_PERMISSION_DENIED
643 : content::MEDIA_DEVICE_OK;
644 }
645
646 callback.Run(devices, result, ui.Pass());
647 }
648
ProcessTabCaptureAccessRequest(content::WebContents * web_contents,const content::MediaStreamRequest & request,const content::MediaResponseCallback & callback,const extensions::Extension * extension)649 void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest(
650 content::WebContents* web_contents,
651 const content::MediaStreamRequest& request,
652 const content::MediaResponseCallback& callback,
653 const extensions::Extension* extension) {
654 content::MediaStreamDevices devices;
655 scoped_ptr<content::MediaStreamUI> ui;
656
657 #if defined(ENABLE_EXTENSIONS) && !defined(USE_ATHENA)
658 Profile* profile =
659 Profile::FromBrowserContext(web_contents->GetBrowserContext());
660 extensions::TabCaptureRegistry* tab_capture_registry =
661 extensions::TabCaptureRegistry::Get(profile);
662 if (!tab_capture_registry) {
663 NOTREACHED();
664 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass());
665 return;
666 }
667 const bool tab_capture_allowed = tab_capture_registry->VerifyRequest(
668 request.render_process_id, request.render_frame_id, extension->id());
669
670 if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE &&
671 tab_capture_allowed &&
672 extension->permissions_data()->HasAPIPermission(
673 extensions::APIPermission::kTabCapture)) {
674 devices.push_back(content::MediaStreamDevice(
675 content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string()));
676 }
677
678 if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE &&
679 tab_capture_allowed &&
680 extension->permissions_data()->HasAPIPermission(
681 extensions::APIPermission::kTabCapture)) {
682 devices.push_back(content::MediaStreamDevice(
683 content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string()));
684 }
685
686 if (!devices.empty()) {
687 ui = media_stream_capture_indicator_->RegisterMediaStream(
688 web_contents, devices);
689 }
690 callback.Run(
691 devices,
692 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE :
693 content::MEDIA_DEVICE_OK,
694 ui.Pass());
695 #else // defined(ENABLE_EXTENSIONS)
696 callback.Run(devices, content::MEDIA_DEVICE_TAB_CAPTURE_FAILURE, ui.Pass());
697 #endif // defined(ENABLE_EXTENSIONS)
698 }
699
700 void MediaCaptureDevicesDispatcher::
ProcessMediaAccessRequestFromPlatformAppOrExtension(content::WebContents * web_contents,const content::MediaStreamRequest & request,const content::MediaResponseCallback & callback,const extensions::Extension * extension)701 ProcessMediaAccessRequestFromPlatformAppOrExtension(
702 content::WebContents* web_contents,
703 const content::MediaStreamRequest& request,
704 const content::MediaResponseCallback& callback,
705 const extensions::Extension* extension) {
706 // TODO(vrk): This code is largely duplicated in
707 // MediaStreamDevicesController::Accept(). Move this code into a shared method
708 // between the two classes.
709
710 Profile* profile =
711 Profile::FromBrowserContext(web_contents->GetBrowserContext());
712
713 bool audio_allowed =
714 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
715 extension->permissions_data()->HasAPIPermission(
716 extensions::APIPermission::kAudioCapture) &&
717 GetDevicePolicy(profile, extension->url(),
718 prefs::kAudioCaptureAllowed,
719 prefs::kAudioCaptureAllowedUrls) != ALWAYS_DENY;
720 bool video_allowed =
721 request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
722 extension->permissions_data()->HasAPIPermission(
723 extensions::APIPermission::kVideoCapture) &&
724 GetDevicePolicy(profile, extension->url(),
725 prefs::kVideoCaptureAllowed,
726 prefs::kVideoCaptureAllowedUrls) != ALWAYS_DENY;
727
728 bool get_default_audio_device = audio_allowed;
729 bool get_default_video_device = video_allowed;
730
731 content::MediaStreamDevices devices;
732
733 // Set an initial error result. If neither audio or video is allowed, we'll
734 // never try to get any device below but will just create |ui| and return an
735 // empty list with "invalid state" result. If at least one is allowed, we'll
736 // try to get device(s), and if failure, we want to return "no hardware"
737 // result.
738 // TODO(grunell): The invalid state result should be changed to a new denied
739 // result + a dcheck to ensure at least one of audio or video types is
740 // capture.
741 content::MediaStreamRequestResult result =
742 (audio_allowed || video_allowed) ? content::MEDIA_DEVICE_NO_HARDWARE
743 : content::MEDIA_DEVICE_INVALID_STATE;
744
745 // Get the exact audio or video device if an id is specified.
746 // We only set any error result here and before running the callback change
747 // it to OK if we have any device.
748 if (audio_allowed && !request.requested_audio_device_id.empty()) {
749 const content::MediaStreamDevice* audio_device =
750 GetRequestedAudioDevice(request.requested_audio_device_id);
751 if (audio_device) {
752 devices.push_back(*audio_device);
753 get_default_audio_device = false;
754 }
755 }
756 if (video_allowed && !request.requested_video_device_id.empty()) {
757 const content::MediaStreamDevice* video_device =
758 GetRequestedVideoDevice(request.requested_video_device_id);
759 if (video_device) {
760 devices.push_back(*video_device);
761 get_default_video_device = false;
762 }
763 }
764
765 // If either or both audio and video devices were requested but not
766 // specified by id, get the default devices.
767 if (get_default_audio_device || get_default_video_device) {
768 GetDefaultDevicesForProfile(profile,
769 get_default_audio_device,
770 get_default_video_device,
771 &devices);
772 }
773
774 scoped_ptr<content::MediaStreamUI> ui;
775 if (!devices.empty()) {
776 result = content::MEDIA_DEVICE_OK;
777 ui = media_stream_capture_indicator_->RegisterMediaStream(
778 web_contents, devices);
779 }
780
781 callback.Run(devices, result, ui.Pass());
782 }
783
ProcessRegularMediaAccessRequest(content::WebContents * web_contents,const content::MediaStreamRequest & request,const content::MediaResponseCallback & callback)784 void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest(
785 content::WebContents* web_contents,
786 const content::MediaStreamRequest& request,
787 const content::MediaResponseCallback& callback) {
788 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
789
790 RequestsQueue& queue = pending_requests_[web_contents];
791 queue.push_back(PendingAccessRequest(request, callback));
792
793 // If this is the only request then show the infobar.
794 if (queue.size() == 1)
795 ProcessQueuedAccessRequest(web_contents);
796 }
797
ProcessQueuedAccessRequest(content::WebContents * web_contents)798 void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(
799 content::WebContents* web_contents) {
800 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
801
802 std::map<content::WebContents*, RequestsQueue>::iterator it =
803 pending_requests_.find(web_contents);
804
805 if (it == pending_requests_.end() || it->second.empty()) {
806 // Don't do anything if the tab was closed.
807 return;
808 }
809
810 DCHECK(!it->second.empty());
811
812 if (PermissionBubbleManager::Enabled() ||
813 MediaStreamPermissionBubbleExperimentEnabled()) {
814 scoped_ptr<MediaStreamDevicesController> controller(
815 new MediaStreamDevicesController(web_contents,
816 it->second.front().request,
817 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse,
818 base::Unretained(this), web_contents)));
819 if (controller->DismissInfoBarAndTakeActionOnSettings())
820 return;
821 PermissionBubbleManager* bubble_manager =
822 PermissionBubbleManager::FromWebContents(web_contents);
823 if (bubble_manager)
824 bubble_manager->AddRequest(controller.release());
825 return;
826 }
827
828 // TODO(gbillock): delete this block and the MediaStreamInfoBarDelegate
829 // when we've transitioned to bubbles. (crbug/337458)
830 MediaStreamInfoBarDelegate::Create(
831 web_contents, it->second.front().request,
832 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse,
833 base::Unretained(this), web_contents));
834 }
835
OnAccessRequestResponse(content::WebContents * web_contents,const content::MediaStreamDevices & devices,content::MediaStreamRequestResult result,scoped_ptr<content::MediaStreamUI> ui)836 void MediaCaptureDevicesDispatcher::OnAccessRequestResponse(
837 content::WebContents* web_contents,
838 const content::MediaStreamDevices& devices,
839 content::MediaStreamRequestResult result,
840 scoped_ptr<content::MediaStreamUI> ui) {
841 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
842
843 std::map<content::WebContents*, RequestsQueue>::iterator it =
844 pending_requests_.find(web_contents);
845 if (it == pending_requests_.end()) {
846 // WebContents has been destroyed. Don't need to do anything.
847 return;
848 }
849
850 RequestsQueue& queue(it->second);
851 if (queue.empty())
852 return;
853
854 content::MediaResponseCallback callback = queue.front().callback;
855 queue.pop_front();
856
857 if (!queue.empty()) {
858 // Post a task to process next queued request. It has to be done
859 // asynchronously to make sure that calling infobar is not destroyed until
860 // after this function returns.
861 BrowserThread::PostTask(
862 BrowserThread::UI, FROM_HERE,
863 base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest,
864 base::Unretained(this), web_contents));
865 }
866
867 callback.Run(devices, result, ui.Pass());
868 }
869
GetDefaultDevicesForProfile(Profile * profile,bool audio,bool video,content::MediaStreamDevices * devices)870 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile(
871 Profile* profile,
872 bool audio,
873 bool video,
874 content::MediaStreamDevices* devices) {
875 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
876 DCHECK(audio || video);
877
878 PrefService* prefs = profile->GetPrefs();
879 std::string default_device;
880 if (audio) {
881 default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
882 const content::MediaStreamDevice* device =
883 GetRequestedAudioDevice(default_device);
884 if (!device)
885 device = GetFirstAvailableAudioDevice();
886 if (device)
887 devices->push_back(*device);
888 }
889
890 if (video) {
891 default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
892 const content::MediaStreamDevice* device =
893 GetRequestedVideoDevice(default_device);
894 if (!device)
895 device = GetFirstAvailableVideoDevice();
896 if (device)
897 devices->push_back(*device);
898 }
899 }
900
901 const content::MediaStreamDevice*
GetRequestedAudioDevice(const std::string & requested_audio_device_id)902 MediaCaptureDevicesDispatcher::GetRequestedAudioDevice(
903 const std::string& requested_audio_device_id) {
904 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
905 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
906 const content::MediaStreamDevice* const device =
907 FindDeviceWithId(audio_devices, requested_audio_device_id);
908 return device;
909 }
910
911 const content::MediaStreamDevice*
GetFirstAvailableAudioDevice()912 MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() {
913 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
914 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
915 if (audio_devices.empty())
916 return NULL;
917 return &(*audio_devices.begin());
918 }
919
920 const content::MediaStreamDevice*
GetRequestedVideoDevice(const std::string & requested_video_device_id)921 MediaCaptureDevicesDispatcher::GetRequestedVideoDevice(
922 const std::string& requested_video_device_id) {
923 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
924 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
925 const content::MediaStreamDevice* const device =
926 FindDeviceWithId(video_devices, requested_video_device_id);
927 return device;
928 }
929
930 const content::MediaStreamDevice*
GetFirstAvailableVideoDevice()931 MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() {
932 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
933 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
934 if (video_devices.empty())
935 return NULL;
936 return &(*video_devices.begin());
937 }
938
DisableDeviceEnumerationForTesting()939 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
940 is_device_enumeration_disabled_ = true;
941 }
942
943 scoped_refptr<MediaStreamCaptureIndicator>
GetMediaStreamCaptureIndicator()944 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
945 return media_stream_capture_indicator_;
946 }
947
948 DesktopStreamsRegistry*
GetDesktopStreamsRegistry()949 MediaCaptureDevicesDispatcher::GetDesktopStreamsRegistry() {
950 if (!desktop_streams_registry_)
951 desktop_streams_registry_.reset(new DesktopStreamsRegistry());
952 return desktop_streams_registry_.get();
953 }
954
OnAudioCaptureDevicesChanged()955 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() {
956 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
957 BrowserThread::PostTask(
958 BrowserThread::UI, FROM_HERE,
959 base::Bind(
960 &MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread,
961 base::Unretained(this)));
962 }
963
OnVideoCaptureDevicesChanged()964 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() {
965 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
966 BrowserThread::PostTask(
967 BrowserThread::UI, FROM_HERE,
968 base::Bind(
969 &MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread,
970 base::Unretained(this)));
971 }
972
OnMediaRequestStateChanged(int render_process_id,int render_frame_id,int page_request_id,const GURL & security_origin,content::MediaStreamType stream_type,content::MediaRequestState state)973 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
974 int render_process_id,
975 int render_frame_id,
976 int page_request_id,
977 const GURL& security_origin,
978 content::MediaStreamType stream_type,
979 content::MediaRequestState state) {
980 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
981 BrowserThread::PostTask(
982 BrowserThread::UI, FROM_HERE,
983 base::Bind(
984 &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread,
985 base::Unretained(this), render_process_id, render_frame_id,
986 page_request_id, security_origin, stream_type, state));
987 }
988
OnCreatingAudioStream(int render_process_id,int render_frame_id)989 void MediaCaptureDevicesDispatcher::OnCreatingAudioStream(
990 int render_process_id,
991 int render_frame_id) {
992 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
993 BrowserThread::PostTask(
994 BrowserThread::UI, FROM_HERE,
995 base::Bind(
996 &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread,
997 base::Unretained(this), render_process_id, render_frame_id));
998 }
999
NotifyAudioDevicesChangedOnUIThread()1000 void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() {
1001 MediaStreamDevices devices = GetAudioCaptureDevices();
1002 FOR_EACH_OBSERVER(Observer, observers_,
1003 OnUpdateAudioDevices(devices));
1004 }
1005
NotifyVideoDevicesChangedOnUIThread()1006 void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() {
1007 MediaStreamDevices devices = GetVideoCaptureDevices();
1008 FOR_EACH_OBSERVER(Observer, observers_,
1009 OnUpdateVideoDevices(devices));
1010 }
1011
UpdateMediaRequestStateOnUIThread(int render_process_id,int render_frame_id,int page_request_id,const GURL & security_origin,content::MediaStreamType stream_type,content::MediaRequestState state)1012 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
1013 int render_process_id,
1014 int render_frame_id,
1015 int page_request_id,
1016 const GURL& security_origin,
1017 content::MediaStreamType stream_type,
1018 content::MediaRequestState state) {
1019 // Track desktop capture sessions. Tracking is necessary to avoid unbalanced
1020 // session counts since not all requests will reach MEDIA_REQUEST_STATE_DONE,
1021 // but they will all reach MEDIA_REQUEST_STATE_CLOSING.
1022 if (stream_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
1023 if (state == content::MEDIA_REQUEST_STATE_DONE) {
1024 DesktopCaptureSession session = { render_process_id, render_frame_id,
1025 page_request_id };
1026 desktop_capture_sessions_.push_back(session);
1027 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
1028 for (DesktopCaptureSessions::iterator it =
1029 desktop_capture_sessions_.begin();
1030 it != desktop_capture_sessions_.end();
1031 ++it) {
1032 if (it->render_process_id == render_process_id &&
1033 it->render_frame_id == render_frame_id &&
1034 it->page_request_id == page_request_id) {
1035 desktop_capture_sessions_.erase(it);
1036 break;
1037 }
1038 }
1039 }
1040 }
1041
1042 // Cancel the request.
1043 if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
1044 bool found = false;
1045 for (RequestsQueues::iterator rqs_it = pending_requests_.begin();
1046 rqs_it != pending_requests_.end(); ++rqs_it) {
1047 RequestsQueue& queue = rqs_it->second;
1048 for (RequestsQueue::iterator it = queue.begin();
1049 it != queue.end(); ++it) {
1050 if (it->request.render_process_id == render_process_id &&
1051 it->request.render_frame_id == render_frame_id &&
1052 it->request.page_request_id == page_request_id) {
1053 queue.erase(it);
1054 found = true;
1055 break;
1056 }
1057 }
1058 if (found)
1059 break;
1060 }
1061 }
1062
1063 #if defined(OS_CHROMEOS)
1064 if (IsOriginForCasting(security_origin) && IsVideoMediaType(stream_type)) {
1065 // Notify ash that casting state has changed.
1066 if (state == content::MEDIA_REQUEST_STATE_DONE) {
1067 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(true);
1068 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
1069 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(false);
1070 }
1071 }
1072 #endif
1073
1074 FOR_EACH_OBSERVER(Observer, observers_,
1075 OnRequestUpdate(render_process_id,
1076 render_frame_id,
1077 stream_type,
1078 state));
1079 }
1080
OnCreatingAudioStreamOnUIThread(int render_process_id,int render_frame_id)1081 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread(
1082 int render_process_id,
1083 int render_frame_id) {
1084 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1085 FOR_EACH_OBSERVER(Observer, observers_,
1086 OnCreatingAudioStream(render_process_id, render_frame_id));
1087 }
1088
IsDesktopCaptureInProgress()1089 bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() {
1090 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1091 return desktop_capture_sessions_.size() > 0;
1092 }
1093
SetTestAudioCaptureDevices(const MediaStreamDevices & devices)1094 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices(
1095 const MediaStreamDevices& devices) {
1096 test_audio_devices_ = devices;
1097 }
1098
SetTestVideoCaptureDevices(const MediaStreamDevices & devices)1099 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices(
1100 const MediaStreamDevices& devices) {
1101 test_video_devices_ = devices;
1102 }
1103