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