1 // Copyright 2013 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 "content/browser/renderer_host/media/media_stream_ui_proxy.h" 6 7 #include "base/command_line.h" 8 #include "content/browser/frame_host/render_frame_host_delegate.h" 9 #include "content/browser/frame_host/render_frame_host_impl.h" 10 #include "content/public/browser/browser_thread.h" 11 #include "content/public/common/content_switches.h" 12 #include "media/video/capture/fake_video_capture_device.h" 13 14 namespace content { 15 16 class MediaStreamUIProxy::Core { 17 public: 18 explicit Core(const base::WeakPtr<MediaStreamUIProxy>& proxy, 19 RenderFrameHostDelegate* test_render_delegate); 20 ~Core(); 21 22 void RequestAccess(const MediaStreamRequest& request); 23 bool CheckAccess(const GURL& security_origin, 24 MediaStreamType type, 25 int process_id, 26 int frame_id); 27 void OnStarted(gfx::NativeViewId* window_id); 28 29 private: 30 void ProcessAccessRequestResponse(const MediaStreamDevices& devices, 31 content::MediaStreamRequestResult result, 32 scoped_ptr<MediaStreamUI> stream_ui); 33 void ProcessStopRequestFromUI(); 34 RenderFrameHostDelegate* GetRenderFrameHostDelegate(int render_process_id, 35 int render_frame_id); 36 37 base::WeakPtr<MediaStreamUIProxy> proxy_; 38 scoped_ptr<MediaStreamUI> ui_; 39 40 RenderFrameHostDelegate* const test_render_delegate_; 41 42 // WeakPtr<> is used to RequestMediaAccessPermission() because there is no way 43 // cancel media requests. 44 base::WeakPtrFactory<Core> weak_factory_; 45 46 DISALLOW_COPY_AND_ASSIGN(Core); 47 }; 48 49 MediaStreamUIProxy::Core::Core(const base::WeakPtr<MediaStreamUIProxy>& proxy, 50 RenderFrameHostDelegate* test_render_delegate) 51 : proxy_(proxy), 52 test_render_delegate_(test_render_delegate), 53 weak_factory_(this) { 54 } 55 56 MediaStreamUIProxy::Core::~Core() { 57 DCHECK_CURRENTLY_ON(BrowserThread::UI); 58 } 59 60 void MediaStreamUIProxy::Core::RequestAccess( 61 const MediaStreamRequest& request) { 62 DCHECK_CURRENTLY_ON(BrowserThread::UI); 63 64 RenderFrameHostDelegate* render_delegate = GetRenderFrameHostDelegate( 65 request.render_process_id, request.render_frame_id); 66 67 // Tab may have gone away, or has no delegate from which to request access. 68 if (!render_delegate) { 69 ProcessAccessRequestResponse(MediaStreamDevices(), 70 MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN, 71 scoped_ptr<MediaStreamUI>()); 72 return; 73 } 74 75 render_delegate->RequestMediaAccessPermission( 76 request, base::Bind(&Core::ProcessAccessRequestResponse, 77 weak_factory_.GetWeakPtr())); 78 } 79 80 bool MediaStreamUIProxy::Core::CheckAccess(const GURL& security_origin, 81 MediaStreamType type, 82 int render_process_id, 83 int render_frame_id) { 84 DCHECK_CURRENTLY_ON(BrowserThread::UI); 85 86 RenderFrameHostDelegate* render_delegate = 87 GetRenderFrameHostDelegate(render_process_id, render_frame_id); 88 if (!render_delegate) 89 return false; 90 91 return render_delegate->CheckMediaAccessPermission(security_origin, type); 92 } 93 94 void MediaStreamUIProxy::Core::OnStarted(gfx::NativeViewId* window_id) { 95 DCHECK_CURRENTLY_ON(BrowserThread::UI); 96 if (ui_) { 97 *window_id = ui_->OnStarted( 98 base::Bind(&Core::ProcessStopRequestFromUI, base::Unretained(this))); 99 } 100 } 101 102 void MediaStreamUIProxy::Core::ProcessAccessRequestResponse( 103 const MediaStreamDevices& devices, 104 content::MediaStreamRequestResult result, 105 scoped_ptr<MediaStreamUI> stream_ui) { 106 DCHECK_CURRENTLY_ON(BrowserThread::UI); 107 108 ui_ = stream_ui.Pass(); 109 BrowserThread::PostTask( 110 BrowserThread::IO, FROM_HERE, 111 base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse, 112 proxy_, devices, result)); 113 } 114 115 void MediaStreamUIProxy::Core::ProcessStopRequestFromUI() { 116 DCHECK_CURRENTLY_ON(BrowserThread::UI); 117 118 BrowserThread::PostTask( 119 BrowserThread::IO, FROM_HERE, 120 base::Bind(&MediaStreamUIProxy::ProcessStopRequestFromUI, proxy_)); 121 } 122 123 RenderFrameHostDelegate* MediaStreamUIProxy::Core::GetRenderFrameHostDelegate( 124 int render_process_id, 125 int render_frame_id) { 126 if (test_render_delegate_) 127 return test_render_delegate_; 128 RenderFrameHostImpl* host = 129 RenderFrameHostImpl::FromID(render_process_id, render_frame_id); 130 return host ? host->delegate() : NULL; 131 } 132 133 // static 134 scoped_ptr<MediaStreamUIProxy> MediaStreamUIProxy::Create() { 135 return scoped_ptr<MediaStreamUIProxy>(new MediaStreamUIProxy(NULL)); 136 } 137 138 // static 139 scoped_ptr<MediaStreamUIProxy> MediaStreamUIProxy::CreateForTests( 140 RenderFrameHostDelegate* render_delegate) { 141 return scoped_ptr<MediaStreamUIProxy>( 142 new MediaStreamUIProxy(render_delegate)); 143 } 144 145 MediaStreamUIProxy::MediaStreamUIProxy( 146 RenderFrameHostDelegate* test_render_delegate) 147 : weak_factory_(this) { 148 DCHECK_CURRENTLY_ON(BrowserThread::IO); 149 core_.reset(new Core(weak_factory_.GetWeakPtr(), test_render_delegate)); 150 } 151 152 MediaStreamUIProxy::~MediaStreamUIProxy() { 153 DCHECK_CURRENTLY_ON(BrowserThread::IO); 154 } 155 156 void MediaStreamUIProxy::RequestAccess( 157 const MediaStreamRequest& request, 158 const ResponseCallback& response_callback) { 159 DCHECK_CURRENTLY_ON(BrowserThread::IO); 160 161 response_callback_ = response_callback; 162 BrowserThread::PostTask( 163 BrowserThread::UI, FROM_HERE, 164 base::Bind(&Core::RequestAccess, base::Unretained(core_.get()), request)); 165 } 166 167 void MediaStreamUIProxy::CheckAccess( 168 const GURL& security_origin, 169 MediaStreamType type, 170 int render_process_id, 171 int render_frame_id, 172 const base::Callback<void(bool)>& callback) { 173 DCHECK_CURRENTLY_ON(BrowserThread::IO); 174 175 BrowserThread::PostTaskAndReplyWithResult( 176 BrowserThread::UI, 177 FROM_HERE, 178 base::Bind(&Core::CheckAccess, 179 base::Unretained(core_.get()), 180 security_origin, 181 type, 182 render_process_id, 183 render_frame_id), 184 base::Bind(&MediaStreamUIProxy::OnCheckedAccess, 185 weak_factory_.GetWeakPtr(), 186 callback)); 187 } 188 189 void MediaStreamUIProxy::OnStarted(const base::Closure& stop_callback, 190 const WindowIdCallback& window_id_callback) { 191 DCHECK_CURRENTLY_ON(BrowserThread::IO); 192 193 stop_callback_ = stop_callback; 194 195 // Owned by the PostTaskAndReply callback. 196 gfx::NativeViewId* window_id = new gfx::NativeViewId(0); 197 198 BrowserThread::PostTaskAndReply( 199 BrowserThread::UI, 200 FROM_HERE, 201 base::Bind(&Core::OnStarted, base::Unretained(core_.get()), window_id), 202 base::Bind(&MediaStreamUIProxy::OnWindowId, 203 weak_factory_.GetWeakPtr(), 204 window_id_callback, 205 base::Owned(window_id))); 206 } 207 208 void MediaStreamUIProxy::ProcessAccessRequestResponse( 209 const MediaStreamDevices& devices, 210 content::MediaStreamRequestResult result) { 211 DCHECK_CURRENTLY_ON(BrowserThread::IO); 212 DCHECK(!response_callback_.is_null()); 213 214 ResponseCallback cb = response_callback_; 215 response_callback_.Reset(); 216 cb.Run(devices, result); 217 } 218 219 void MediaStreamUIProxy::ProcessStopRequestFromUI() { 220 DCHECK_CURRENTLY_ON(BrowserThread::IO); 221 DCHECK(!stop_callback_.is_null()); 222 223 base::Closure cb = stop_callback_; 224 stop_callback_.Reset(); 225 cb.Run(); 226 } 227 228 void MediaStreamUIProxy::OnWindowId(const WindowIdCallback& window_id_callback, 229 gfx::NativeViewId* window_id) { 230 DCHECK_CURRENTLY_ON(BrowserThread::IO); 231 if (!window_id_callback.is_null()) 232 window_id_callback.Run(*window_id); 233 } 234 235 void MediaStreamUIProxy::OnCheckedAccess( 236 const base::Callback<void(bool)>& callback, 237 bool have_access) { 238 DCHECK_CURRENTLY_ON(BrowserThread::IO); 239 if (!callback.is_null()) 240 callback.Run(have_access); 241 } 242 243 FakeMediaStreamUIProxy::FakeMediaStreamUIProxy() 244 : MediaStreamUIProxy(NULL), 245 mic_access_(true), 246 camera_access_(true) { 247 } 248 249 FakeMediaStreamUIProxy::~FakeMediaStreamUIProxy() {} 250 251 void FakeMediaStreamUIProxy::SetAvailableDevices( 252 const MediaStreamDevices& devices) { 253 devices_ = devices; 254 } 255 256 void FakeMediaStreamUIProxy::SetMicAccess(bool access) { 257 mic_access_ = access; 258 } 259 260 void FakeMediaStreamUIProxy::SetCameraAccess(bool access) { 261 camera_access_ = access; 262 } 263 264 void FakeMediaStreamUIProxy::RequestAccess( 265 const MediaStreamRequest& request, 266 const ResponseCallback& response_callback) { 267 DCHECK_CURRENTLY_ON(BrowserThread::IO); 268 269 response_callback_ = response_callback; 270 271 if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 272 switches::kUseFakeUIForMediaStream) == "deny") { 273 // Immediately deny the request. 274 BrowserThread::PostTask( 275 BrowserThread::IO, FROM_HERE, 276 base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse, 277 weak_factory_.GetWeakPtr(), 278 MediaStreamDevices(), 279 MEDIA_DEVICE_PERMISSION_DENIED)); 280 return; 281 } 282 283 MediaStreamDevices devices_to_use; 284 bool accepted_audio = false; 285 bool accepted_video = false; 286 287 // Use the first capture device of the same media type in the list for the 288 // fake UI. 289 for (MediaStreamDevices::const_iterator it = devices_.begin(); 290 it != devices_.end(); ++it) { 291 if (!accepted_audio && 292 IsAudioInputMediaType(request.audio_type) && 293 IsAudioInputMediaType(it->type) && 294 (request.requested_audio_device_id.empty() || 295 request.requested_audio_device_id == it->id)) { 296 devices_to_use.push_back(*it); 297 accepted_audio = true; 298 } else if (!accepted_video && 299 IsVideoMediaType(request.video_type) && 300 IsVideoMediaType(it->type) && 301 (request.requested_video_device_id.empty() || 302 request.requested_video_device_id == it->id)) { 303 devices_to_use.push_back(*it); 304 accepted_video = true; 305 } 306 } 307 308 // Fail the request if a device doesn't exist for the requested type. 309 if ((request.audio_type != MEDIA_NO_SERVICE && !accepted_audio) || 310 (request.video_type != MEDIA_NO_SERVICE && !accepted_video)) { 311 devices_to_use.clear(); 312 } 313 314 BrowserThread::PostTask( 315 BrowserThread::IO, FROM_HERE, 316 base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse, 317 weak_factory_.GetWeakPtr(), 318 devices_to_use, 319 devices_to_use.empty() ? 320 MEDIA_DEVICE_NO_HARDWARE : 321 MEDIA_DEVICE_OK)); 322 } 323 324 void FakeMediaStreamUIProxy::CheckAccess( 325 const GURL& security_origin, 326 MediaStreamType type, 327 int render_process_id, 328 int render_frame_id, 329 const base::Callback<void(bool)>& callback) { 330 DCHECK_CURRENTLY_ON(BrowserThread::IO); 331 DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE || 332 type == MEDIA_DEVICE_VIDEO_CAPTURE); 333 334 bool have_access = false; 335 if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 336 switches::kUseFakeUIForMediaStream) != "deny") { 337 have_access = 338 type == MEDIA_DEVICE_AUDIO_CAPTURE ? mic_access_ : camera_access_; 339 } 340 341 BrowserThread::PostTask( 342 BrowserThread::IO, 343 FROM_HERE, 344 base::Bind(&MediaStreamUIProxy::OnCheckedAccess, 345 weak_factory_.GetWeakPtr(), 346 callback, 347 have_access)); 348 return; 349 } 350 351 void FakeMediaStreamUIProxy::OnStarted( 352 const base::Closure& stop_callback, 353 const WindowIdCallback& window_id_callback) {} 354 355 } // namespace content 356