| Index: content/browser/renderer_host/media/media_devices_dispatcher_host.cc
 | 
| diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
 | 
| index 948b50255ca89e991a394680d752da8a2fd004f5..cea286fb55e71a06468c55f4d1fccdd8dd0aca44 100644
 | 
| --- a/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
 | 
| +++ b/content/browser/renderer_host/media/media_devices_dispatcher_host.cc
 | 
| @@ -4,6 +4,8 @@
 | 
|  
 | 
|  #include "content/browser/renderer_host/media/media_devices_dispatcher_host.h"
 | 
|  
 | 
| +#include <stddef.h>
 | 
| +
 | 
|  #include <utility>
 | 
|  #include <vector>
 | 
|  
 | 
| @@ -40,29 +42,49 @@ MediaDeviceInfo TranslateDeviceInfo(bool has_permission,
 | 
|                                      device_info.group_id));
 | 
|  }
 | 
|  
 | 
| +MediaDeviceInfoArray TranslateMediaDeviceInfoArray(
 | 
| +    bool has_permission,
 | 
| +    const std::string& device_id_salt,
 | 
| +    const std::string& group_id_salt,
 | 
| +    const url::Origin& security_origin,
 | 
| +    const MediaDeviceInfoArray& input) {
 | 
| +  MediaDeviceInfoArray result;
 | 
| +  for (const auto& device_info : input) {
 | 
| +    result.push_back(TranslateDeviceInfo(has_permission, device_id_salt,
 | 
| +                                         group_id_salt, security_origin,
 | 
| +                                         device_info));
 | 
| +  }
 | 
| +  return result;
 | 
| +}
 | 
| +
 | 
|  }  // namespace
 | 
|  
 | 
| +struct MediaDevicesDispatcherHost::SubscriptionInfo {
 | 
| +  uint32_t subscription_id;
 | 
| +  url::Origin security_origin;
 | 
| +};
 | 
| +
 | 
|  // static
 | 
|  void MediaDevicesDispatcherHost::Create(
 | 
|      int render_process_id,
 | 
| -    int routing_id,
 | 
| +    int render_frame_id,
 | 
|      const std::string& device_id_salt,
 | 
|      MediaStreamManager* media_stream_manager,
 | 
|      ::mojom::MediaDevicesDispatcherHostRequest request) {
 | 
|    DCHECK_CURRENTLY_ON(BrowserThread::IO);
 | 
| -  mojo::MakeStrongBinding(
 | 
| -      base::MakeUnique<MediaDevicesDispatcherHost>(
 | 
| -          render_process_id, routing_id, device_id_salt, media_stream_manager),
 | 
| -      std::move(request));
 | 
| +  mojo::MakeStrongBinding(base::MakeUnique<MediaDevicesDispatcherHost>(
 | 
| +                              render_process_id, render_frame_id,
 | 
| +                              device_id_salt, media_stream_manager),
 | 
| +                          std::move(request));
 | 
|  }
 | 
|  
 | 
|  MediaDevicesDispatcherHost::MediaDevicesDispatcherHost(
 | 
|      int render_process_id,
 | 
| -    int routing_id,
 | 
| +    int render_frame_id,
 | 
|      const std::string& device_id_salt,
 | 
|      MediaStreamManager* media_stream_manager)
 | 
|      : render_process_id_(render_process_id),
 | 
| -      routing_id_(routing_id),
 | 
| +      render_frame_id_(render_frame_id),
 | 
|        device_id_salt_(device_id_salt),
 | 
|        group_id_salt_(ResourceContext::CreateRandomMediaDeviceIDSalt()),
 | 
|        media_stream_manager_(media_stream_manager),
 | 
