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

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: fixed testbots. 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;
142 }
143
144 // We have got all the requested devices, it is ready if it has not
145 // been posted for UI yet.
146 return !request->posted_task;
147 }
148
133 } // namespace 149 } // namespace
134 150
135 MediaStreamDeviceSettings::MediaStreamDeviceSettings( 151 MediaStreamDeviceSettings::MediaStreamDeviceSettings(
136 SettingsRequester* requester) 152 SettingsRequester* requester)
137 : requester_(requester), 153 : requester_(requester),
138 use_fake_ui_(false), 154 use_fake_ui_(false),
139 weak_ptr_factory_(this) { 155 weak_ptr_factory_(this) {
140 DCHECK(requester_); 156 DCHECK(requester_);
141 } 157 }
142 158
143 MediaStreamDeviceSettings::~MediaStreamDeviceSettings() { 159 MediaStreamDeviceSettings::~MediaStreamDeviceSettings() {
144 STLDeleteValues(&requests_); 160 STLDeleteValues(&requests_);
145 } 161 }
146 162
147 void MediaStreamDeviceSettings::RequestCaptureDeviceUsage( 163 void MediaStreamDeviceSettings::RequestCaptureDeviceUsage(
148 const std::string& label, int render_process_id, int render_view_id, 164 const std::string& label, int render_process_id, int render_view_id,
149 const StreamOptions& request_options, const GURL& security_origin) { 165 const StreamOptions& request_options, const GURL& security_origin) {
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
151 167 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 168
158 // Create a new request. 169 // Create a new request.
159 requests_.insert(std::make_pair(label, new MediaStreamDeviceSettingsRequest( 170 requests_.insert(std::make_pair(label, new MediaStreamDeviceSettingsRequest(
160 render_process_id, render_view_id, security_origin, request_options))); 171 render_process_id, render_view_id, security_origin, request_options)));
161 } 172 }
162 173
163 void MediaStreamDeviceSettings::RemovePendingCaptureRequest( 174 void MediaStreamDeviceSettings::RemovePendingCaptureRequest(
164 const std::string& label) { 175 const std::string& label) {
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
166
167 SettingsRequests::iterator request_it = requests_.find(label); 177 SettingsRequests::iterator request_it = requests_.find(label);
168 if (request_it != requests_.end()) { 178 if (request_it != requests_.end()) {
169 // Proceed the next pending request for the same page. 179 // 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 184
177 // TODO(xians): Post a cancel request on UI thread to dismiss the infobar 185 // TODO(xians): Post a cancel request on UI thread to dismiss the infobar
178 // if request has been sent to the UI. 186 // if request has been sent to the UI.
179 // Remove the request from the queue. 187 // Remove the request from the queue.
180 requests_.erase(request_it); 188 requests_.erase(request_it);
181 delete request; 189 delete request;
190
191 // Simply return if the canceled request has not been brought to UI.
192 if (!was_posted)
193 return;
194
195 // Process the next pending request to replace the old infobar on the same
196 // page.
197 ProcessNextRequestForView(render_view_id, render_process_id);
182 } 198 }
183 } 199 }
184 200
185 void MediaStreamDeviceSettings::AvailableDevices( 201 void MediaStreamDeviceSettings::AvailableDevices(
186 const std::string& label, 202 const std::string& label,
187 MediaStreamType stream_type, 203 MediaStreamType stream_type,
188 const StreamDeviceInfoArray& devices) { 204 const StreamDeviceInfoArray& devices) {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
190 206
191 SettingsRequests::iterator request_it = requests_.find(label); 207 SettingsRequests::iterator request_it = requests_.find(label);
192 DCHECK(request_it != requests_.end()); 208 DCHECK(request_it != requests_.end());
193
194 // Add the answer for the request. 209 // Add the answer for the request.
195 MediaStreamDeviceSettingsRequest* request = request_it->second; 210 MediaStreamDeviceSettingsRequest* request = request_it->second;
196 DCHECK_EQ(request->devices_full.count(stream_type), static_cast<size_t>(0)) << 211 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."; 212 << "This request already has a list of devices for this stream type.";
198 request->devices_full[stream_type] = devices; 213 request->devices_full[stream_type] = devices;
199 214
200 // Check if we're done. 215 if (IsRequestReadyForView(request)) {
201 size_t num_media_requests = 0; 216 if (use_fake_ui_) {
202 if (request->options.audio) { 217 PostRequestToFakeUI(label);
203 num_media_requests++; 218 return;
204 } 219 }
205 if (request->options.video) {
206 num_media_requests++;
207 }
208 220
209 if (request->devices_full.size() == num_media_requests) { 221 if (IsUIBusy(request->render_view_id, request->render_process_id)) {
210 // We have all answers needed. 222 // The UI can handle only one request at the time, do not post the
211 if (!use_fake_ui_) { 223 // request to the view if the UI is handling any other request.
212 // Abort if the task was already posted: wait for it to PostResponse. 224 return;
213 if (request->posted_task) { 225 }
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);
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 226
238 if (!request->devices_full[ 227 PostRequestToUI(label);
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 }
256 } 228 }
257 } 229 }
258 230
259 void MediaStreamDeviceSettings::PostResponse( 231 void MediaStreamDeviceSettings::PostResponse(
260 const std::string& label, 232 const std::string& label,
261 const content::MediaStreamDevices& devices) { 233 const content::MediaStreamDevices& devices) {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
263
264 SettingsRequests::iterator req = requests_.find(label); 235 SettingsRequests::iterator req = requests_.find(label);
265 // Return if the request has been removed. 236 // Return if the request has been removed.
266 if (req == requests_.end()) 237 if (req == requests_.end())
267 return; 238 return;
268 239
269 DCHECK(requester_); 240 DCHECK(requester_);
270 MediaStreamDeviceSettingsRequest* request = req->second; 241 scoped_ptr<MediaStreamDeviceSettingsRequest> request(req->second);
271 requests_.erase(req); 242 requests_.erase(req);
272 243
273 // Look for queued requests for the same view. If there is a pending request, 244 // Look for queued requests for the same view. If there is a pending request,
274 // post it for user approval. 245 // post it for user approval.
275 std::string new_label = FindReadyRequestForView(request->render_view_id, 246 ProcessNextRequestForView(request->render_view_id,
276 request->render_process_id); 247 request->render_process_id);
277 if (!new_label.empty()) {
278 PostRequestToUi(new_label);
279 }
280 248
281 if (devices.size() > 0) { 249 if (devices.size() > 0) {
282 // Build a list of "full" device objects for the accepted devices. 250 // Build a list of "full" device objects for the accepted devices.
283 StreamDeviceInfoArray deviceList; 251 StreamDeviceInfoArray deviceList;
284 for (content::MediaStreamDevices::const_iterator dev = devices.begin(); 252 for (content::MediaStreamDevices::const_iterator dev = devices.begin();
285 dev != devices.end(); ++dev) { 253 dev != devices.end(); ++dev) {
286 DeviceMap::iterator subList = request->devices_full.find(dev->type); 254 DeviceMap::iterator subList = request->devices_full.find(dev->type);
287 DCHECK(subList != request->devices_full.end()); 255 DCHECK(subList != request->devices_full.end());
288 256
289 deviceList.push_back(*std::find_if(subList->second.begin(), 257 deviceList.push_back(*std::find_if(subList->second.begin(),
290 subList->second.end(), DeviceIdEquals(dev->device_id))); 258 subList->second.end(), DeviceIdEquals(dev->device_id)));
291 } 259 }
292 requester_->DevicesAccepted(label, deviceList); 260 requester_->DevicesAccepted(label, deviceList);
293 } else { 261 } else {
294 requester_->SettingsError(label); 262 requester_->SettingsError(label);
295 } 263 }
296 delete request;
297 } 264 }
298 265
299 void MediaStreamDeviceSettings::UseFakeUI() { 266 void MediaStreamDeviceSettings::UseFakeUI() {
300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
301 use_fake_ui_ = true; 268 use_fake_ui_ = true;
302 } 269 }
303 270
304 bool MediaStreamDeviceSettings::IsUiBusy(int render_view_id, 271 bool MediaStreamDeviceSettings::IsUIBusy(int render_view_id,
305 int render_process_id) { 272 int render_process_id) {
306 for (SettingsRequests::iterator it = requests_.begin(); 273 for (SettingsRequests::iterator it = requests_.begin();
307 it != requests_.end(); ++it) { 274 it != requests_.end(); ++it) {
308 if (it->second->render_process_id == render_process_id && 275 if (it->second->render_process_id == render_process_id &&
309 it->second->render_view_id == render_view_id && 276 it->second->render_view_id == render_view_id &&
310 it->second->posted_task) { 277 it->second->posted_task) {
311 return true; 278 return true;
312 } 279 }
313 } 280 }
314 return false; 281 return false;
315 } 282 }
316 283
317 std::string MediaStreamDeviceSettings::FindReadyRequestForView( 284 void MediaStreamDeviceSettings::ProcessNextRequestForView(
318 int render_view_id, int render_process_id) { 285 int render_view_id, int render_process_id) {
286 std::string new_label;
319 for (SettingsRequests::iterator it = requests_.begin(); it != requests_.end(); 287 for (SettingsRequests::iterator it = requests_.begin(); it != requests_.end();
320 ++it) { 288 ++it) {
321 if (it->second->render_process_id == render_process_id && 289 if (it->second->render_process_id == render_process_id &&
322 it->second->render_view_id == render_view_id) { 290 it->second->render_view_id == render_view_id) {
323 // This request belongs to the given render view. 291 // This request belongs to the given render view.
324 MediaStreamDeviceSettingsRequest* request = it->second; 292 MediaStreamDeviceSettingsRequest* request = it->second;
325 if (request->options.audio && 293 if (IsRequestReadyForView(request)) {
326 request->devices_full[ 294 new_label = it->first;
327 content::MEDIA_STREAM_DEVICE_TYPE_AUDIO_CAPTURE].empty()) { 295 break;
328 // Audio requested, but no devices enumerated yet. Continue to next
329 // request.
330 continue;
331 } 296 }
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 } 297 }
343 } 298 }
344 return std::string(); 299
300 if (new_label.empty())
301 return;
302
303 if (use_fake_ui_)
304 PostRequestToFakeUI(new_label);
305 else
306 PostRequestToUI(new_label);
345 } 307 }
346 308
347 void MediaStreamDeviceSettings::PostRequestToUi(const std::string& label) { 309 void MediaStreamDeviceSettings::PostRequestToUI(const std::string& label) {
348 MediaStreamDeviceSettingsRequest* request = requests_[label]; 310 MediaStreamDeviceSettingsRequest* request = requests_[label];
349 DCHECK(request != NULL); 311 DCHECK(request != NULL);
350 312
351 request->posted_task = true; 313 request->posted_task = true;
352 314
353 // Create the simplified list of devices. 315 // Create the simplified list of devices.
354 for (DeviceMap::iterator it = request->devices_full.begin(); 316 for (DeviceMap::iterator it = request->devices_full.begin();
355 it != request->devices_full.end(); ++it) { 317 it != request->devices_full.end(); ++it) {
356 request->devices[it->first].clear(); 318 request->devices[it->first].clear();
357 for (StreamDeviceInfoArray::iterator device = it->second.begin(); 319 for (StreamDeviceInfoArray::iterator device = it->second.begin();
358 device != it->second.end(); ++device) { 320 device != it->second.end(); ++device) {
359 request->devices[it->first].push_back(MediaStreamDevice( 321 request->devices[it->first].push_back(MediaStreamDevice(
360 it->first, device->device_id, device->name)); 322 it->first, device->device_id, device->name));
361 } 323 }
362 } 324 }
363 325
364 scoped_refptr<ResponseCallbackHelper> helper = 326 scoped_refptr<ResponseCallbackHelper> helper =
365 new ResponseCallbackHelper(weak_ptr_factory_.GetWeakPtr()); 327 new ResponseCallbackHelper(weak_ptr_factory_.GetWeakPtr());
366 content::MediaResponseCallback callback = 328 content::MediaResponseCallback callback =
367 base::Bind(&ResponseCallbackHelper::PostResponse, 329 base::Bind(&ResponseCallbackHelper::PostResponse,
368 helper.get(), label); 330 helper.get(), label);
369 331
370 BrowserThread::PostTask( 332 BrowserThread::PostTask(
371 BrowserThread::UI, FROM_HERE, 333 BrowserThread::UI, FROM_HERE,
372 base::Bind(&DoDeviceRequest, *request, callback)); 334 base::Bind(&DoDeviceRequest, *request, callback));
373 } 335 }
374 336
337 void MediaStreamDeviceSettings::PostRequestToFakeUI(const std::string& label) {
338 SettingsRequests::iterator request_it = requests_.find(label);
339 DCHECK(request_it != requests_.end());
340 MediaStreamDeviceSettingsRequest* request = request_it->second;
341 // Used to fake UI, which is needed for server based testing.
342 // Choose first non-opened device for each media type.
343 content::MediaStreamDevices devices_to_use;
344 for (DeviceMap::iterator it = request->devices_full.begin();
345 it != request->devices_full.end(); ++it) {
346 StreamDeviceInfoArray::iterator device_it = it->second.begin();
347 for (; device_it != it->second.end(); ++device_it) {
348 if (!device_it->in_use) {
349 devices_to_use.push_back(content::MediaStreamDevice(
350 device_it->stream_type, device_it->device_id, device_it->name));
351 break;
352 }
353 }
354
355 if (it->second.size() != 0 && device_it == it->second.end()) {
356 // Use the first capture device in the list if all the devices are
357 // being used.
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 BrowserThread::PostTask(
366 BrowserThread::IO, FROM_HERE,
367 base::Bind(&MediaStreamDeviceSettings::PostResponse,
368 weak_ptr_factory_.GetWeakPtr(), label, devices_to_use));
369 }
370
375 } // namespace media_stream 371 } // namespace media_stream
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698