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
66using content::BrowserThread;
67using content::MediaCaptureDevices;
68using content::MediaStreamDevices;
69
70namespace {
71
72// A finch experiment to enable the permission bubble for media requests only.
73bool 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.
83const 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.
104bool 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
114bool 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.
121bool 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.
138base::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.
156scoped_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|.
195gfx::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
215const 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
235MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
236    const content::MediaStreamRequest& request,
237    const content::MediaResponseCallback& callback)
238    : request(request),
239      callback(callback) {
240}
241
242MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {}
243
244MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
245  return Singleton<MediaCaptureDevicesDispatcher>::get();
246}
247
248MediaCaptureDevicesDispatcher::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
276MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
277
278void 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
290void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) {
291  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
292  if (!observers_.HasObserver(observer))
293    observers_.AddObserver(observer);
294}
295
296void MediaCaptureDevicesDispatcher::RemoveObserver(Observer* observer) {
297  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
298  observers_.RemoveObserver(observer);
299}
300
301const MediaStreamDevices&
302MediaCaptureDevicesDispatcher::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
310const MediaStreamDevices&
311MediaCaptureDevicesDispatcher::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
319void 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
331void 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
356bool 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
406bool 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)
448bool 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
469void 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
538void 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
649void 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
700void 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
784void 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
798void 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
836void 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
870void 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
901const content::MediaStreamDevice*
902MediaCaptureDevicesDispatcher::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
911const content::MediaStreamDevice*
912MediaCaptureDevicesDispatcher::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
920const content::MediaStreamDevice*
921MediaCaptureDevicesDispatcher::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
930const content::MediaStreamDevice*
931MediaCaptureDevicesDispatcher::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
939void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
940  is_device_enumeration_disabled_ = true;
941}
942
943scoped_refptr<MediaStreamCaptureIndicator>
944MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
945  return media_stream_capture_indicator_;
946}
947
948DesktopStreamsRegistry*
949MediaCaptureDevicesDispatcher::GetDesktopStreamsRegistry() {
950  if (!desktop_streams_registry_)
951    desktop_streams_registry_.reset(new DesktopStreamsRegistry());
952  return desktop_streams_registry_.get();
953}
954
955void 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
964void 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
973void 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
989void 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
1000void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() {
1001  MediaStreamDevices devices = GetAudioCaptureDevices();
1002  FOR_EACH_OBSERVER(Observer, observers_,
1003                    OnUpdateAudioDevices(devices));
1004}
1005
1006void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() {
1007  MediaStreamDevices devices = GetVideoCaptureDevices();
1008  FOR_EACH_OBSERVER(Observer, observers_,
1009                    OnUpdateVideoDevices(devices));
1010}
1011
1012void 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
1081void 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
1089bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() {
1090  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1091  return desktop_capture_sessions_.size() > 0;
1092}
1093
1094void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices(
1095    const MediaStreamDevices& devices) {
1096  test_audio_devices_ = devices;
1097}
1098
1099void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices(
1100    const MediaStreamDevices& devices) {
1101  test_video_devices_ = devices;
1102}
1103