// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/renderer_host/media/media_stream_ui_proxy.h" #include "base/command_line.h" #include "content/browser/frame_host/render_frame_host_delegate.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/content_switches.h" #include "media/video/capture/fake_video_capture_device.h" namespace content { class MediaStreamUIProxy::Core { public: explicit Core(const base::WeakPtr<MediaStreamUIProxy>& proxy, RenderFrameHostDelegate* test_render_delegate); ~Core(); void RequestAccess(const MediaStreamRequest& request); bool CheckAccess(const GURL& security_origin, MediaStreamType type, int process_id, int frame_id); void OnStarted(gfx::NativeViewId* window_id); private: void ProcessAccessRequestResponse(const MediaStreamDevices& devices, content::MediaStreamRequestResult result, scoped_ptr<MediaStreamUI> stream_ui); void ProcessStopRequestFromUI(); RenderFrameHostDelegate* GetRenderFrameHostDelegate(int render_process_id, int render_frame_id); base::WeakPtr<MediaStreamUIProxy> proxy_; scoped_ptr<MediaStreamUI> ui_; RenderFrameHostDelegate* const test_render_delegate_; // WeakPtr<> is used to RequestMediaAccessPermission() because there is no way // cancel media requests. base::WeakPtrFactory<Core> weak_factory_; DISALLOW_COPY_AND_ASSIGN(Core); }; MediaStreamUIProxy::Core::Core(const base::WeakPtr<MediaStreamUIProxy>& proxy, RenderFrameHostDelegate* test_render_delegate) : proxy_(proxy), test_render_delegate_(test_render_delegate), weak_factory_(this) { } MediaStreamUIProxy::Core::~Core() { DCHECK_CURRENTLY_ON(BrowserThread::UI); } void MediaStreamUIProxy::Core::RequestAccess( const MediaStreamRequest& request) { DCHECK_CURRENTLY_ON(BrowserThread::UI); RenderFrameHostDelegate* render_delegate = GetRenderFrameHostDelegate( request.render_process_id, request.render_frame_id); // Tab may have gone away, or has no delegate from which to request access. if (!render_delegate) { ProcessAccessRequestResponse(MediaStreamDevices(), MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN, scoped_ptr<MediaStreamUI>()); return; } render_delegate->RequestMediaAccessPermission( request, base::Bind(&Core::ProcessAccessRequestResponse, weak_factory_.GetWeakPtr())); } bool MediaStreamUIProxy::Core::CheckAccess(const GURL& security_origin, MediaStreamType type, int render_process_id, int render_frame_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); RenderFrameHostDelegate* render_delegate = GetRenderFrameHostDelegate(render_process_id, render_frame_id); if (!render_delegate) return false; return render_delegate->CheckMediaAccessPermission(security_origin, type); } void MediaStreamUIProxy::Core::OnStarted(gfx::NativeViewId* window_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); if (ui_) { *window_id = ui_->OnStarted( base::Bind(&Core::ProcessStopRequestFromUI, base::Unretained(this))); } } void MediaStreamUIProxy::Core::ProcessAccessRequestResponse( const MediaStreamDevices& devices, content::MediaStreamRequestResult result, scoped_ptr<MediaStreamUI> stream_ui) { DCHECK_CURRENTLY_ON(BrowserThread::UI); ui_ = stream_ui.Pass(); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse, proxy_, devices, result)); } void MediaStreamUIProxy::Core::ProcessStopRequestFromUI() { DCHECK_CURRENTLY_ON(BrowserThread::UI); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&MediaStreamUIProxy::ProcessStopRequestFromUI, proxy_)); } RenderFrameHostDelegate* MediaStreamUIProxy::Core::GetRenderFrameHostDelegate( int render_process_id, int render_frame_id) { if (test_render_delegate_) return test_render_delegate_; RenderFrameHostImpl* host = RenderFrameHostImpl::FromID(render_process_id, render_frame_id); return host ? host->delegate() : NULL; } // static scoped_ptr<MediaStreamUIProxy> MediaStreamUIProxy::Create() { return scoped_ptr<MediaStreamUIProxy>(new MediaStreamUIProxy(NULL)); } // static scoped_ptr<MediaStreamUIProxy> MediaStreamUIProxy::CreateForTests( RenderFrameHostDelegate* render_delegate) { return scoped_ptr<MediaStreamUIProxy>( new MediaStreamUIProxy(render_delegate)); } MediaStreamUIProxy::MediaStreamUIProxy( RenderFrameHostDelegate* test_render_delegate) : weak_factory_(this) { DCHECK_CURRENTLY_ON(BrowserThread::IO); core_.reset(new Core(weak_factory_.GetWeakPtr(), test_render_delegate)); } MediaStreamUIProxy::~MediaStreamUIProxy() { DCHECK_CURRENTLY_ON(BrowserThread::IO); } void MediaStreamUIProxy::RequestAccess( const MediaStreamRequest& request, const ResponseCallback& response_callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); response_callback_ = response_callback; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&Core::RequestAccess, base::Unretained(core_.get()), request)); } void MediaStreamUIProxy::CheckAccess( const GURL& security_origin, MediaStreamType type, int render_process_id, int render_frame_id, const base::Callback<void(bool)>& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); BrowserThread::PostTaskAndReplyWithResult( BrowserThread::UI, FROM_HERE, base::Bind(&Core::CheckAccess, base::Unretained(core_.get()), security_origin, type, render_process_id, render_frame_id), base::Bind(&MediaStreamUIProxy::OnCheckedAccess, weak_factory_.GetWeakPtr(), callback)); } void MediaStreamUIProxy::OnStarted(const base::Closure& stop_callback, const WindowIdCallback& window_id_callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); stop_callback_ = stop_callback; // Owned by the PostTaskAndReply callback. gfx::NativeViewId* window_id = new gfx::NativeViewId(0); BrowserThread::PostTaskAndReply( BrowserThread::UI, FROM_HERE, base::Bind(&Core::OnStarted, base::Unretained(core_.get()), window_id), base::Bind(&MediaStreamUIProxy::OnWindowId, weak_factory_.GetWeakPtr(), window_id_callback, base::Owned(window_id))); } void MediaStreamUIProxy::ProcessAccessRequestResponse( const MediaStreamDevices& devices, content::MediaStreamRequestResult result) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(!response_callback_.is_null()); ResponseCallback cb = response_callback_; response_callback_.Reset(); cb.Run(devices, result); } void MediaStreamUIProxy::ProcessStopRequestFromUI() { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(!stop_callback_.is_null()); base::Closure cb = stop_callback_; stop_callback_.Reset(); cb.Run(); } void MediaStreamUIProxy::OnWindowId(const WindowIdCallback& window_id_callback, gfx::NativeViewId* window_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!window_id_callback.is_null()) window_id_callback.Run(*window_id); } void MediaStreamUIProxy::OnCheckedAccess( const base::Callback<void(bool)>& callback, bool have_access) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!callback.is_null()) callback.Run(have_access); } FakeMediaStreamUIProxy::FakeMediaStreamUIProxy() : MediaStreamUIProxy(NULL), mic_access_(true), camera_access_(true) { } FakeMediaStreamUIProxy::~FakeMediaStreamUIProxy() {} void FakeMediaStreamUIProxy::SetAvailableDevices( const MediaStreamDevices& devices) { devices_ = devices; } void FakeMediaStreamUIProxy::SetMicAccess(bool access) { mic_access_ = access; } void FakeMediaStreamUIProxy::SetCameraAccess(bool access) { camera_access_ = access; } void FakeMediaStreamUIProxy::RequestAccess( const MediaStreamRequest& request, const ResponseCallback& response_callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); response_callback_ = response_callback; if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kUseFakeUIForMediaStream) == "deny") { // Immediately deny the request. BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse, weak_factory_.GetWeakPtr(), MediaStreamDevices(), MEDIA_DEVICE_PERMISSION_DENIED)); return; } MediaStreamDevices devices_to_use; bool accepted_audio = false; bool accepted_video = false; // Use the first capture device of the same media type in the list for the // fake UI. for (MediaStreamDevices::const_iterator it = devices_.begin(); it != devices_.end(); ++it) { if (!accepted_audio && IsAudioInputMediaType(request.audio_type) && IsAudioInputMediaType(it->type) && (request.requested_audio_device_id.empty() || request.requested_audio_device_id == it->id)) { devices_to_use.push_back(*it); accepted_audio = true; } else if (!accepted_video && IsVideoMediaType(request.video_type) && IsVideoMediaType(it->type) && (request.requested_video_device_id.empty() || request.requested_video_device_id == it->id)) { devices_to_use.push_back(*it); accepted_video = true; } } // Fail the request if a device doesn't exist for the requested type. if ((request.audio_type != MEDIA_NO_SERVICE && !accepted_audio) || (request.video_type != MEDIA_NO_SERVICE && !accepted_video)) { devices_to_use.clear(); } BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&MediaStreamUIProxy::ProcessAccessRequestResponse, weak_factory_.GetWeakPtr(), devices_to_use, devices_to_use.empty() ? MEDIA_DEVICE_NO_HARDWARE : MEDIA_DEVICE_OK)); } void FakeMediaStreamUIProxy::CheckAccess( const GURL& security_origin, MediaStreamType type, int render_process_id, int render_frame_id, const base::Callback<void(bool)>& callback) { DCHECK_CURRENTLY_ON(BrowserThread::IO); DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE || type == MEDIA_DEVICE_VIDEO_CAPTURE); bool have_access = false; if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kUseFakeUIForMediaStream) != "deny") { have_access = type == MEDIA_DEVICE_AUDIO_CAPTURE ? mic_access_ : camera_access_; } BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&MediaStreamUIProxy::OnCheckedAccess, weak_factory_.GetWeakPtr(), callback, have_access)); return; } void FakeMediaStreamUIProxy::OnStarted( const base::Closure& stop_callback, const WindowIdCallback& window_id_callback) {} } // namespace content