Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(59)

Side by Side Diff: content/browser/renderer_host/media/media_stream_device_settings.cc

Issue 10829190: Resolve the problems where we can leak the system tray UI (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebased and addressed the comments. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/renderer_host/media/media_stream_device_settings.h" 5 #include "content/browser/renderer_host/media/media_stream_device_settings.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback.h" 10 #include "base/callback.h"
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 }; 79 };
80 80
81 } // namespace 81 } // namespace
82 82
83 namespace media_stream { 83 namespace media_stream {
84 84
85 typedef std::map<MediaStreamType, StreamDeviceInfoArray> DeviceMap; 85 typedef std::map<MediaStreamType, StreamDeviceInfoArray> DeviceMap;
86 86
87 // Device request contains all data needed to keep track of requests between the 87 // Device request contains all data needed to keep track of requests between the
88 // different calls. 88 // different calls.
89 class MediaStreamDeviceSettingsRequest : public MediaStreamRequest { 89 struct MediaStreamDeviceSettingsRequest : public MediaStreamRequest {
90 public: 90 public:
91 MediaStreamDeviceSettingsRequest( 91 MediaStreamDeviceSettingsRequest(
92 int render_pid, 92 int render_pid,
93 int render_vid, 93 int render_vid,
94 const GURL& origin, 94 const GURL& origin,
95 const StreamOptions& request_options) 95 const StreamOptions& request_options)
96 : MediaStreamRequest(render_pid, render_vid, origin), 96 : MediaStreamRequest(render_pid, render_vid, origin),
97 options(request_options), 97 options(request_options),
98 posted_task(false) {} 98 posted_task(false) {}
99 99
100 ~MediaStreamDeviceSettingsRequest() {} 100 ~MediaStreamDeviceSettingsRequest() {}
101 101
102 // Request options. 102 // Request options.
103 StreamOptions options; 103 StreamOptions options;
104 // Map containing available devices for the requested capture types. 104 // Map containing available devices for the requested capture types.
105 // Note, never call devices_full[stream_type].empty() before making sure
106 // that type of device has existed on the map, otherwise it will create an
107 // empty device entry on the map.
105 DeviceMap devices_full; 108 DeviceMap devices_full;
106 // Whether or not a task was posted to make the call to 109 // Whether or not a task was posted to make the call to
107 // RequestMediaAccessPermission, to make sure that we never post twice to it. 110 // RequestMediaAccessPermission, to make sure that we never post twice to it.
108 bool posted_task; 111 bool posted_task;
109 }; 112 };
110 113
111 namespace { 114 namespace {
112 115
113 // Sends the request to the appropriate WebContents. 116 // Sends the request to the appropriate WebContents.
114 void DoDeviceRequest( 117 void DoDeviceRequest(const MediaStreamDeviceSettingsRequest& request,
115 const MediaStreamDeviceSettingsRequest& request, 118 const content::MediaResponseCallback& callback) {
116 const content::MediaResponseCallback& callback) {
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
118 120
119 // Send the permission request to the web contents. 121 // Send the permission request to the web contents.
120 content::RenderViewHostImpl* host = 122 content::RenderViewHostImpl* host =
121 content::RenderViewHostImpl::FromID(request.render_process_id, 123 content::RenderViewHostImpl::FromID(request.render_process_id,
122 request.render_view_id); 124 request.render_view_id);
123 125
124 // Tab may have gone away. 126 // Tab may have gone away.
125 if (!host || !host->GetDelegate()) { 127 if (!host || !host->GetDelegate()) {
126 callback.Run(content::MediaStreamDevices()); 128 callback.Run(content::MediaStreamDevices());
127 return; 129 return;
128 } 130 }
129 131
130 host->GetDelegate()->RequestMediaAccessPermission(&request, callback); 132 host->GetDelegate()->RequestMediaAccessPermission(&request, callback);
131 } 133 }
132 134
135 bool IsRequestReadyForView(
136 media_stream::MediaStreamDeviceSettingsRequest* request) {
137 if ((request->options.audio && request->devices_full.count(
138 content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) == 0) ||
139 (request->options.video && request->devices_full.count(
140 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE) == 0))
141 return false;
scherkus (not reviewing) 2012/08/13 19:51:22 please use {} for if-statements spanning >1 lines
no longer working on chromium 2012/08/14 11:17:04 Done.
142
143 // We have got all the requested devices, it is ready if it has not
144 // been posted for UI yet.
145 return !request->posted_task;
146 }
147
133 } // namespace 148 } // namespace
134 149
135 MediaStreamDeviceSettings::MediaStreamDeviceSettings( 150 MediaStreamDeviceSettings::MediaStreamDeviceSettings(
136 SettingsRequester* requester) 151 SettingsRequester* requester)
137 : requester_(requester), 152 : requester_(requester),
138 use_fake_ui_(false), 153 use_fake_ui_(false),
139 weak_ptr_factory_(this) { 154 weak_ptr_factory_(this) {
140 DCHECK(requester_); 155 DCHECK(requester_);
141 } 156 }
142 157
143 MediaStreamDeviceSettings::~MediaStreamDeviceSettings() { 158 MediaStreamDeviceSettings::~MediaStreamDeviceSettings() {
144 STLDeleteValues(&requests_); 159 STLDeleteValues(&requests_);
145 } 160 }
146 161
147 void MediaStreamDeviceSettings::RequestCaptureDeviceUsage( 162 void MediaStreamDeviceSettings::RequestCaptureDeviceUsage(
148 const std::string& label, int render_process_id, int render_view_id, 163 const std::string& label, int render_process_id, int render_view_id,
149 const StreamOptions& request_options, const GURL& security_origin) { 164 const StreamOptions& request_options, const GURL& security_origin) {
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
151 166 DCHECK(requests_.find(label) == requests_.end());
152 if (requests_.find(label) != requests_.end()) {
153 // Request with this id already exists.
154 requester_->SettingsError(label);
155 return;
156 }
157 167
158 // Create a new request. 168 // Create a new request.
159 requests_.insert(std::make_pair(label, new MediaStreamDeviceSettingsRequest( 169 requests_.insert(std::make_pair(label, new MediaStreamDeviceSettingsRequest(
160 render_process_id, render_view_id, security_origin, request_options))); 170 render_process_id, render_view_id, security_origin, request_options)));
161 } 171 }
162 172
163 void MediaStreamDeviceSettings::RemovePendingCaptureRequest( 173 void MediaStreamDeviceSettings::RemovePendingCaptureRequest(
164 const std::string& label) { 174 const std::string& label) {
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
166
167 SettingsRequests::iterator request_it = requests_.find(label); 176 SettingsRequests::iterator request_it = requests_.find(label);
168 if (request_it != requests_.end()) { 177 if (request_it != requests_.end()) {
169 // Proceed the next pending request for the same page. 178 // Proceed the next pending request for the same page.
170 MediaStreamDeviceSettingsRequest* request = request_it->second; 179 MediaStreamDeviceSettingsRequest* request = request_it->second;
171 std::string new_label = FindReadyRequestForView(request->render_view_id, 180 int render_view_id = request->render_view_id;
172 request->render_process_id); 181 int render_process_id = request->render_process_id;
173 if (!new_label.empty()) { 182 bool was_posted = request->posted_task;
174 PostRequestToUi(new_label);
175 }
176 183
177 // TODO(xians): Post a cancel request on UI thread to dismiss the infobar 184 // TODO(xians): Post a cancel request on UI thread to dismiss the infobar
178 // if request has been sent to the UI. 185 // if request has been sent to the UI.
179 // Remove the request from the queue. 186 // Remove the request from the queue.
180 requests_.erase(request_it); 187 requests_.erase(request_it);
181 delete request; 188 delete request;
189
190 // Simply return if the canceled request has not been brought to UI.
191 if (!was_posted)
192 return;
193
194 // Process the next pending request to replace the old infobar on the same
195 // page.
196 ProcessNextRequestForView(render_view_id, render_process_id);
182 } 197 }
183 } 198 }
184 199
185 void MediaStreamDeviceSettings::AvailableDevices( 200 void MediaStreamDeviceSettings::AvailableDevices(
186 const std::string& label, 201 const std::string& label,
187 MediaStreamType stream_type, 202 MediaStreamType stream_type,
188 const StreamDeviceInfoArray& devices) { 203 const StreamDeviceInfoArray& devices) {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
190 205
191 SettingsRequests::iterator request_it = requests_.find(label); 206 SettingsRequests::iterator request_it = requests_.find(label);
192 DCHECK(request_it != requests_.end()); 207 DCHECK(request_it != requests_.end());
193
194 // Add the answer for the request. 208 // Add the answer for the request.
195 MediaStreamDeviceSettingsRequest* request = request_it->second; 209 MediaStreamDeviceSettingsRequest* request = request_it->second;
196 DCHECK_EQ(request->devices_full.count(stream_type), static_cast<size_t>(0)) << 210 DCHECK_EQ(request->devices_full.count(stream_type), static_cast<size_t>(0))
197 "This request already has a list of devices for this stream type."; 211 << "This request already has a list of devices for this stream type.";
198 request->devices_full[stream_type] = devices; 212 request->devices_full[stream_type] = devices;
199 213
200 // Check if we're done. 214 if (IsRequestReadyForView(request)) {
scherkus (not reviewing) 2012/08/13 19:51:22 nit: prefer if-and-early-return instead of deeply
no longer working on chromium 2012/08/14 11:17:04 Done.
201 size_t num_media_requests = 0; 215 if (use_fake_ui_) {
202 if (request->options.audio) { 216 PostRequestToFakeUI(label);
203 num_media_requests++; 217 } else if (!IsUiBusy(request->render_view_id, request->render_process_id)) {
204 } 218 // The UI can handle only one request at the time, post the request
205 if (request->options.video) { 219 // to the view only if the UI is not handling any other request.
206 num_media_requests++;
207 }
208
209 if (request->devices_full.size() == num_media_requests) {
210 // We have all answers needed.
211 if (!use_fake_ui_) {
212 // Abort if the task was already posted: wait for it to PostResponse.
213 if (request->posted_task) {
214 return;
215 }
216 // Since the UI can only handle one request at the time, verify there
217 // is no unanswered request posted for this view. If there is, this
218 // new request will be handled once we get a response for the first one.
219 if (IsUiBusy(request->render_view_id, request->render_process_id)) {
220 return;
221 }
222 PostRequestToUi(label); 220 PostRequestToUi(label);
223 } else {
224 // Used to fake UI, which is needed for server based testing.
225 // Choose first non-opened device for each media type.
226 StreamDeviceInfoArray devices_to_use;
227 for (DeviceMap::iterator it = request->devices_full.begin();
228 it != request->devices_full.end(); ++it) {
229 for (StreamDeviceInfoArray::iterator device_it = it->second.begin();
230 device_it != it->second.end(); ++device_it) {
231 if (!device_it->in_use) {
232 devices_to_use.push_back(*device_it);
233 break;
234 }
235 }
236 }
237
238 if (!request->devices_full[
239 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE].empty() &&
240 num_media_requests != devices_to_use.size()) {
241 // Not all requested device types were opened. This happens if all
242 // video capture devices are already opened, |in_use| isn't set for
243 // audio devices. Allow the first video capture device in the list to be
244 // opened for this user too.
245 StreamDeviceInfoArray device_array =
246 request->devices_full[
247 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE];
248 devices_to_use.push_back(*(device_array.begin()));
249 }
250
251 // Post result and delete request.
252 requester_->DevicesAccepted(label, devices_to_use);
253 requests_.erase(request_it);
254 delete request;
255 } 221 }
256 } 222 }
257 } 223 }
258 224
259 void MediaStreamDeviceSettings::PostResponse( 225 void MediaStreamDeviceSettings::PostResponse(
260 const std::string& label, 226 const std::string& label,
261 const content::MediaStreamDevices& devices) { 227 const content::MediaStreamDevices& devices) {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
263
264 SettingsRequests::iterator req = requests_.find(label); 229 SettingsRequests::iterator req = requests_.find(label);
265 // Return if the request has been removed. 230 // Return if the request has been removed.
266 if (req == requests_.end()) 231 if (req == requests_.end())
267 return; 232 return;
268 233
269 DCHECK(requester_); 234 DCHECK(requester_);
270 MediaStreamDeviceSettingsRequest* request = req->second; 235 scoped_ptr<MediaStreamDeviceSettingsRequest> request(req->second);
271 requests_.erase(req); 236 requests_.erase(req);
272 237
273 // Look for queued requests for the same view. If there is a pending request, 238 // Look for queued requests for the same view. If there is a pending request,
274 // post it for user approval. 239 // post it for user approval.
275 std::string new_label = FindReadyRequestForView(request->render_view_id, 240 ProcessNextRequestForView(request->render_view_id,
276 request->render_process_id); 241 request->render_process_id);
277 if (!new_label.empty()) {
278 PostRequestToUi(new_label);
279 }
280 242
281 if (devices.size() > 0) { 243 if (devices.size() > 0) {
282 // Build a list of "full" device objects for the accepted devices. 244 // Build a list of "full" device objects for the accepted devices.
283 StreamDeviceInfoArray deviceList; 245 StreamDeviceInfoArray deviceList;
284 for (content::MediaStreamDevices::const_iterator dev = devices.begin(); 246 for (content::MediaStreamDevices::const_iterator dev = devices.begin();
285 dev != devices.end(); ++dev) { 247 dev != devices.end(); ++dev) {
286 DeviceMap::iterator subList = request->devices_full.find(dev->type); 248 DeviceMap::iterator subList = request->devices_full.find(dev->type);
287 DCHECK(subList != request->devices_full.end()); 249 DCHECK(subList != request->devices_full.end());
288 250
289 deviceList.push_back(*std::find_if(subList->second.begin(), 251 deviceList.push_back(*std::find_if(subList->second.begin(),
290 subList->second.end(), DeviceIdEquals(dev->device_id))); 252 subList->second.end(), DeviceIdEquals(dev->device_id)));
291 } 253 }
292 requester_->DevicesAccepted(label, deviceList); 254 requester_->DevicesAccepted(label, deviceList);
293 } else { 255 } else {
294 requester_->SettingsError(label); 256 requester_->SettingsError(label);
295 } 257 }
296 delete request;
297 } 258 }
298 259
299 void MediaStreamDeviceSettings::UseFakeUI() { 260 void MediaStreamDeviceSettings::UseFakeUI() {
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
301 use_fake_ui_ = true; 262 use_fake_ui_ = true;
302 } 263 }
303 264
304 bool MediaStreamDeviceSettings::IsUiBusy(int render_view_id, 265 bool MediaStreamDeviceSettings::IsUiBusy(int render_view_id,
305 int render_process_id) { 266 int render_process_id) {
306 for (SettingsRequests::iterator it = requests_.begin(); 267 for (SettingsRequests::iterator it = requests_.begin();
307 it != requests_.end(); ++it) { 268 it != requests_.end(); ++it) {
308 if (it->second->render_process_id == render_process_id && 269 if (it->second->render_process_id == render_process_id &&
309 it->second->render_view_id == render_view_id && 270 it->second->render_view_id == render_view_id &&
310 it->second->posted_task) { 271 it->second->posted_task) {
311 return true; 272 return true;
312 } 273 }
313 } 274 }
314 return false; 275 return false;
315 } 276 }
316 277
317 std::string MediaStreamDeviceSettings::FindReadyRequestForView( 278 void MediaStreamDeviceSettings::ProcessNextRequestForView(
318 int render_view_id, int render_process_id) { 279 int render_view_id, int render_process_id) {
280 std::string new_label;
319 for (SettingsRequests::iterator it = requests_.begin(); it != requests_.end(); 281 for (SettingsRequests::iterator it = requests_.begin(); it != requests_.end();
320 ++it) { 282 ++it) {
321 if (it->second->render_process_id == render_process_id && 283 if (it->second->render_process_id == render_process_id &&
322 it->second->render_view_id == render_view_id) { 284 it->second->render_view_id == render_view_id) {
323 // This request belongs to the given render view. 285 // This request belongs to the given render view.
324 MediaStreamDeviceSettingsRequest* request = it->second; 286 MediaStreamDeviceSettingsRequest* request = it->second;
325 if (request->options.audio && 287 if (IsRequestReadyForView(request)) {
326 request->devices_full[ 288 new_label = it->first;
327 content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE].empty()) { 289 break;
328 // Audio requested, but no devices enumerated yet. Continue to next
329 // request.
330 continue;
331 } 290 }
332 if (request->options.video &&
333 request->devices_full[
334 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE].empty()) {
335 // Video requested, but no devices enumerated yet. Continue to next
336 // request.
337 continue;
338 }
339 // This request belongs to the same view as the treated request and is
340 // ready to be requested. Return its label.
341 return it->first;
342 } 291 }
343 } 292 }
344 return std::string(); 293
294 if (!new_label.empty()) {
scherkus (not reviewing) 2012/08/13 19:51:22 ditto for early return
no longer working on chromium 2012/08/14 11:17:04 Done.
295 if (use_fake_ui_)
296 PostRequestToFakeUI(new_label);
297 else
298 PostRequestToUi(new_label);
299 }
345 } 300 }
346 301
347 void MediaStreamDeviceSettings::PostRequestToUi(const std::string& label) { 302 void MediaStreamDeviceSettings::PostRequestToUi(const std::string& label) {
348 MediaStreamDeviceSettingsRequest* request = requests_[label]; 303 MediaStreamDeviceSettingsRequest* request = requests_[label];
349 DCHECK(request != NULL); 304 DCHECK(request != NULL);
350 305
351 request->posted_task = true; 306 request->posted_task = true;
352 307
353 // Create the simplified list of devices. 308 // Create the simplified list of devices.
354 for (DeviceMap::iterator it = request->devices_full.begin(); 309 for (DeviceMap::iterator it = request->devices_full.begin();
(...skipping 10 matching lines...) Expand all
365 new ResponseCallbackHelper(weak_ptr_factory_.GetWeakPtr()); 320 new ResponseCallbackHelper(weak_ptr_factory_.GetWeakPtr());
366 content::MediaResponseCallback callback = 321 content::MediaResponseCallback callback =
367 base::Bind(&ResponseCallbackHelper::PostResponse, 322 base::Bind(&ResponseCallbackHelper::PostResponse,
368 helper.get(), label); 323 helper.get(), label);
369 324
370 BrowserThread::PostTask( 325 BrowserThread::PostTask(
371 BrowserThread::UI, FROM_HERE, 326 BrowserThread::UI, FROM_HERE,
372 base::Bind(&DoDeviceRequest, *request, callback)); 327 base::Bind(&DoDeviceRequest, *request, callback));
373 } 328 }
374 329
330 void MediaStreamDeviceSettings::PostRequestToFakeUI(const std::string& label) {
331 SettingsRequests::iterator request_it = requests_.find(label);
332 DCHECK(request_it != requests_.end());
333 MediaStreamDeviceSettingsRequest* request = request_it->second;
334 // Used to fake UI, which is needed for server based testing.
335 // Choose first non-opened device for each media type.
336 content::MediaStreamDevices devices_to_use;
337 for (DeviceMap::iterator it = request->devices_full.begin();
338 it != request->devices_full.end(); ++it) {
339 StreamDeviceInfoArray::iterator device_it = it->second.begin();
340 for (; device_it != it->second.end(); ++device_it) {
341 if (!device_it->in_use) {
342 devices_to_use.push_back(content::MediaStreamDevice(
343 device_it->stream_type, device_it->device_id, device_it->name));
344 break;
345 }
346 }
347
348 if (it->second.size() != 0 && device_it == it->second.end()) {
349 // Use the first capture device in the list if all the devices are
350 // being used.
351 devices_to_use.push_back(
352 content::MediaStreamDevice(it->second.begin()->stream_type,
353 it->second.begin()->device_id,
354 it->second.begin()->name));
355 }
356 }
357
358 BrowserThread::PostTask(
359 BrowserThread::IO, FROM_HERE,
360 base::Bind(&MediaStreamDeviceSettings::PostResponse,
361 weak_ptr_factory_.GetWeakPtr(), label, devices_to_use));
362 }
363
375 } // namespace media_stream 364 } // namespace media_stream
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698