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 "base/command_line.h"
      8 #include "base/logging.h"
      9 #include "base/prefs/pref_service.h"
     10 #include "base/prefs/scoped_user_pref_update.h"
     11 #include "base/sha1.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h"
     16 #include "chrome/browser/media/audio_stream_indicator.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_infobar_delegate.h"
     20 #include "chrome/browser/profiles/profile.h"
     21 #include "chrome/browser/ui/screen_capture_notification_ui.h"
     22 #include "chrome/browser/ui/simple_message_box.h"
     23 #include "chrome/common/chrome_switches.h"
     24 #include "chrome/common/pref_names.h"
     25 #include "components/user_prefs/pref_registry_syncable.h"
     26 #include "content/public/browser/browser_thread.h"
     27 #include "content/public/browser/desktop_media_id.h"
     28 #include "content/public/browser/media_devices_monitor.h"
     29 #include "content/public/browser/notification_service.h"
     30 #include "content/public/browser/notification_source.h"
     31 #include "content/public/browser/notification_types.h"
     32 #include "content/public/browser/web_contents.h"
     33 #include "content/public/common/media_stream_request.h"
     34 #include "extensions/common/constants.h"
     35 #include "extensions/common/extension.h"
     36 #include "grit/generated_resources.h"
     37 #include "media/audio/audio_manager_base.h"
     38 #include "ui/base/l10n/l10n_util.h"
     39 
     40 #if defined(OS_CHROMEOS)
     41 #include "ash/shell.h"
     42 #endif  //  defined(OS_CHROMEOS)
     43 
     44 using content::BrowserThread;
     45 using content::MediaStreamDevices;
     46 
     47 namespace {
     48 
     49 // Finds a device in |devices| that has |device_id|, or NULL if not found.
     50 const content::MediaStreamDevice* FindDeviceWithId(
     51     const content::MediaStreamDevices& devices,
     52     const std::string& device_id) {
     53   content::MediaStreamDevices::const_iterator iter = devices.begin();
     54   for (; iter != devices.end(); ++iter) {
     55     if (iter->id == device_id) {
     56       return &(*iter);
     57     }
     58   }
     59   return NULL;
     60 };
     61 
     62 // This is a short-term solution to grant camera and/or microphone access to
     63 // extensions:
     64 // 1. Virtual keyboard extension.
     65 // 2. Google Voice Search Hotword extension.
     66 // 3. Flutter gesture recognition extension.
     67 // 4. TODO(smus): Airbender experiment 1.
     68 // 5. TODO(smus): Airbender experiment 2.
     69 // Once http://crbug.com/292856 is fixed, remove this whitelist.
     70 bool IsMediaRequestWhitelistedForExtension(
     71     const extensions::Extension* extension) {
     72   return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" ||
     73       extension->id() == "bepbmhgboaologfdajaanbcjmnhjmhfn" ||
     74       extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" ||
     75       extension->id() == "clffjmdilanldobdnedchkdbofoimcgb" ||
     76       extension->id() == "nnckehldicaciogcbchegobnafnjkcne";
     77 }
     78 
     79 // This is a short-term solution to allow testing of the the Screen Capture API
     80 // with Google Hangouts in M27.
     81 // TODO(sergeyu): Remove this whitelist as soon as possible.
     82 bool IsOriginWhitelistedForScreenCapture(const GURL& origin) {
     83 #if defined(OFFICIAL_BUILD)
     84   if (// Google Hangouts.
     85       (origin.SchemeIs("https") &&
     86        EndsWith(origin.spec(), ".talkgadget.google.com/", true)) ||
     87       origin.spec() == "https://talkgadget.google.com/" ||
     88       origin.spec() == "https://plus.google.com/" ||
     89       origin.spec() == "chrome-extension://pkedcjkdefgpdelpbcmbmeomcjbeemfm/" ||
     90       origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" ||
     91       origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" ||
     92       origin.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/") {
     93     return true;
     94   }
     95   // Check against hashed origins.
     96   const std::string origin_hash = base::SHA1HashString(origin.spec());
     97   DCHECK_EQ(origin_hash.length(), base::kSHA1Length);
     98   const std::string hexencoded_origin_hash =
     99       base::HexEncode(origin_hash.data(), origin_hash.length());
    100   return
    101       hexencoded_origin_hash == "3C2705BC432E7C51CA8553FDC5BEE873FF2468EE" ||
    102       hexencoded_origin_hash == "50F02B8A668CAB274527D58356F07C2143080FCC";
    103 #else
    104   return false;
    105 #endif
    106 }
    107 
    108 // Helper to get title of the calling application shown in the screen capture
    109 // notification.
    110 base::string16 GetApplicationTitle(content::WebContents* web_contents,
    111                                    const extensions::Extension* extension) {
    112   // Use extension name as title for extensions and origin for drive-by web.
    113   std::string title;
    114   if (extension) {
    115     title = extension->name();
    116   } else {
    117     title = web_contents->GetURL().GetOrigin().spec();
    118   }
    119   return UTF8ToUTF16(title);
    120 }
    121 
    122 // Helper to get list of media stream devices for desktop capture in |devices|.
    123 // Registers to display notification if |display_notification| is true.
    124 // Returns an instance of MediaStreamUI to be passed to content layer.
    125 scoped_ptr<content::MediaStreamUI> GetDevicesForDesktopCapture(
    126     content::MediaStreamDevices& devices,
    127     content::DesktopMediaID media_id,
    128     bool capture_audio,
    129     bool display_notification,
    130     base::string16 application_title) {
    131   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    132   scoped_ptr<content::MediaStreamUI> ui;
    133 
    134   // Add selected desktop source to the list.
    135   devices.push_back(content::MediaStreamDevice(
    136       content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen"));
    137   if (capture_audio) {
    138     // Use the special loopback device ID for system audio capture.
    139     devices.push_back(content::MediaStreamDevice(
    140         content::MEDIA_LOOPBACK_AUDIO_CAPTURE,
    141         media::AudioManagerBase::kLoopbackInputDeviceId, "System Audio"));
    142   }
    143 
    144   // If required, register to display the notification for stream capture.
    145   if (display_notification) {
    146     ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16(
    147         IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT,
    148         application_title));
    149   }
    150 
    151   return ui.Pass();
    152 }
    153 
    154 }  // namespace
    155 
    156 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest(
    157     const content::MediaStreamRequest& request,
    158     const content::MediaResponseCallback& callback)
    159     : request(request),
    160       callback(callback) {
    161 }
    162 
    163 MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {}
    164 
    165 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() {
    166   return Singleton<MediaCaptureDevicesDispatcher>::get();
    167 }
    168 
    169 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher()
    170     : devices_enumerated_(false),
    171       is_device_enumeration_disabled_(false),
    172       media_stream_capture_indicator_(new MediaStreamCaptureIndicator()),
    173       audio_stream_indicator_(new AudioStreamIndicator()) {
    174   // MediaCaptureDevicesDispatcher is a singleton. It should be created on
    175   // UI thread. Otherwise, it will not receive
    176   // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in
    177   // possible use after free.
    178   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    179   notifications_registrar_.Add(
    180       this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
    181       content::NotificationService::AllSources());
    182 }
    183 
    184 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {}
    185 
    186 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs(
    187     user_prefs::PrefRegistrySyncable* registry) {
    188   registry->RegisterStringPref(
    189       prefs::kDefaultAudioCaptureDevice,
    190       std::string(),
    191       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    192   registry->RegisterStringPref(
    193       prefs::kDefaultVideoCaptureDevice,
    194       std::string(),
    195       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    196 }
    197 
    198 void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) {
    199   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    200   if (!observers_.HasObserver(observer))
    201     observers_.AddObserver(observer);
    202 }
    203 
    204 void MediaCaptureDevicesDispatcher::RemoveObserver(Observer* observer) {
    205   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    206   observers_.RemoveObserver(observer);
    207 }
    208 
    209 const MediaStreamDevices&
    210 MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() {
    211   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    212   if (!is_device_enumeration_disabled_ && !devices_enumerated_) {
    213     content::EnsureMonitorCaptureDevices();
    214     devices_enumerated_ = true;
    215   }
    216   return audio_devices_;
    217 }
    218 
    219 const MediaStreamDevices&
    220 MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() {
    221   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    222   if (!is_device_enumeration_disabled_ && !devices_enumerated_) {
    223     content::EnsureMonitorCaptureDevices();
    224     devices_enumerated_ = true;
    225   }
    226   return video_devices_;
    227 }
    228 
    229 void MediaCaptureDevicesDispatcher::Observe(
    230     int type,
    231     const content::NotificationSource& source,
    232     const content::NotificationDetails& details) {
    233   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    234   if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) {
    235     content::WebContents* web_contents =
    236         content::Source<content::WebContents>(source).ptr();
    237     pending_requests_.erase(web_contents);
    238   }
    239 }
    240 
    241 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest(
    242     content::WebContents* web_contents,
    243     const content::MediaStreamRequest& request,
    244     const content::MediaResponseCallback& callback,
    245     const extensions::Extension* extension) {
    246   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    247 
    248   if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE ||
    249       request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE) {
    250     ProcessDesktopCaptureAccessRequest(
    251         web_contents, request, callback, extension);
    252   } else if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE ||
    253              request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) {
    254     ProcessTabCaptureAccessRequest(
    255         web_contents, request, callback, extension);
    256   } else if (extension && (extension->is_platform_app() ||
    257                            IsMediaRequestWhitelistedForExtension(extension))) {
    258     // For extensions access is approved based on extension permissions.
    259     ProcessMediaAccessRequestFromPlatformAppOrExtension(
    260         web_contents, request, callback, extension);
    261   } else {
    262     ProcessRegularMediaAccessRequest(web_contents, request, callback);
    263   }
    264 }
    265 
    266 void MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest(
    267     content::WebContents* web_contents,
    268     const content::MediaStreamRequest& request,
    269     const content::MediaResponseCallback& callback,
    270     const extensions::Extension* extension) {
    271   content::MediaStreamDevices devices;
    272   scoped_ptr<content::MediaStreamUI> ui;
    273 
    274   if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
    275     callback.Run(devices, ui.Pass());
    276     return;
    277   }
    278 
    279   // If the device id wasn't specified then this is a screen capture request
    280   // (i.e. chooseDesktopMedia() API wasn't used to generate device id).
    281   if (request.requested_video_device_id.empty()) {
    282     ProcessScreenCaptureAccessRequest(
    283         web_contents, request, callback, extension);
    284     return;
    285   }
    286 
    287   // Resolve DesktopMediaID for the specified device id.
    288   content::DesktopMediaID media_id =
    289       GetDesktopStreamsRegistry()->RequestMediaForStreamId(
    290           request.requested_video_device_id, request.render_process_id,
    291           request.render_view_id, request.security_origin);
    292 
    293   // Received invalid device id.
    294   if (media_id.type == content::DesktopMediaID::TYPE_NONE) {
    295     callback.Run(devices, ui.Pass());
    296     return;
    297   }
    298 
    299   bool loopback_audio_supported = false;
    300 #if defined(USE_CRAS) || defined(OS_WIN)
    301   // Currently loopback audio capture is supported only on Windows and ChromeOS.
    302   loopback_audio_supported = true;
    303 #endif
    304 
    305   // Audio is only supported for screen capture streams.
    306   bool capture_audio =
    307       (media_id.type == content::DesktopMediaID::TYPE_SCREEN &&
    308        request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE &&
    309        loopback_audio_supported);
    310 
    311   ui = GetDevicesForDesktopCapture(
    312       devices, media_id, capture_audio, true,
    313       GetApplicationTitle(web_contents, extension));
    314 
    315   callback.Run(devices, ui.Pass());
    316 }
    317 
    318 void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest(
    319     content::WebContents* web_contents,
    320     const content::MediaStreamRequest& request,
    321     const content::MediaResponseCallback& callback,
    322     const extensions::Extension* extension) {
    323   content::MediaStreamDevices devices;
    324   scoped_ptr<content::MediaStreamUI> ui;
    325 
    326   DCHECK_EQ(request.video_type, content::MEDIA_DESKTOP_VIDEO_CAPTURE);
    327 
    328   bool loopback_audio_supported = false;
    329 #if defined(USE_CRAS) || defined(OS_WIN)
    330   // Currently loopback audio capture is supported only on Windows and ChromeOS.
    331   loopback_audio_supported = true;
    332 #endif
    333 
    334   const bool component_extension =
    335       extension && extension->location() == extensions::Manifest::COMPONENT;
    336 
    337   const bool screen_capture_enabled =
    338       CommandLine::ForCurrentProcess()->HasSwitch(
    339           switches::kEnableUserMediaScreenCapturing) ||
    340       IsOriginWhitelistedForScreenCapture(request.security_origin);
    341 
    342   const bool origin_is_secure =
    343       request.security_origin.SchemeIsSecure() ||
    344       request.security_origin.SchemeIs(extensions::kExtensionScheme) ||
    345       CommandLine::ForCurrentProcess()->HasSwitch(
    346           switches::kAllowHttpScreenCapture);
    347 
    348   // Approve request only when the following conditions are met:
    349   //  1. Screen capturing is enabled via command line switch or white-listed for
    350   //     the given origin.
    351   //  2. Request comes from a page with a secure origin or from an extension.
    352   if (screen_capture_enabled && origin_is_secure) {
    353     // Get title of the calling application prior to showing the message box.
    354     // chrome::ShowMessageBox() starts a nested message loop which may allow
    355     // |web_contents| to be destroyed on the UI thread before the message box
    356     // is closed. See http://crbug.com/326690.
    357     base::string16 application_title =
    358         GetApplicationTitle(web_contents, extension);
    359     web_contents = NULL;
    360 
    361     // For component extensions, bypass message box.
    362     bool user_approved = false;
    363     if (!component_extension) {
    364       base::string16 application_name = UTF8ToUTF16(
    365           extension ? extension->name() : request.security_origin.spec());
    366       base::string16 confirmation_text = l10n_util::GetStringFUTF16(
    367           request.audio_type == content::MEDIA_NO_SERVICE ?
    368               IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT :
    369               IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT,
    370           application_name);
    371       chrome::MessageBoxResult result = chrome::ShowMessageBox(
    372           NULL,
    373           l10n_util::GetStringFUTF16(
    374               IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name),
    375           confirmation_text,
    376           chrome::MESSAGE_BOX_TYPE_QUESTION);
    377       user_approved = (result == chrome::MESSAGE_BOX_RESULT_YES);
    378     }
    379 
    380     if (user_approved || component_extension) {
    381       content::DesktopMediaID screen_id;
    382 #if defined(OS_CHROMEOS)
    383       screen_id = content::DesktopMediaID::RegisterAuraWindow(
    384           ash::Shell::GetInstance()->GetPrimaryRootWindow());
    385 #else  // defined(OS_CHROMEOS)
    386       screen_id =
    387           content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, 0);
    388 #endif  // !defined(OS_CHROMEOS)
    389 
    390       bool capture_audio =
    391           (request.audio_type == content::MEDIA_LOOPBACK_AUDIO_CAPTURE &&
    392            loopback_audio_supported);
    393 
    394       // Unless we're being invoked from a component extension, register to
    395       // display the notification for stream capture.
    396       bool display_notification = !component_extension;
    397 
    398       ui = GetDevicesForDesktopCapture(devices, screen_id,  capture_audio,
    399                                        display_notification, application_title);
    400     }
    401   }
    402 
    403   callback.Run(devices, ui.Pass());
    404 }
    405 
    406 void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest(
    407     content::WebContents* web_contents,
    408     const content::MediaStreamRequest& request,
    409     const content::MediaResponseCallback& callback,
    410     const extensions::Extension* extension) {
    411   content::MediaStreamDevices devices;
    412   scoped_ptr<content::MediaStreamUI> ui;
    413 
    414 #if defined(OS_ANDROID)
    415   // Tab capture is not supported on Android.
    416   callback.Run(devices, ui.Pass());
    417 #else  // defined(OS_ANDROID)
    418   Profile* profile =
    419       Profile::FromBrowserContext(web_contents->GetBrowserContext());
    420   extensions::TabCaptureRegistry* tab_capture_registry =
    421       extensions::TabCaptureRegistry::Get(profile);
    422   if (!tab_capture_registry) {
    423     NOTREACHED();
    424     callback.Run(devices, ui.Pass());
    425     return;
    426   }
    427   bool tab_capture_allowed =
    428       tab_capture_registry->VerifyRequest(request.render_process_id,
    429                                           request.render_view_id);
    430 
    431   if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE &&
    432       tab_capture_allowed &&
    433       extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
    434     devices.push_back(content::MediaStreamDevice(
    435         content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string()));
    436   }
    437 
    438   if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE &&
    439       tab_capture_allowed &&
    440       extension->HasAPIPermission(extensions::APIPermission::kTabCapture)) {
    441     devices.push_back(content::MediaStreamDevice(
    442         content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string()));
    443   }
    444 
    445   if (!devices.empty()) {
    446     ui = media_stream_capture_indicator_->RegisterMediaStream(
    447         web_contents, devices);
    448   }
    449   callback.Run(devices, ui.Pass());
    450 #endif  // !defined(OS_ANDROID)
    451 }
    452 
    453 void MediaCaptureDevicesDispatcher::
    454     ProcessMediaAccessRequestFromPlatformAppOrExtension(
    455         content::WebContents* web_contents,
    456         const content::MediaStreamRequest& request,
    457         const content::MediaResponseCallback& callback,
    458         const extensions::Extension* extension) {
    459   content::MediaStreamDevices devices;
    460   Profile* profile =
    461       Profile::FromBrowserContext(web_contents->GetBrowserContext());
    462 
    463   if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
    464       extension->HasAPIPermission(extensions::APIPermission::kAudioCapture)) {
    465     GetDefaultDevicesForProfile(profile, true, false, &devices);
    466   }
    467 
    468   if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
    469       extension->HasAPIPermission(extensions::APIPermission::kVideoCapture)) {
    470     GetDefaultDevicesForProfile(profile, false, true, &devices);
    471   }
    472 
    473   scoped_ptr<content::MediaStreamUI> ui;
    474   if (!devices.empty()) {
    475     ui = media_stream_capture_indicator_->RegisterMediaStream(
    476         web_contents, devices);
    477   }
    478   callback.Run(devices, ui.Pass());
    479 }
    480 
    481 void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest(
    482     content::WebContents* web_contents,
    483     const content::MediaStreamRequest& request,
    484     const content::MediaResponseCallback& callback) {
    485   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    486 
    487   RequestsQueue& queue = pending_requests_[web_contents];
    488   queue.push_back(PendingAccessRequest(request, callback));
    489 
    490   // If this is the only request then show the infobar.
    491   if (queue.size() == 1)
    492     ProcessQueuedAccessRequest(web_contents);
    493 }
    494 
    495 void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest(
    496     content::WebContents* web_contents) {
    497   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    498 
    499   std::map<content::WebContents*, RequestsQueue>::iterator it =
    500       pending_requests_.find(web_contents);
    501 
    502   if (it == pending_requests_.end() || it->second.empty()) {
    503     // Don't do anything if the tab was was closed.
    504     return;
    505   }
    506 
    507   DCHECK(!it->second.empty());
    508 
    509   MediaStreamInfoBarDelegate::Create(
    510       web_contents, it->second.front().request,
    511       base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse,
    512                  base::Unretained(this), web_contents));
    513 }
    514 
    515 void MediaCaptureDevicesDispatcher::OnAccessRequestResponse(
    516     content::WebContents* web_contents,
    517     const content::MediaStreamDevices& devices,
    518     scoped_ptr<content::MediaStreamUI> ui) {
    519   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    520 
    521   std::map<content::WebContents*, RequestsQueue>::iterator it =
    522       pending_requests_.find(web_contents);
    523   if (it == pending_requests_.end()) {
    524     // WebContents has been destroyed. Don't need to do anything.
    525     return;
    526   }
    527 
    528   RequestsQueue& queue(it->second);
    529   if (queue.empty())
    530     return;
    531 
    532   content::MediaResponseCallback callback = queue.front().callback;
    533   queue.pop_front();
    534 
    535   if (!queue.empty()) {
    536     // Post a task to process next queued request. It has to be done
    537     // asynchronously to make sure that calling infobar is not destroyed until
    538     // after this function returns.
    539     BrowserThread::PostTask(
    540         BrowserThread::UI, FROM_HERE,
    541         base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest,
    542                    base::Unretained(this), web_contents));
    543   }
    544 
    545   callback.Run(devices, ui.Pass());
    546 }
    547 
    548 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile(
    549     Profile* profile,
    550     bool audio,
    551     bool video,
    552     content::MediaStreamDevices* devices) {
    553   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    554   DCHECK(audio || video);
    555 
    556   PrefService* prefs = profile->GetPrefs();
    557   std::string default_device;
    558   if (audio) {
    559     default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice);
    560     const content::MediaStreamDevice* device =
    561         GetRequestedAudioDevice(default_device);
    562     if (!device)
    563       device = GetFirstAvailableAudioDevice();
    564     if (device)
    565       devices->push_back(*device);
    566   }
    567 
    568   if (video) {
    569     default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice);
    570     const content::MediaStreamDevice* device =
    571         GetRequestedVideoDevice(default_device);
    572     if (!device)
    573       device = GetFirstAvailableVideoDevice();
    574     if (device)
    575       devices->push_back(*device);
    576   }
    577 }
    578 
    579 const content::MediaStreamDevice*
    580 MediaCaptureDevicesDispatcher::GetRequestedAudioDevice(
    581     const std::string& requested_audio_device_id) {
    582   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    583   const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
    584   const content::MediaStreamDevice* const device =
    585       FindDeviceWithId(audio_devices, requested_audio_device_id);
    586   return device;
    587 }
    588 
    589 const content::MediaStreamDevice*
    590 MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() {
    591   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    592   const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices();
    593   if (audio_devices.empty())
    594     return NULL;
    595   return &(*audio_devices.begin());
    596 }
    597 
    598 const content::MediaStreamDevice*
    599 MediaCaptureDevicesDispatcher::GetRequestedVideoDevice(
    600     const std::string& requested_video_device_id) {
    601   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    602   const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
    603   const content::MediaStreamDevice* const device =
    604       FindDeviceWithId(video_devices, requested_video_device_id);
    605   return device;
    606 }
    607 
    608 const content::MediaStreamDevice*
    609 MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() {
    610   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    611   const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices();
    612   if (video_devices.empty())
    613     return NULL;
    614   return &(*video_devices.begin());
    615 }
    616 
    617 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() {
    618   is_device_enumeration_disabled_ = true;
    619 }
    620 
    621 scoped_refptr<MediaStreamCaptureIndicator>
    622 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() {
    623   return media_stream_capture_indicator_;
    624 }
    625 
    626 scoped_refptr<AudioStreamIndicator>
    627 MediaCaptureDevicesDispatcher::GetAudioStreamIndicator() {
    628   return audio_stream_indicator_;
    629 }
    630 
    631 DesktopStreamsRegistry*
    632 MediaCaptureDevicesDispatcher::GetDesktopStreamsRegistry() {
    633   if (!desktop_streams_registry_)
    634     desktop_streams_registry_.reset(new DesktopStreamsRegistry());
    635   return desktop_streams_registry_.get();
    636 }
    637 
    638 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged(
    639     const content::MediaStreamDevices& devices) {
    640   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    641   BrowserThread::PostTask(
    642       BrowserThread::UI, FROM_HERE,
    643       base::Bind(&MediaCaptureDevicesDispatcher::UpdateAudioDevicesOnUIThread,
    644                  base::Unretained(this), devices));
    645 }
    646 
    647 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged(
    648     const content::MediaStreamDevices& devices) {
    649   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    650   BrowserThread::PostTask(
    651       BrowserThread::UI, FROM_HERE,
    652       base::Bind(&MediaCaptureDevicesDispatcher::UpdateVideoDevicesOnUIThread,
    653                  base::Unretained(this), devices));
    654 }
    655 
    656 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged(
    657     int render_process_id,
    658     int render_view_id,
    659     int page_request_id,
    660     const content::MediaStreamDevice& device,
    661     content::MediaRequestState state) {
    662   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    663   BrowserThread::PostTask(
    664       BrowserThread::UI, FROM_HERE,
    665       base::Bind(
    666           &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread,
    667           base::Unretained(this), render_process_id, render_view_id,
    668           page_request_id, device, state));
    669 }
    670 
    671 void MediaCaptureDevicesDispatcher::OnAudioStreamPlayingChanged(
    672     int render_process_id, int render_view_id, int stream_id,
    673     bool is_playing, float power_dbfs, bool clipped) {
    674   audio_stream_indicator_->UpdateWebContentsStatus(
    675       render_process_id, render_view_id, stream_id,
    676       is_playing, power_dbfs, clipped);
    677 }
    678 
    679 void MediaCaptureDevicesDispatcher::OnCreatingAudioStream(
    680     int render_process_id,
    681     int render_view_id) {
    682   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    683   BrowserThread::PostTask(
    684       BrowserThread::UI, FROM_HERE,
    685       base::Bind(
    686           &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread,
    687           base::Unretained(this), render_process_id, render_view_id));
    688 }
    689 
    690 void MediaCaptureDevicesDispatcher::UpdateAudioDevicesOnUIThread(
    691     const content::MediaStreamDevices& devices) {
    692   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    693   devices_enumerated_ = true;
    694   audio_devices_ = devices;
    695   FOR_EACH_OBSERVER(Observer, observers_,
    696                     OnUpdateAudioDevices(audio_devices_));
    697 }
    698 
    699 void MediaCaptureDevicesDispatcher::UpdateVideoDevicesOnUIThread(
    700     const content::MediaStreamDevices& devices) {
    701   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    702   devices_enumerated_ = true;
    703   video_devices_ = devices;
    704   FOR_EACH_OBSERVER(Observer, observers_,
    705                     OnUpdateVideoDevices(video_devices_));
    706 }
    707 
    708 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread(
    709     int render_process_id,
    710     int render_view_id,
    711     int page_request_id,
    712     const content::MediaStreamDevice& device,
    713     content::MediaRequestState state) {
    714   // Track desktop capture sessions.  Tracking is necessary to avoid unbalanced
    715   // session counts since not all requests will reach MEDIA_REQUEST_STATE_DONE,
    716   // but they will all reach MEDIA_REQUEST_STATE_CLOSING.
    717   if (device.type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) {
    718     if (state == content::MEDIA_REQUEST_STATE_DONE) {
    719       DesktopCaptureSession session = { render_process_id, render_view_id,
    720                                         page_request_id };
    721       desktop_capture_sessions_.push_back(session);
    722     } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
    723       for (DesktopCaptureSessions::iterator it =
    724                desktop_capture_sessions_.begin();
    725            it != desktop_capture_sessions_.end();
    726            ++it) {
    727         if (it->render_process_id == render_process_id &&
    728             it->render_view_id == render_view_id &&
    729             it->page_request_id == page_request_id) {
    730           desktop_capture_sessions_.erase(it);
    731           break;
    732         }
    733       }
    734     }
    735   }
    736 
    737   // Cancel the request.
    738   if (state == content::MEDIA_REQUEST_STATE_CLOSING) {
    739     bool found = false;
    740     for (RequestsQueues::iterator rqs_it = pending_requests_.begin();
    741          rqs_it != pending_requests_.end(); ++rqs_it) {
    742       RequestsQueue& queue = rqs_it->second;
    743       for (RequestsQueue::iterator it = queue.begin();
    744            it != queue.end(); ++it) {
    745         if (it->request.render_process_id == render_process_id &&
    746             it->request.render_view_id == render_view_id &&
    747             it->request.page_request_id == page_request_id) {
    748           queue.erase(it);
    749           found = true;
    750           break;
    751         }
    752       }
    753       if (found)
    754         break;
    755     }
    756   }
    757 
    758   FOR_EACH_OBSERVER(Observer, observers_,
    759                     OnRequestUpdate(render_process_id,
    760                                     render_view_id,
    761                                     device,
    762                                     state));
    763 }
    764 
    765 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread(
    766     int render_process_id,
    767     int render_view_id) {
    768   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    769   FOR_EACH_OBSERVER(Observer, observers_,
    770                     OnCreatingAudioStream(render_process_id, render_view_id));
    771 }
    772 
    773 bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() {
    774   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    775   return desktop_capture_sessions_.size() > 0;
    776 }
    777