| @@ -72,6 +94,13 @@ MediaDevicesDispatcherHost::MediaDevicesDispatcherHost(
 | 
|  
 | 
|  MediaDevicesDispatcherHost::~MediaDevicesDispatcherHost() {
 | 
|    DCHECK_CURRENTLY_ON(BrowserThread::IO);
 | 
| +  for (size_t i = 0; i < NUM_MEDIA_DEVICE_TYPES; ++i) {
 | 
| +    if (!device_change_subscriptions_[i].empty()) {
 | 
| +      media_stream_manager_->media_devices_manager()
 | 
| +          ->UnsubscribeDeviceChangeNotifications(
 | 
| +              static_cast<MediaDeviceType>(i), this);
 | 
| +    }
 | 
| +  }
 | 
|  }
 | 
|  
 | 
|  void MediaDevicesDispatcherHost::EnumerateDevices(
 | 
| @@ -101,18 +130,129 @@ void MediaDevicesDispatcherHost::EnumerateDevices(
 | 
|    devices_to_enumerate[MEDIA_DEVICE_TYPE_AUDIO_OUTPUT] = request_audio_output;
 | 
|  
 | 
|    permission_checker_.CheckPermissions(
 | 
| -      devices_to_enumerate, render_process_id_, routing_id_, security_origin,
 | 
| +      devices_to_enumerate, render_process_id_, render_frame_id_,
 | 
| +      security_origin,
 | 
|        base::Bind(&MediaDevicesDispatcherHost::DoEnumerateDevices,
 | 
|                   weak_factory_.GetWeakPtr(), devices_to_enumerate,
 | 
|                   security_origin, client_callback));
 | 
|  }
 | 
|  
 | 
| +void MediaDevicesDispatcherHost::SubscribeDeviceChangeNotifications(
 | 
| +    MediaDeviceType type,
 | 
| +    uint32_t subscription_id,
 | 
| +    const url::Origin& security_origin) {
 | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 | 
| +  DCHECK(IsValidMediaDeviceType(type));
 | 
| +  if (!MediaStreamManager::IsOriginAllowed(render_process_id_,
 | 
| +                                           security_origin)) {
 | 
| +    bad_message::ReceivedBadMessage(render_process_id_,
 | 
| +                                    bad_message::MDDH_UNAUTHORIZED_ORIGIN);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  auto it = std::find_if(device_change_subscriptions_[type].begin(),
 | 
| +                         device_change_subscriptions_[type].end(),
 | 
| +                         [subscription_id](const SubscriptionInfo& info) {
 | 
| +                           return info.subscription_id == subscription_id;
 | 
| +                         });
 | 
| +
 | 
| +  if (it != device_change_subscriptions_[type].end()) {
 | 
| +    bad_message::ReceivedBadMessage(
 | 
| +        render_process_id_, bad_message::MDDH_INVALID_SUBSCRIPTION_REQUEST);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  if (device_change_subscriptions_[type].empty()) {
 | 
| +    media_stream_manager_->media_devices_manager()
 | 
| +        ->SubscribeDeviceChangeNotifications(type, this);
 | 
| +  }
 | 
| +
 | 
| +  device_change_subscriptions_[type].push_back(
 | 
| +      SubscriptionInfo{subscription_id, security_origin});
 | 
| +}
 | 
| +
 | 
| +void MediaDevicesDispatcherHost::UnsubscribeDeviceChangeNotifications(
 | 
| +    MediaDeviceType type,
 | 
| +    uint32_t subscription_id) {
 | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 | 
| +  DCHECK(IsValidMediaDeviceType(type));
 | 
| +  auto it = std::find_if(device_change_subscriptions_[type].begin(),
 | 
| +                         device_change_subscriptions_[type].end(),
 | 
| +                         [subscription_id](const SubscriptionInfo& info) {
 | 
| +                           return info.subscription_id == subscription_id;
 | 
| +                         });
 | 
| +
 | 
| +  if (it == device_change_subscriptions_[type].end()) {
 | 
| +    bad_message::ReceivedBadMessage(
 | 
| +        render_process_id_, bad_message::MDDH_INVALID_UNSUBSCRIPTION_REQUEST);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  device_change_subscriptions_[type].erase(it);
 | 
| +  if (device_change_subscriptions_[type].empty()) {
 | 
| +    media_stream_manager_->media_devices_manager()
 | 
| +        ->UnsubscribeDeviceChangeNotifications(type, this);
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void MediaDevicesDispatcherHost::OnDevicesChanged(
 | 
| +    MediaDeviceType type,
 | 
| +    const MediaDeviceInfoArray& device_infos) {
 | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::IO);
 | 
| +  DCHECK(IsValidMediaDeviceType(type));
 | 
| +  BrowserThread::PostTask(
 | 
| +      BrowserThread::UI, FROM_HERE,
 | 
| +      base::Bind(&MediaDevicesDispatcherHost::NotifyDeviceChangeOnUIThread,
 | 
| +                 weak_factory_.GetWeakPtr(), device_change_subscriptions_[type],
 | 
| +                 type, device_infos));
 | 
| +}
 | 
| +
 | 
| +void MediaDevicesDispatcherHost::NotifyDeviceChangeOnUIThread(
 | 
| +    const std::vector<SubscriptionInfo>& subscriptions,
 | 
| +    MediaDeviceType type,
 | 
| +    const MediaDeviceInfoArray& device_infos) {
 | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 | 
| +  DCHECK(IsValidMediaDeviceType(type));
 | 
| +
 | 
| +  ::mojom::MediaDevicesListenerPtr media_devices_listener;
 | 
| +  if (device_change_listener_) {
 | 
| +    media_devices_listener = std::move(device_change_listener_);
 | 
| +  } else {
 | 
| +    RenderFrameHost* render_frame_host =
 | 
| +        RenderFrameHost::FromID(render_process_id_, render_frame_id_);
 | 
| +    if (!render_frame_host)
 | 
| +      return;
 | 
| +
 | 
| +    render_frame_host->GetRemoteInterfaces()->GetInterface(
 | 
| +        mojo::GetProxy(&media_devices_listener));
 | 
| +    if (!media_devices_listener)
 | 
| +      return;
 | 
| +  }
 | 
| +
 | 
| +  for (const auto& subscription : subscriptions) {
 | 
| +    bool has_permission = permission_checker_.CheckPermissionOnUIThread(
 | 
| +        type, render_process_id_, render_frame_id_,
 | 
| +        subscription.security_origin);
 | 
| +    media_devices_listener->OnDevicesChanged(
 | 
| +        type, subscription.subscription_id,
 | 
| +        TranslateMediaDeviceInfoArray(
 | 
| +            has_permission, device_id_salt_, group_id_salt_,
 | 
| +            subscription.security_origin, device_infos));
 | 
| +  }
 | 
| +}
 | 
| +
 | 
|  void MediaDevicesDispatcherHost::SetPermissionChecker(
 | 
|      const MediaDevicesPermissionChecker& permission_checker) {
 | 
|    DCHECK_CURRENTLY_ON(BrowserThread::IO);
 | 
|    permission_checker_ = permission_checker;
 | 
|  }
 | 
|  
 | 
| +void MediaDevicesDispatcherHost::SetDeviceChangeListenerForTesting(
 | 
| +    ::mojom::MediaDevicesListenerPtr listener) {
 | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 | 
| +  device_change_listener_ = std::move(listener);
 | 
| +}
 | 
| +
 | 
|  void MediaDevicesDispatcherHost::DoEnumerateDevices(
 | 
|      const MediaDevicesManager::BoolDeviceTypes& requested_types,
 | 
|      const url::Origin& security_origin,
 | 
| 
 |