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

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: added dispatcher_host_unittests and addressed Perk's and Magnus' 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 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 class 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 bool IsReadyForView() {
103 if (options_.audio && devices_full_.count(
104 content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE) == 0)
105 return false;
106
107 if (options_.video && devices_full_.count(
108 content::MEDIA_STREAM_DEVICE_TYPE_VIDEO_CAPTURE) == 0)
109 return false;
wjia(left Chromium) 2012/08/10 01:23:13 combine both if above.
no longer working on chromium 2012/08/10 11:50:56 Done.
110
111 // We have got all the requested devices, it is ready if it has not
112 // been posted for UI.
113 return !posted_task_;
114 }
115
102 // Request options. 116 // Request options.
103 StreamOptions options; 117 StreamOptions options_;
104 // Map containing available devices for the requested capture types. 118 // Map containing available devices for the requested capture types.
105 DeviceMap devices_full; 119 // Note, never call devices_full_[stream_type].empty() before making sure
120 // that type of device has existed on the map, otherwise it will create an
121 // empty device entry on the map.
122 DeviceMap devices_full_;
106 // Whether or not a task was posted to make the call to 123 // Whether or not a task was posted to make the call to
107 // RequestMediaAccessPermission, to make sure that we never post twice to it. 124 // RequestMediaAccessPermission, to make sure that we never post twice to it.
108 bool posted_task; 125 bool posted_task_;
109 }; 126 };
110 127
111 namespace { 128 namespace {
112 129
113 // Sends the request to the appropriate WebContents. 130 // Sends the request to the appropriate WebContents.
114 void DoDeviceRequest( 131 void DoDeviceRequest(
115 const MediaStreamDeviceSettingsRequest& request, 132 const MediaStreamDeviceSettingsRequest& request,
116 const content::MediaResponseCallback& callback) { 133 const content::MediaResponseCallback& callback) {
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
118 135
(...skipping 22 matching lines...) Expand all
141 } 158 }
142 159
143 MediaStreamDeviceSettings::~MediaStreamDeviceSettings() { 160 MediaStreamDeviceSettings::~MediaStreamDeviceSettings() {
144 STLDeleteValues(&requests_); 161 STLDeleteValues(&requests_);
145 } 162 }
146 163
147 void MediaStreamDeviceSettings::RequestCaptureDeviceUsage( 164 void MediaStreamDeviceSettings::RequestCaptureDeviceUsage(
148 const std::string& label, int render_process_id, int render_view_id, 165 const std::string& label, int render_process_id, int render_view_id,
149 const StreamOptions& request_options, const GURL& security_origin) { 166 const StreamOptions& request_options, const GURL& security_origin) {
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
151 168 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 169
158 // Create a new request. 170 // Create a new request.
159 requests_.insert(std::make_pair(label, new MediaStreamDeviceSettingsRequest( 171 requests_.insert(std::make_pair(label, new MediaStreamDeviceSettingsRequest(
160 render_process_id, render_view_id, security_origin, request_options))); 172 render_process_id, render_view_id, security_origin, request_options)));
161 } 173 }
162 174
163 void MediaStreamDeviceSettings::RemovePendingCaptureRequest( 175 void MediaStreamDeviceSettings::RemovePendingCaptureRequest(
164 const std::string& label) { 176 const std::string& label) {
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
166
167 SettingsRequests::iterator request_it = requests_.find(label); 178 SettingsRequests::iterator request_it = requests_.find(label);
168 if (request_it != requests_.end()) { 179 if (request_it != requests_.end()) {
169 // Proceed the next pending request for the same page.
170 MediaStreamDeviceSettingsRequest* request = request_it->second; 180 MediaStreamDeviceSettingsRequest* request = request_it->second;
171 std::string new_label = FindReadyRequestForView(request->render_view_id, 181 int render_view_id = request->render_view_id;
172 request->render_process_id); 182 int render_process_id = request->render_process_id;
173 if (!new_label.empty()) { 183 bool was_posted = request->posted_task_;
174 PostRequestToUi(new_label);
175 }
176
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 // Proceed the next pending request to replace the old infobar on the same
195 // page.
196 std::string new_label = FindReadyRequestForView(render_view_id,
197 render_process_id);
198 if (!new_label.empty()) {
199 PostRequestToUi(new_label);
200 }
182 } 201 }
183 } 202 }
184 203
185 void MediaStreamDeviceSettings::AvailableDevices( 204 void MediaStreamDeviceSettings::AvailableDevices(
186 const std::string& label, 205 const std::string& label,
187 MediaStreamType stream_type, 206 MediaStreamType stream_type,
188 const StreamDeviceInfoArray& devices) { 207 const StreamDeviceInfoArray& devices) {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
190 209
191 SettingsRequests::iterator request_it = requests_.find(label); 210 SettingsRequests::iterator request_it = requests_.find(label);
192 DCHECK(request_it != requests_.end()); 211 DCHECK(request_it != requests_.end());
193
194 // Add the answer for the request. 212 // Add the answer for the request.
195 MediaStreamDeviceSettingsRequest* request = request_it->second; 213 MediaStreamDeviceSettingsRequest* request = request_it->second;
196 DCHECK_EQ(request->devices_full.count(stream_type), static_cast<size_t>(0)) << 214 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."; 215 << "This request already has a list of devices for this stream type.";
198 request->devices_full[stream_type] = devices; 216 request->devices_full_[stream_type] = devices;
199 217
200 // Check if we're done. 218 if (request->IsReadyForView()) {
201 size_t num_media_requests = 0;
202 if (request->options.audio) {
203 num_media_requests++;
204 }
205 if (request->options.video) {
206 num_media_requests++;
207 }
208
209 if (request->devices_full.size() == num_media_requests) {
210 // We have all answers needed. 219 // We have all answers needed.
211 if (!use_fake_ui_) { 220 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 221 // 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 222 // 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. 223 // 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)) { 224 if (IsUiBusy(request->render_view_id, request->render_process_id)) {
220 return; 225 return;
221 } 226 }
222 PostRequestToUi(label); 227 PostRequestToUi(label);
223 } else { 228 } else {
224 // Used to fake UI, which is needed for server based testing. 229 PostRequestToFakeUI(label);
perkj_chrome 2012/08/10 07:09:31 nit: usually you should do the short exit first. i
no longer working on chromium 2012/08/10 11:50:56 Done.
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 } 230 }
256 } 231 }
257 } 232 }
258 233
259 void MediaStreamDeviceSettings::PostResponse( 234 void MediaStreamDeviceSettings::PostResponse(
260 const std::string& label, 235 const std::string& label,
261 const content::MediaStreamDevices& devices) { 236 const content::MediaStreamDevices& devices) {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
263
264 SettingsRequests::iterator req = requests_.find(label); 238 SettingsRequests::iterator req = requests_.find(label);
265 // Return if the request has been removed. 239 // Return if the request has been removed.
266 if (req == requests_.end()) 240 if (req == requests_.end())
267 return; 241 return;
268 242
269 DCHECK(requester_); 243 DCHECK(requester_);
270 MediaStreamDeviceSettingsRequest* request = req->second; 244 scoped_ptr<MediaStreamDeviceSettingsRequest> request(req->second);
271 requests_.erase(req); 245 requests_.erase(req);
272 246
273 // Look for queued requests for the same view. If there is a pending request, 247 // Look for queued requests for the same view. If there is a pending request,
274 // post it for user approval. 248 // post it for user approval.
275 std::string new_label = FindReadyRequestForView(request->render_view_id, 249 std::string new_label = FindReadyRequestForView(request->render_view_id,
276 request->render_process_id); 250 request->render_process_id);
277 if (!new_label.empty()) { 251 if (!new_label.empty()) {
278 PostRequestToUi(new_label); 252 PostRequestToUi(new_label);
279 } 253 }
280 254
281 if (devices.size() > 0) { 255 if (devices.size() > 0) {
282 // Build a list of "full" device objects for the accepted devices. 256 // Build a list of "full" device objects for the accepted devices.
283 StreamDeviceInfoArray deviceList; 257 StreamDeviceInfoArray deviceList;
284 for (content::MediaStreamDevices::const_iterator dev = devices.begin(); 258 for (content::MediaStreamDevices::const_iterator dev = devices.begin();
285 dev != devices.end(); ++dev) { 259 dev != devices.end(); ++dev) {
286 DeviceMap::iterator subList = request->devices_full.find(dev->type); 260 DeviceMap::iterator subList = request->devices_full_.find(dev->type);
287 DCHECK(subList != request->devices_full.end()); 261 DCHECK(subList != request->devices_full_.end());
288 262
289 deviceList.push_back(*std::find_if(subList->second.begin(), 263 deviceList.push_back(*std::find_if(subList->second.begin(),
290 subList->second.end(), DeviceIdEquals(dev->device_id))); 264 subList->second.end(), DeviceIdEquals(dev->device_id)));
291 } 265 }
292 requester_->DevicesAccepted(label, deviceList); 266 requester_->DevicesAccepted(label, deviceList);
293 } else { 267 } else {
294 requester_->SettingsError(label); 268 requester_->SettingsError(label);
295 } 269 }
296 delete request;
297 } 270 }
298 271
299 void MediaStreamDeviceSettings::UseFakeUI() { 272 void MediaStreamDeviceSettings::UseFakeUI() {
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
301 use_fake_ui_ = true; 274 use_fake_ui_ = true;
302 } 275 }
303 276
304 bool MediaStreamDeviceSettings::IsUiBusy(int render_view_id, 277 bool MediaStreamDeviceSettings::IsUiBusy(int render_view_id,
305 int render_process_id) { 278 int render_process_id) {
306 for (SettingsRequests::iterator it = requests_.begin(); 279 for (SettingsRequests::iterator it = requests_.begin();
307 it != requests_.end(); ++it) { 280 it != requests_.end(); ++it) {
308 if (it->second->render_process_id == render_process_id && 281 if (it->second->render_process_id == render_process_id &&
309 it->second->render_view_id == render_view_id && 282 it->second->render_view_id == render_view_id &&
310 it->second->posted_task) { 283 it->second->posted_task_) {
wjia(left Chromium) 2012/08/10 01:23:13 this looks weird, some members have "_" while othe
no longer working on chromium 2012/08/10 11:50:56 Done.
311 return true; 284 return true;
312 } 285 }
313 } 286 }
314 return false; 287 return false;
315 } 288 }
316 289
317 std::string MediaStreamDeviceSettings::FindReadyRequestForView( 290 std::string MediaStreamDeviceSettings::FindReadyRequestForView(
318 int render_view_id, int render_process_id) { 291 int render_view_id, int render_process_id) {
319 for (SettingsRequests::iterator it = requests_.begin(); it != requests_.end(); 292 for (SettingsRequests::iterator it = requests_.begin(); it != requests_.end();
320 ++it) { 293 ++it) {
321 if (it->second->render_process_id == render_process_id && 294 if (it->second->render_process_id == render_process_id &&
322 it->second->render_view_id == render_view_id) { 295 it->second->render_view_id == render_view_id) {
323 // This request belongs to the given render view. 296 // This request belongs to the given render view.
324 MediaStreamDeviceSettingsRequest* request = it->second; 297 MediaStreamDeviceSettingsRequest* request = it->second;
325 if (request->options.audio && 298 if (request->IsReadyForView())
326 request->devices_full[ 299 return it->first;
327 content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE].empty()) {
328 // Audio requested, but no devices enumerated yet. Continue to next
329 // request.
330 continue;
331 }
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 } 300 }
343 } 301 }
344 return std::string(); 302 return std::string();
345 } 303 }
346 304
347 void MediaStreamDeviceSettings::PostRequestToUi(const std::string& label) { 305 void MediaStreamDeviceSettings::PostRequestToUi(const std::string& label) {
348 MediaStreamDeviceSettingsRequest* request = requests_[label]; 306 MediaStreamDeviceSettingsRequest* request = requests_[label];
349 DCHECK(request != NULL); 307 DCHECK(request != NULL);
350 308
351 request->posted_task = true; 309 request->posted_task_ = true;
352 310
353 // Create the simplified list of devices. 311 // Create the simplified list of devices.
354 for (DeviceMap::iterator it = request->devices_full.begin(); 312 for (DeviceMap::iterator it = request->devices_full_.begin();
355 it != request->devices_full.end(); ++it) { 313 it != request->devices_full_.end(); ++it) {
356 request->devices[it->first].clear(); 314 request->devices[it->first].clear();
357 for (StreamDeviceInfoArray::iterator device = it->second.begin(); 315 for (StreamDeviceInfoArray::iterator device = it->second.begin();
358 device != it->second.end(); ++device) { 316 device != it->second.end(); ++device) {
359 request->devices[it->first].push_back(MediaStreamDevice( 317 request->devices[it->first].push_back(MediaStreamDevice(
360 it->first, device->device_id, device->name)); 318 it->first, device->device_id, device->name));
361 } 319 }
362 } 320 }
363 321
364 scoped_refptr<ResponseCallbackHelper> helper = 322 scoped_refptr<ResponseCallbackHelper> helper =
365 new ResponseCallbackHelper(weak_ptr_factory_.GetWeakPtr()); 323 new ResponseCallbackHelper(weak_ptr_factory_.GetWeakPtr());
366 content::MediaResponseCallback callback = 324 content::MediaResponseCallback callback =
367 base::Bind(&ResponseCallbackHelper::PostResponse, 325 base::Bind(&ResponseCallbackHelper::PostResponse,
368 helper.get(), label); 326 helper.get(), label);
369 327
370 BrowserThread::PostTask( 328 BrowserThread::PostTask(
371 BrowserThread::UI, FROM_HERE, 329 BrowserThread::UI, FROM_HERE,
372 base::Bind(&DoDeviceRequest, *request, callback)); 330 base::Bind(&DoDeviceRequest, *request, callback));
373 } 331 }
374 332
333 void MediaStreamDeviceSettings::PostRequestToFakeUI(const std::string& label) {
334 SettingsRequests::iterator request_it = requests_.find(label);
335 DCHECK(request_it != requests_.end());
336 MediaStreamDeviceSettingsRequest* request = request_it->second;
337
338 // Used to fake UI, which is needed for server based testing.
339 // Choose first non-opened device for each media type.
340 content::MediaStreamDevices devices_to_use;
341 for (DeviceMap::iterator it = request->devices_full_.begin();
342 it != request->devices_full_.end(); ++it) {
wjia(left Chromium) 2012/08/10 01:23:13 indent.
no longer working on chromium 2012/08/10 11:50:56 Done.
343 StreamDeviceInfoArray::iterator device_it = it->second.begin();
344 for (; device_it != it->second.end(); ++device_it) {
345 if (!device_it->in_use) {
346 devices_to_use.push_back(content::MediaStreamDevice(
347 device_it->stream_type, device_it->device_id, device_it->name));
348 break;
349 }
350 }
351
352 if (it->second.size() != 0 &&
353 device_it == it->second.end()) {
354 // Not all requested device types were opened. This happens if all
355 // video capture devices are already opened, |in_use| isn't set for
wjia(left Chromium) 2012/08/10 01:23:13 is this comment still valid?
mflodman_chromium_OOO 2012/08/10 07:15:44 Think it should be removed. Or explained in a gene
no longer working on chromium 2012/08/10 11:50:56 Done.
356 // audio devices. Allow the first video capture device in the list to be
357 // opened for this user too.
358 devices_to_use.push_back(
359 content::MediaStreamDevice(it->second.begin()->stream_type,
360 it->second.begin()->device_id,
361 it->second.begin()->name));
362 }
363 }
364
365 // Post result and delete request.
wjia(left Chromium) 2012/08/10 01:23:13 ditto.
no longer working on chromium 2012/08/10 11:50:56 Done.
366 BrowserThread::PostTask(
367 BrowserThread::IO, FROM_HERE,
368 base::Bind(&MediaStreamDeviceSettings::PostResponse,
369 weak_ptr_factory_.GetWeakPtr(), label, devices_to_use));
wjia(left Chromium) 2012/08/10 01:23:13 is this safe? POstResponse will call PostRequestTo
no longer working on chromium 2012/08/10 11:50:56 Thanks for pointing out. we need to handle the fak
370 }
371
375 } // namespace media_stream 372 } // namespace media_stream
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698