Chromium Code Reviews| Index: content/browser/renderer_host/media/media_stream_manager.cc | 
| diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc | 
| index f35795252c46127548d77e93a667a0460b0a487b..6bf60c920a2ed2824fddd5f36d546d9b4c550124 100644 | 
| --- a/content/browser/renderer_host/media/media_stream_manager.cc | 
| +++ b/content/browser/renderer_host/media/media_stream_manager.cc | 
| @@ -18,6 +18,7 @@ | 
| #include "content/public/browser/browser_thread.h" | 
| #include "content/public/browser/content_browser_client.h" | 
| #include "content/public/browser/media_observer.h" | 
| +#include "content/public/browser/media_request_state.h" | 
| #include "googleurl/src/gurl.h" | 
| #if defined(OS_WIN) | 
| @@ -25,6 +26,11 @@ | 
| #endif | 
| using content::BrowserThread; | 
| +using content::MediaStreamRequest; | 
| + | 
| +namespace { | 
| +const char kExtensionScheme[] = "chrome-extension"; | 
| 
 
jam
2013/02/01 03:10:35
I just saw this while looking through the code. th
 
 | 
| +} // namespace | 
| namespace media_stream { | 
| @@ -73,16 +79,8 @@ void DeviceThread::CleanUp() { | 
| #endif | 
| // TODO(xians): Merge DeviceRequest with MediaStreamRequest. | 
| -struct MediaStreamManager::DeviceRequest { | 
| - enum RequestState { | 
| - STATE_NOT_REQUESTED = 0, | 
| - STATE_REQUESTED, | 
| - STATE_PENDING_APPROVAL, | 
| - STATE_OPENING, | 
| - STATE_DONE, | 
| - STATE_ERROR | 
| - }; | 
| - | 
| +class MediaStreamManager::DeviceRequest { | 
| + public: | 
| enum RequestType { | 
| GENERATE_STREAM = 0, | 
| ENUMERATE_DEVICES, | 
| @@ -91,10 +89,11 @@ struct MediaStreamManager::DeviceRequest { | 
| DeviceRequest() | 
| : requester(NULL), | 
| - state(content::NUM_MEDIA_TYPES, STATE_NOT_REQUESTED), | 
| type(GENERATE_STREAM), | 
| render_process_id(-1), | 
| - render_view_id(-1) { | 
| + render_view_id(-1), | 
| + state_(content::NUM_MEDIA_TYPES, | 
| + content::MEDIA_REQUEST_STATE_NOT_REQUESTED) { | 
| } | 
| DeviceRequest(MediaStreamRequester* requester, | 
| @@ -104,11 +103,12 @@ struct MediaStreamManager::DeviceRequest { | 
| const GURL& request_security_origin) | 
| : requester(requester), | 
| options(request_options), | 
| - state(content::NUM_MEDIA_TYPES, STATE_NOT_REQUESTED), | 
| type(GENERATE_STREAM), | 
| render_process_id(render_process_id), | 
| render_view_id(render_view_id), | 
| - security_origin(request_security_origin) { | 
| + security_origin(request_security_origin), | 
| + state_(content::NUM_MEDIA_TYPES, | 
| + content::MEDIA_REQUEST_STATE_NOT_REQUESTED) { | 
| DCHECK(requester); | 
| } | 
| @@ -116,13 +116,41 @@ struct MediaStreamManager::DeviceRequest { | 
| MediaStreamRequester* requester; | 
| StreamOptions options; | 
| - std::vector<RequestState> state; | 
| RequestType type; | 
| int render_process_id; | 
| int render_view_id; | 
| GURL security_origin; | 
| std::string requested_device_id; | 
| StreamDeviceInfoArray devices; | 
| + | 
| + // Update the request state and notify observers. | 
| + void setState(MediaStreamType stream_type, | 
| + content::MediaRequestState new_state) { | 
| + state_[stream_type] = new_state; | 
| + | 
| + if (options.video_type != content::MEDIA_TAB_VIDEO_CAPTURE && | 
| + options.audio_type != content::MEDIA_TAB_AUDIO_CAPTURE) | 
| + return; | 
| + | 
| + content::MediaObserver* media_observer = | 
| + content::GetContentClient()->browser()->GetMediaObserver(); | 
| + if (media_observer == NULL) | 
| + return; | 
| + | 
| + media_observer->OnMediaRequestStateChanged( | 
| + render_process_id, render_view_id, | 
| + content::MediaStreamDevice(stream_type, | 
| + requested_device_id, | 
| + requested_device_id), | 
| + new_state); | 
| + } | 
| + | 
| + content::MediaRequestState getState(MediaStreamType stream_type) const { | 
| + return state_[stream_type]; | 
| + } | 
| + | 
| + private: | 
| + std::vector<content::MediaRequestState> state_; | 
| }; | 
| MediaStreamManager::EnumerationCache::EnumerationCache() | 
| @@ -207,6 +235,7 @@ void MediaStreamManager::GenerateStreamForDevice( | 
| security_origin), | 
| label); | 
| DeviceRequest& request = requests_[*label]; | 
| + request.requested_device_id = device_id; | 
| // Get user confirmation to use the capture device. | 
| device_settings_->RequestCaptureDeviceUsage(*label, | 
| @@ -214,24 +243,48 @@ void MediaStreamManager::GenerateStreamForDevice( | 
| render_view_id, | 
| options, | 
| security_origin); | 
| + | 
| + if (!security_origin.SchemeIs(kExtensionScheme) || | 
| + (options.audio_type != content::MEDIA_TAB_AUDIO_CAPTURE && | 
| + options.audio_type != content::MEDIA_NO_SERVICE) || | 
| + (options.video_type != content::MEDIA_TAB_VIDEO_CAPTURE && | 
| + options.video_type != content::MEDIA_NO_SERVICE)) { | 
| + LOG(ERROR) << "Invalid request or used tab capture outside extension API."; | 
| + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | 
| + base::Bind(&MediaStreamManager::CancelGenerateStream, | 
| + base::Unretained(this), *label)); | 
| + return; | 
| + } | 
| + | 
| // TODO(miu): We should ask the device manager whether a device with id | 
| // |device_id| actually exists. Note that no such MediaStreamProvider API for | 
| // this currently exists. Also, we don't have a user-friendly device name for | 
| // the infobar UI. | 
| + StreamDeviceInfoArray devices; | 
| if (content::IsAudioMediaType(options.audio_type)) { | 
| - request.state[options.audio_type] = DeviceRequest::STATE_PENDING_APPROVAL; | 
| - device_settings_->AvailableDevices( | 
| - *label, options.audio_type, StreamDeviceInfoArray( | 
| - 1, StreamDeviceInfo(options.audio_type, device_id, device_id, | 
| - false))); | 
| + // TODO(justinlin): Updating the state to requested and pending are no-ops | 
| + // in terms of the media manager, but these are the state changes we want to | 
| + // support in terms of extensions (which is registered as an observer). | 
| + request.setState(options.audio_type, | 
| + content::MEDIA_REQUEST_STATE_REQUESTED); | 
| + request.setState(options.audio_type, | 
| + content::MEDIA_REQUEST_STATE_PENDING_APPROVAL); | 
| + devices.push_back( | 
| + StreamDeviceInfo(options.audio_type, device_id, device_id, false)); | 
| } | 
| if (content::IsVideoMediaType(options.video_type)) { | 
| - request.state[options.video_type] = DeviceRequest::STATE_PENDING_APPROVAL; | 
| - device_settings_->AvailableDevices( | 
| - *label, options.video_type, StreamDeviceInfoArray( | 
| - 1, StreamDeviceInfo(options.video_type, device_id, device_id, | 
| - false))); | 
| + request.setState(options.video_type, | 
| + content::MEDIA_REQUEST_STATE_REQUESTED); | 
| + request.setState(options.video_type, | 
| + content::MEDIA_REQUEST_STATE_PENDING_APPROVAL); | 
| + devices.push_back( | 
| + StreamDeviceInfo(options.video_type, device_id, device_id, false)); | 
| } | 
| + | 
| + // Bypass the user authorization dropdown for tab capture. | 
| + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | 
| + base::Bind(&MediaStreamManager::DevicesAccepted, | 
| + base::Unretained(this), *label, devices)); | 
| } | 
| void MediaStreamManager::CancelGenerateStream(const std::string& label) { | 
| @@ -245,7 +298,8 @@ void MediaStreamManager::CancelGenerateStream(const std::string& label) { | 
| for (int i = content::MEDIA_NO_SERVICE + 1; i < content::NUM_MEDIA_TYPES; | 
| ++i) { | 
| const MediaStreamType stream_type = static_cast<MediaStreamType>(i); | 
| - if (request.state[stream_type] != DeviceRequest::STATE_OPENING) { | 
| + if (request.getState(stream_type) != | 
| + content::MEDIA_REQUEST_STATE_OPENING) { | 
| continue; | 
| } | 
| for (StreamDeviceInfoArray::const_iterator device_it = | 
| @@ -280,6 +334,14 @@ void MediaStreamManager::StopGeneratedStream(const std::string& label) { | 
| } | 
| if (it->second.type == DeviceRequest::GENERATE_STREAM && | 
| RequestDone(it->second)) { | 
| + // Notify observers that this device is being closed. | 
| + for (int i = content::MEDIA_NO_SERVICE + 1; | 
| + i != content::NUM_MEDIA_TYPES; ++i) { | 
| + if (it->second.getState(static_cast<MediaStreamType>(i)) != | 
| + content::MEDIA_REQUEST_STATE_NOT_REQUESTED) | 
| + it->second.setState(static_cast<MediaStreamType>(i), | 
| + content::MEDIA_REQUEST_STATE_CLOSING); | 
| + } | 
| NotifyObserverDevicesClosed(&(it->second)); | 
| } | 
| requests_.erase(it); | 
| @@ -319,7 +381,7 @@ void MediaStreamManager::EnumerateDevices( | 
| if (cache->valid) { | 
| // Cached device list of this type exists. Just send it out. | 
| - new_request.state[type] = DeviceRequest::STATE_REQUESTED; | 
| + new_request.setState(type, content::MEDIA_REQUEST_STATE_REQUESTED); | 
| AddRequest(new_request, label); | 
| // Need to post a task since the requester won't have label till | 
| // this function returns. | 
| @@ -423,7 +485,8 @@ void MediaStreamManager::StartEnumeration( | 
| ++i) { | 
| const MediaStreamType stream_type = static_cast<MediaStreamType>(i); | 
| if (Requested(new_request->options, stream_type)) { | 
| - new_request->state[stream_type] = DeviceRequest::STATE_REQUESTED; | 
| + new_request->setState(stream_type, | 
| + content::MEDIA_REQUEST_STATE_REQUESTED); | 
| DCHECK_GE(active_enumeration_ref_count_[stream_type], 0); | 
| if (active_enumeration_ref_count_[stream_type] == 0) { | 
| ++active_enumeration_ref_count_[stream_type]; | 
| @@ -502,7 +565,8 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, | 
| return; | 
| } | 
| - DCHECK_NE(request->state[stream_type], DeviceRequest::STATE_REQUESTED); | 
| + DCHECK_NE(request->getState(stream_type), | 
| + content::MEDIA_REQUEST_STATE_REQUESTED); | 
| // Check if all devices for this stream type are opened. Update the state if | 
| // they are. | 
| @@ -516,7 +580,8 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, | 
| return; | 
| } | 
| } | 
| - request->state[stream_type] = DeviceRequest::STATE_DONE; | 
| + | 
| + request->setState(stream_type, content::MEDIA_REQUEST_STATE_DONE); | 
| if (!RequestDone(*request)) { | 
| // This stream_type is done, but not the other type. | 
| @@ -583,10 +648,12 @@ void MediaStreamManager::DevicesEnumerated( | 
| std::list<std::string> label_list; | 
| for (DeviceRequests::iterator it = requests_.begin(); it != requests_.end(); | 
| ++it) { | 
| - if (it->second.state[stream_type] == DeviceRequest::STATE_REQUESTED && | 
| + if (it->second.getState(stream_type) == | 
| + content::MEDIA_REQUEST_STATE_REQUESTED && | 
| Requested(it->second.options, stream_type)) { | 
| if (it->second.type != DeviceRequest::ENUMERATE_DEVICES) | 
| - it->second.state[stream_type] = DeviceRequest::STATE_PENDING_APPROVAL; | 
| + it->second.setState(stream_type, | 
| + content::MEDIA_REQUEST_STATE_PENDING_APPROVAL); | 
| label_list.push_back(it->first); | 
| } | 
| } | 
| @@ -607,8 +674,8 @@ void MediaStreamManager::DevicesEnumerated( | 
| device.in_use = false; | 
| device.session_id = | 
| GetDeviceManager(device_it->stream_type)->Open(device); | 
| - request.state[device_it->stream_type] = | 
| - DeviceRequest::STATE_OPENING; | 
| + request.setState(device_it->stream_type, | 
| + content::MEDIA_REQUEST_STATE_OPENING); | 
| request.devices.push_back(device); | 
| break; | 
| } | 
| @@ -656,8 +723,10 @@ void MediaStreamManager::Error(MediaStreamType stream_type, | 
| // We've found the failing device. Find the error case: | 
| // An error should only be reported to the MediaStreamManager if | 
| // the request has not been fulfilled yet. | 
| - DCHECK(it->second.state[stream_type] != DeviceRequest::STATE_DONE); | 
| - if (it->second.state[stream_type] != DeviceRequest::STATE_DONE) { | 
| + DCHECK(it->second.getState(stream_type) != | 
| + content::MEDIA_REQUEST_STATE_DONE); | 
| + if (it->second.getState(stream_type) != | 
| + content::MEDIA_REQUEST_STATE_DONE) { | 
| // Request is not done, devices are not opened in this case. | 
| if (devices.size() <= 1) { | 
| // 1. Device not opened and no other devices for this request -> | 
| @@ -701,12 +770,14 @@ void MediaStreamManager::DevicesAccepted(const std::string& label, | 
| // Set in_use to false to be able to track if this device has been | 
| // opened. in_use might be true if the device type can be used in more | 
| // than one session. | 
| - DCHECK_EQ(request.state[device_it->stream_type], | 
| - DeviceRequest::STATE_PENDING_APPROVAL); | 
| + DCHECK_EQ(request.getState(device_it->stream_type), | 
| + content::MEDIA_REQUEST_STATE_PENDING_APPROVAL); | 
| device_info.in_use = false; | 
| + | 
| device_info.session_id = | 
| GetDeviceManager(device_info.stream_type)->Open(device_info); | 
| - request.state[device_it->stream_type] = DeviceRequest::STATE_OPENING; | 
| + request.setState(device_it->stream_type, | 
| + content::MEDIA_REQUEST_STATE_OPENING); | 
| request.devices.push_back(device_info); | 
| if (device_info.stream_type == request.options.audio_type) { | 
| @@ -718,10 +789,12 @@ void MediaStreamManager::DevicesAccepted(const std::string& label, | 
| // Check whether we've received all stream types requested. | 
| if (!found_audio && content::IsAudioMediaType(request.options.audio_type)) { | 
| - request.state[request.options.audio_type] = DeviceRequest::STATE_ERROR; | 
| + request.setState(request.options.audio_type, | 
| + content::MEDIA_REQUEST_STATE_ERROR); | 
| } | 
| if (!found_video && content::IsVideoMediaType(request.options.video_type)) { | 
| - request.state[request.options.video_type] = DeviceRequest::STATE_ERROR; | 
| + request.setState(request.options.video_type, | 
| + content::MEDIA_REQUEST_STATE_ERROR); | 
| } | 
| } | 
| @@ -806,16 +879,20 @@ bool MediaStreamManager::RequestDone(const DeviceRequest& request) const { | 
| const bool audio_done = | 
| !requested_audio || | 
| - request.state[request.options.audio_type] == DeviceRequest::STATE_DONE || | 
| - request.state[request.options.audio_type] == DeviceRequest::STATE_ERROR; | 
| + request.getState(request.options.audio_type) == | 
| + content::MEDIA_REQUEST_STATE_DONE || | 
| + request.getState(request.options.audio_type) == | 
| + content::MEDIA_REQUEST_STATE_ERROR; | 
| if (!audio_done) { | 
| return false; | 
| } | 
| const bool video_done = | 
| !requested_video || | 
| - request.state[request.options.video_type] == DeviceRequest::STATE_DONE || | 
| - request.state[request.options.video_type] == DeviceRequest::STATE_ERROR; | 
| + request.getState(request.options.video_type) == | 
| + content::MEDIA_REQUEST_STATE_DONE || | 
| + request.getState(request.options.video_type) == | 
| + content::MEDIA_REQUEST_STATE_ERROR; | 
| if (!video_done) { | 
| return false; | 
| } |