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