Upstream version 11.39.250.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 "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.
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.
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.
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
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 }
135
136 // Helper to get title of the calling application shown in the screen capture
137 // notification.
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.
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|.
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
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
235 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
236     const content::MediaStreamRequest& request,
237     const content::MediaResponseCallback& callback)
238     : request(request),
239       callback(callback) {
240 }
241
242 MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {}
243
244 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
245   return Singleton<MediaCaptureDevicesDispatcher>::get();
246 }
247
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
276 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
277
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
290 void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) {
291   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
292   if (!observers_.HasObserver(observer))
293     observers_.AddObserver(observer);
294 }
295
296 void MediaCaptureDevicesDispatcher::RemoveObserver(Observer* observer) {
297   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
298   observers_.RemoveObserver(observer);
299 }
300
301 const MediaStreamDevices&
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&
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
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
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
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
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)
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
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
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
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::
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
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
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
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
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*
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*
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*
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*
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
939 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
940   is_device_enumeration_disabled_ = true;
941 }
942
943 scoped_refptr<MediaStreamCaptureIndicator>
944 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
945   return media_stream_capture_indicator_;
946 }
947
948 DesktopStreamsRegistry*
949 MediaCaptureDevicesDispatcher::GetDesktopStreamsRegistry() {
950   if (!desktop_streams_registry_)
951     desktop_streams_registry_.reset(new DesktopStreamsRegistry());
952   return desktop_streams_registry_.get();
953 }
954
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
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
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
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
1000 void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() {
1001   MediaStreamDevices devices = GetAudioCaptureDevices();
1002   FOR_EACH_OBSERVER(Observer, observers_,
1003                     OnUpdateAudioDevices(devices));
1004 }
1005
1006 void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() {
1007   MediaStreamDevices devices = GetVideoCaptureDevices();
1008   FOR_EACH_OBSERVER(Observer, observers_,
1009                     OnUpdateVideoDevices(devices));
1010 }
1011
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
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
1089 bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() {
1090   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1091   return desktop_capture_sessions_.size() > 0;
1092 }
1093
1094 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices(
1095     const MediaStreamDevices& devices) {
1096   test_audio_devices_ = devices;
1097 }
1098
1099 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices(
1100     const MediaStreamDevices& devices) {
1101   test_video_devices_ = devices;
1102 }