OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/media/media_capture_devices_dispatcher.h" | |
6 | |
7 #include "base/command_line.h" | |
8 #include "base/logging.h" | |
9 #include "base/metrics/field_trial.h" | |
10 #include "base/strings/string_number_conversions.h" | |
11 #include "base/strings/string_util.h" | |
12 #include "base/strings/utf_string_conversions.h" | |
13 #include "build/build_config.h" | |
14 #include "chrome/browser/media/desktop_streams_registry.h" | |
15 #include "chrome/browser/media/media_access_handler.h" | |
16 #include "chrome/browser/media/media_stream_capture_indicator.h" | |
17 #include "chrome/browser/media/permission_bubble_media_access_handler.h" | |
18 #include "chrome/browser/profiles/profile.h" | |
19 #include "chrome/browser/ui/browser.h" | |
20 #include "chrome/browser/ui/browser_window.h" | |
21 #include "chrome/common/chrome_switches.h" | |
22 #include "chrome/common/pref_names.h" | |
23 #include "components/pref_registry/pref_registry_syncable.h" | |
24 #include "components/prefs/pref_service.h" | |
25 #include "components/prefs/scoped_user_pref_update.h" | |
26 #include "content/public/browser/browser_thread.h" | |
27 #include "content/public/browser/media_capture_devices.h" | |
28 #include "content/public/browser/notification_source.h" | |
29 #include "content/public/browser/render_frame_host.h" | |
30 #include "content/public/browser/render_process_host.h" | |
31 #include "content/public/browser/web_contents.h" | |
32 #include "content/public/common/media_stream_request.h" | |
33 #include "extensions/common/constants.h" | |
34 #include "media/base/media_switches.h" | |
35 | |
36 #if defined(OS_CHROMEOS) | |
37 #include "ash/shell.h" | |
38 #endif // defined(OS_CHROMEOS) | |
39 | |
40 #if defined(ENABLE_EXTENSIONS) | |
41 #include "chrome/browser/media/desktop_capture_access_handler.h" | |
42 #include "chrome/browser/media/extension_media_access_handler.h" | |
43 #include "chrome/browser/media/tab_capture_access_handler.h" | |
44 #include "extensions/browser/extension_registry.h" | |
45 #include "extensions/common/extension.h" | |
46 #include "extensions/common/permissions/permissions_data.h" | |
47 #endif | |
48 | |
49 using content::BrowserThread; | |
50 using content::MediaCaptureDevices; | |
51 using content::MediaStreamDevices; | |
52 | |
53 namespace { | |
54 | |
55 // Finds a device in |devices| that has |device_id|, or NULL if not found. | |
56 const content::MediaStreamDevice* FindDeviceWithId( | |
57 const content::MediaStreamDevices& devices, | |
58 const std::string& device_id) { | |
59 content::MediaStreamDevices::const_iterator iter = devices.begin(); | |
60 for (; iter != devices.end(); ++iter) { | |
61 if (iter->id == device_id) { | |
62 return &(*iter); | |
63 } | |
64 } | |
65 return NULL; | |
66 } | |
67 | |
68 #if defined(ENABLE_EXTENSIONS) | |
69 inline CaptureAccessHandlerBase* ToCaptureAccessHandlerBase( | |
70 MediaAccessHandler* handler) { | |
71 return static_cast<CaptureAccessHandlerBase*>(handler); | |
72 } | |
73 #endif | |
74 } // namespace | |
75 | |
76 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() { | |
77 return base::Singleton<MediaCaptureDevicesDispatcher>::get(); | |
78 } | |
79 | |
80 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() | |
81 : is_device_enumeration_disabled_(false), | |
82 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) { | |
83 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
84 | |
85 #if defined(ENABLE_EXTENSIONS) | |
86 media_access_handlers_.push_back(new ExtensionMediaAccessHandler()); | |
87 media_access_handlers_.push_back(new DesktopCaptureAccessHandler()); | |
88 media_access_handlers_.push_back(new TabCaptureAccessHandler()); | |
89 #endif | |
90 media_access_handlers_.push_back(new PermissionBubbleMediaAccessHandler()); | |
91 } | |
92 | |
93 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {} | |
94 | |
95 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs( | |
96 user_prefs::PrefRegistrySyncable* registry) { | |
97 registry->RegisterStringPref(prefs::kDefaultAudioCaptureDevice, | |
98 std::string()); | |
99 registry->RegisterStringPref(prefs::kDefaultVideoCaptureDevice, | |
100 std::string()); | |
101 } | |
102 | |
103 bool MediaCaptureDevicesDispatcher::IsOriginForCasting(const GURL& origin) { | |
104 // Whitelisted tab casting extensions. | |
105 return | |
106 // Dev | |
107 origin.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" || | |
108 // Canary | |
109 origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" || | |
110 // Beta (internal) | |
111 origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" || | |
112 // Google Cast Beta | |
113 origin.spec() == "chrome-extension://dliochdbjfkdbacpmhlcpmleaejidimm/" || | |
114 // Google Cast Stable | |
115 origin.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/" || | |
116 // http://crbug.com/457908 | |
117 origin.spec() == "chrome-extension://ekpaaapppgpmolpcldedioblbkmijaca/" || | |
118 // http://crbug.com/574889 | |
119 origin.spec() == "chrome-extension://pkedcjkdefgpdelpbcmbmeomcjbeemfm/"; | |
120 } | |
121 | |
122 void MediaCaptureDevicesDispatcher::AddObserver(Observer* observer) { | |
123 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
124 if (!observers_.HasObserver(observer)) | |
125 observers_.AddObserver(observer); | |
126 } | |
127 | |
128 void MediaCaptureDevicesDispatcher::RemoveObserver(Observer* observer) { | |
129 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
130 observers_.RemoveObserver(observer); | |
131 } | |
132 | |
133 const MediaStreamDevices& | |
134 MediaCaptureDevicesDispatcher::GetAudioCaptureDevices() { | |
135 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
136 if (is_device_enumeration_disabled_ || !test_audio_devices_.empty()) | |
137 return test_audio_devices_; | |
138 | |
139 return MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices(); | |
140 } | |
141 | |
142 const MediaStreamDevices& | |
143 MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() { | |
144 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
145 if (is_device_enumeration_disabled_ || !test_video_devices_.empty()) | |
146 return test_video_devices_; | |
147 | |
148 return MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(); | |
149 } | |
150 | |
151 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest( | |
152 content::WebContents* web_contents, | |
153 const content::MediaStreamRequest& request, | |
154 const content::MediaResponseCallback& callback, | |
155 const extensions::Extension* extension) { | |
156 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
157 | |
158 for (MediaAccessHandler* handler : media_access_handlers_) { | |
159 if (handler->SupportsStreamType(request.video_type, extension) || | |
160 handler->SupportsStreamType(request.audio_type, extension)) { | |
161 handler->HandleRequest(web_contents, request, callback, extension); | |
162 return; | |
163 } | |
164 } | |
165 callback.Run(content::MediaStreamDevices(), | |
166 content::MEDIA_DEVICE_NOT_SUPPORTED, nullptr); | |
167 } | |
168 | |
169 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission( | |
170 content::WebContents* web_contents, | |
171 const GURL& security_origin, | |
172 content::MediaStreamType type) { | |
173 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
174 return CheckMediaAccessPermission(web_contents, security_origin, type, | |
175 nullptr); | |
176 } | |
177 | |
178 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission( | |
179 content::WebContents* web_contents, | |
180 const GURL& security_origin, | |
181 content::MediaStreamType type, | |
182 const extensions::Extension* extension) { | |
183 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
184 for (MediaAccessHandler* handler : media_access_handlers_) { | |
185 if (handler->SupportsStreamType(type, extension)) { | |
186 return handler->CheckMediaAccessPermission(web_contents, security_origin, | |
187 type, extension); | |
188 } | |
189 } | |
190 return false; | |
191 } | |
192 | |
193 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile( | |
194 Profile* profile, | |
195 bool audio, | |
196 bool video, | |
197 content::MediaStreamDevices* devices) { | |
198 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
199 DCHECK(audio || video); | |
200 | |
201 PrefService* prefs = profile->GetPrefs(); | |
202 std::string default_device; | |
203 if (audio) { | |
204 default_device = prefs->GetString(prefs::kDefaultAudioCaptureDevice); | |
205 const content::MediaStreamDevice* device = | |
206 GetRequestedAudioDevice(default_device); | |
207 if (!device) | |
208 device = GetFirstAvailableAudioDevice(); | |
209 if (device) | |
210 devices->push_back(*device); | |
211 } | |
212 | |
213 if (video) { | |
214 default_device = prefs->GetString(prefs::kDefaultVideoCaptureDevice); | |
215 const content::MediaStreamDevice* device = | |
216 GetRequestedVideoDevice(default_device); | |
217 if (!device) | |
218 device = GetFirstAvailableVideoDevice(); | |
219 if (device) | |
220 devices->push_back(*device); | |
221 } | |
222 } | |
223 | |
224 const content::MediaStreamDevice* | |
225 MediaCaptureDevicesDispatcher::GetRequestedAudioDevice( | |
226 const std::string& requested_audio_device_id) { | |
227 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
228 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices(); | |
229 const content::MediaStreamDevice* const device = | |
230 FindDeviceWithId(audio_devices, requested_audio_device_id); | |
231 return device; | |
232 } | |
233 | |
234 const content::MediaStreamDevice* | |
235 MediaCaptureDevicesDispatcher::GetFirstAvailableAudioDevice() { | |
236 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
237 const content::MediaStreamDevices& audio_devices = GetAudioCaptureDevices(); | |
238 if (audio_devices.empty()) | |
239 return NULL; | |
240 return &(*audio_devices.begin()); | |
241 } | |
242 | |
243 const content::MediaStreamDevice* | |
244 MediaCaptureDevicesDispatcher::GetRequestedVideoDevice( | |
245 const std::string& requested_video_device_id) { | |
246 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
247 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices(); | |
248 const content::MediaStreamDevice* const device = | |
249 FindDeviceWithId(video_devices, requested_video_device_id); | |
250 return device; | |
251 } | |
252 | |
253 const content::MediaStreamDevice* | |
254 MediaCaptureDevicesDispatcher::GetFirstAvailableVideoDevice() { | |
255 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
256 const content::MediaStreamDevices& video_devices = GetVideoCaptureDevices(); | |
257 if (video_devices.empty()) | |
258 return NULL; | |
259 return &(*video_devices.begin()); | |
260 } | |
261 | |
262 void MediaCaptureDevicesDispatcher::DisableDeviceEnumerationForTesting() { | |
263 is_device_enumeration_disabled_ = true; | |
264 } | |
265 | |
266 scoped_refptr<MediaStreamCaptureIndicator> | |
267 MediaCaptureDevicesDispatcher::GetMediaStreamCaptureIndicator() { | |
268 return media_stream_capture_indicator_; | |
269 } | |
270 | |
271 DesktopStreamsRegistry* | |
272 MediaCaptureDevicesDispatcher::GetDesktopStreamsRegistry() { | |
273 if (!desktop_streams_registry_) | |
274 desktop_streams_registry_.reset(new DesktopStreamsRegistry()); | |
275 return desktop_streams_registry_.get(); | |
276 } | |
277 | |
278 void MediaCaptureDevicesDispatcher::OnAudioCaptureDevicesChanged() { | |
279 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
280 BrowserThread::PostTask( | |
281 BrowserThread::UI, FROM_HERE, | |
282 base::Bind( | |
283 &MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread, | |
284 base::Unretained(this))); | |
285 } | |
286 | |
287 void MediaCaptureDevicesDispatcher::OnVideoCaptureDevicesChanged() { | |
288 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
289 BrowserThread::PostTask( | |
290 BrowserThread::UI, FROM_HERE, | |
291 base::Bind( | |
292 &MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread, | |
293 base::Unretained(this))); | |
294 } | |
295 | |
296 void MediaCaptureDevicesDispatcher::OnMediaRequestStateChanged( | |
297 int render_process_id, | |
298 int render_frame_id, | |
299 int page_request_id, | |
300 const GURL& security_origin, | |
301 content::MediaStreamType stream_type, | |
302 content::MediaRequestState state) { | |
303 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
304 BrowserThread::PostTask( | |
305 BrowserThread::UI, FROM_HERE, | |
306 base::Bind( | |
307 &MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread, | |
308 base::Unretained(this), render_process_id, render_frame_id, | |
309 page_request_id, security_origin, stream_type, state)); | |
310 } | |
311 | |
312 void MediaCaptureDevicesDispatcher::OnCreatingAudioStream( | |
313 int render_process_id, | |
314 int render_frame_id) { | |
315 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
316 BrowserThread::PostTask( | |
317 BrowserThread::UI, FROM_HERE, | |
318 base::Bind( | |
319 &MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread, | |
320 base::Unretained(this), render_process_id, render_frame_id)); | |
321 } | |
322 | |
323 void MediaCaptureDevicesDispatcher::NotifyAudioDevicesChangedOnUIThread() { | |
324 MediaStreamDevices devices = GetAudioCaptureDevices(); | |
325 FOR_EACH_OBSERVER(Observer, observers_, | |
326 OnUpdateAudioDevices(devices)); | |
327 } | |
328 | |
329 void MediaCaptureDevicesDispatcher::NotifyVideoDevicesChangedOnUIThread() { | |
330 MediaStreamDevices devices = GetVideoCaptureDevices(); | |
331 FOR_EACH_OBSERVER(Observer, observers_, | |
332 OnUpdateVideoDevices(devices)); | |
333 } | |
334 | |
335 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread( | |
336 int render_process_id, | |
337 int render_frame_id, | |
338 int page_request_id, | |
339 const GURL& security_origin, | |
340 content::MediaStreamType stream_type, | |
341 content::MediaRequestState state) { | |
342 for (MediaAccessHandler* handler : media_access_handlers_) { | |
343 if (handler->SupportsStreamType(stream_type, nullptr)) { | |
344 handler->UpdateMediaRequestState(render_process_id, render_frame_id, | |
345 page_request_id, stream_type, state); | |
346 break; | |
347 } | |
348 } | |
349 | |
350 #if defined(OS_CHROMEOS) | |
351 if (IsOriginForCasting(security_origin) && IsVideoMediaType(stream_type)) { | |
352 // Notify ash that casting state has changed. | |
353 if (state == content::MEDIA_REQUEST_STATE_DONE) { | |
354 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(true); | |
355 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) { | |
356 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(false); | |
357 } | |
358 } | |
359 #endif | |
360 | |
361 FOR_EACH_OBSERVER(Observer, observers_, | |
362 OnRequestUpdate(render_process_id, | |
363 render_frame_id, | |
364 stream_type, | |
365 state)); | |
366 } | |
367 | |
368 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread( | |
369 int render_process_id, | |
370 int render_frame_id) { | |
371 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
372 FOR_EACH_OBSERVER(Observer, observers_, | |
373 OnCreatingAudioStream(render_process_id, render_frame_id)); | |
374 } | |
375 | |
376 bool MediaCaptureDevicesDispatcher::IsInsecureCapturingInProgress( | |
377 int render_process_id, | |
378 int render_frame_id) { | |
379 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
380 #if defined(ENABLE_EXTENSIONS) | |
381 for (MediaAccessHandler* handler : media_access_handlers_) { | |
382 if (handler->SupportsStreamType(content::MEDIA_DESKTOP_VIDEO_CAPTURE, | |
383 nullptr) || | |
384 handler->SupportsStreamType(content::MEDIA_TAB_VIDEO_CAPTURE, | |
385 nullptr)) { | |
386 if (ToCaptureAccessHandlerBase(handler)->IsInsecureCapturingInProgress( | |
387 render_process_id, render_frame_id)) | |
388 return true; | |
389 } | |
390 } | |
391 #endif | |
392 return false; | |
393 } | |
394 | |
395 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices( | |
396 const MediaStreamDevices& devices) { | |
397 test_audio_devices_ = devices; | |
398 } | |
399 | |
400 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices( | |
401 const MediaStreamDevices& devices) { | |
402 test_video_devices_ = devices; | |
403 } | |
404 | |
405 void MediaCaptureDevicesDispatcher::OnSetCapturingLinkSecured( | |
406 int render_process_id, | |
407 int render_frame_id, | |
408 int page_request_id, | |
409 content::MediaStreamType stream_type, | |
410 bool is_secure) { | |
411 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
412 if (stream_type != content::MEDIA_TAB_VIDEO_CAPTURE && | |
413 stream_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) | |
414 return; | |
415 | |
416 BrowserThread::PostTask( | |
417 BrowserThread::UI, FROM_HERE, | |
418 base::Bind(&MediaCaptureDevicesDispatcher::UpdateCapturingLinkSecured, | |
419 base::Unretained(this), render_process_id, render_frame_id, | |
420 page_request_id, stream_type, is_secure)); | |
421 } | |
422 | |
423 void MediaCaptureDevicesDispatcher::UpdateCapturingLinkSecured( | |
424 int render_process_id, | |
425 int render_frame_id, | |
426 int page_request_id, | |
427 content::MediaStreamType stream_type, | |
428 bool is_secure) { | |
429 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
430 if (stream_type != content::MEDIA_TAB_VIDEO_CAPTURE && | |
431 stream_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) | |
432 return; | |
433 | |
434 #if defined(ENABLE_EXTENSIONS) | |
435 for (MediaAccessHandler* handler : media_access_handlers_) { | |
436 if (handler->SupportsStreamType(stream_type, nullptr)) { | |
437 ToCaptureAccessHandlerBase(handler)->UpdateCapturingLinkSecured( | |
438 render_process_id, render_frame_id, page_request_id, is_secure); | |
439 break; | |
440 } | |
441 } | |
442 #endif | |
443 } | |
OLD | NEW |