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