OLD | NEW |
---|---|
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 "chrome/browser/media/media_capture_devices_dispatcher.h" | 5 #include "chrome/browser/media/media_capture_devices_dispatcher.h" |
6 | 6 |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/metrics/field_trial.h" | 9 #include "base/metrics/field_trial.h" |
10 #include "base/prefs/pref_service.h" | 10 #include "base/prefs/pref_service.h" |
11 #include "base/prefs/scoped_user_pref_update.h" | 11 #include "base/prefs/scoped_user_pref_update.h" |
12 #include "base/sha1.h" | 12 #include "base/sha1.h" |
13 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" |
14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
15 #include "base/strings/utf_string_conversions.h" | 15 #include "base/strings/utf_string_conversions.h" |
16 #include "chrome/browser/media/desktop_capture_access_handler.h" | |
16 #include "chrome/browser/media/desktop_streams_registry.h" | 17 #include "chrome/browser/media/desktop_streams_registry.h" |
17 #include "chrome/browser/media/media_stream_capture_indicator.h" | 18 #include "chrome/browser/media/media_stream_capture_indicator.h" |
18 #include "chrome/browser/media/media_stream_device_permissions.h" | 19 #include "chrome/browser/media/permission_bubble_media_access_handler.h" |
19 #include "chrome/browser/media/media_stream_infobar_delegate.h" | 20 #include "chrome/browser/media/tab_capture_access_handler.h" |
20 #include "chrome/browser/profiles/profile.h" | |
21 #include "chrome/browser/ui/browser.h" | 21 #include "chrome/browser/ui/browser.h" |
22 #include "chrome/browser/ui/browser_finder.h" | |
23 #include "chrome/browser/ui/browser_window.h" | 22 #include "chrome/browser/ui/browser_window.h" |
24 #include "chrome/browser/ui/screen_capture_notification_ui.h" | |
25 #include "chrome/browser/ui/simple_message_box.h" | |
26 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h" | |
27 #include "chrome/common/chrome_switches.h" | 23 #include "chrome/common/chrome_switches.h" |
28 #include "chrome/common/chrome_version_info.h" | 24 #include "chrome/common/chrome_version_info.h" |
29 #include "chrome/common/pref_names.h" | 25 #include "chrome/common/pref_names.h" |
30 #include "chrome/grit/generated_resources.h" | |
31 #include "components/content_settings/core/browser/host_content_settings_map.h" | |
32 #include "components/pref_registry/pref_registry_syncable.h" | 26 #include "components/pref_registry/pref_registry_syncable.h" |
33 #include "content/public/browser/browser_thread.h" | 27 #include "content/public/browser/browser_thread.h" |
34 #include "content/public/browser/desktop_media_id.h" | 28 #include "content/public/browser/desktop_media_id.h" |
35 #include "content/public/browser/media_capture_devices.h" | 29 #include "content/public/browser/media_capture_devices.h" |
36 #include "content/public/browser/notification_service.h" | |
37 #include "content/public/browser/notification_source.h" | 30 #include "content/public/browser/notification_source.h" |
38 #include "content/public/browser/notification_types.h" | |
39 #include "content/public/browser/render_frame_host.h" | 31 #include "content/public/browser/render_frame_host.h" |
40 #include "content/public/browser/render_process_host.h" | 32 #include "content/public/browser/render_process_host.h" |
41 #include "content/public/browser/web_contents.h" | 33 #include "content/public/browser/web_contents.h" |
42 #include "content/public/common/media_stream_request.h" | 34 #include "content/public/common/media_stream_request.h" |
43 #include "extensions/common/constants.h" | 35 #include "extensions/common/constants.h" |
44 #include "media/audio/audio_manager_base.h" | |
45 #include "media/base/media_switches.h" | 36 #include "media/base/media_switches.h" |
46 #include "net/base/net_util.h" | 37 #include "net/base/net_util.h" |
47 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h" | |
48 #include "ui/base/l10n/l10n_util.h" | |
49 | 38 |
50 #if defined(OS_CHROMEOS) | 39 #if defined(OS_CHROMEOS) |
51 #include "ash/shell.h" | 40 #include "ash/shell.h" |
52 #endif // defined(OS_CHROMEOS) | 41 #endif // defined(OS_CHROMEOS) |
53 | 42 |
54 #if defined(ENABLE_EXTENSIONS) | 43 #if defined(ENABLE_EXTENSIONS) |
55 #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" | 44 #include "chrome/browser/media/extension_media_access_handler.h" |
56 #include "extensions/browser/app_window/app_window.h" | |
57 #include "extensions/browser/app_window/app_window_registry.h" | |
58 #include "extensions/browser/extension_registry.h" | 45 #include "extensions/browser/extension_registry.h" |
59 #include "extensions/common/extension.h" | 46 #include "extensions/common/extension.h" |
60 #include "extensions/common/permissions/permissions_data.h" | 47 #include "extensions/common/permissions/permissions_data.h" |
61 #endif | 48 #endif |
62 | 49 |
63 using content::BrowserThread; | 50 using content::BrowserThread; |
64 using content::MediaCaptureDevices; | 51 using content::MediaCaptureDevices; |
65 using content::MediaStreamDevices; | 52 using content::MediaStreamDevices; |
66 | 53 |
67 namespace { | 54 namespace { |
68 | 55 |
69 // A finch experiment to enable the permission bubble for media requests only. | |
70 bool MediaStreamPermissionBubbleExperimentEnabled() { | |
71 const std::string group = | |
72 base::FieldTrialList::FindFullName("MediaStreamPermissionBubble"); | |
73 if (group == "enabled") | |
74 return true; | |
75 | |
76 return false; | |
77 } | |
78 | |
79 // Finds a device in |devices| that has |device_id|, or NULL if not found. | 56 // Finds a device in |devices| that has |device_id|, or NULL if not found. |
80 const content::MediaStreamDevice* FindDeviceWithId( | 57 const content::MediaStreamDevice* FindDeviceWithId( |
81 const content::MediaStreamDevices& devices, | 58 const content::MediaStreamDevices& devices, |
82 const std::string& device_id) { | 59 const std::string& device_id) { |
83 content::MediaStreamDevices::const_iterator iter = devices.begin(); | 60 content::MediaStreamDevices::const_iterator iter = devices.begin(); |
84 for (; iter != devices.end(); ++iter) { | 61 for (; iter != devices.end(); ++iter) { |
85 if (iter->id == device_id) { | 62 if (iter->id == device_id) { |
86 return &(*iter); | 63 return &(*iter); |
87 } | 64 } |
88 } | 65 } |
89 return NULL; | 66 return NULL; |
90 } | 67 } |
91 | 68 |
92 #if defined(ENABLE_EXTENSIONS) | 69 inline DesktopCaptureAccessHandler* ToDesktopCaptureAccessHandler( |
93 // This is a short-term solution to grant camera and/or microphone access to | 70 MediaAccessHandler* handler) { |
94 // extensions: | 71 return static_cast<DesktopCaptureAccessHandler*>(handler); |
95 // 1. Virtual keyboard extension. | |
96 // 2. Flutter gesture recognition extension. | |
97 // 3. TODO(smus): Airbender experiment 1. | |
98 // 4. TODO(smus): Airbender experiment 2. | |
99 // 5. Hotwording component extension. | |
100 // 6. XKB input method component extension. | |
101 // 7. M17n/T13n/CJK input method component extension. | |
102 // Once http://crbug.com/292856 is fixed, remove this whitelist. | |
103 bool IsMediaRequestWhitelistedForExtension( | |
104 const extensions::Extension* extension) { | |
105 return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" || | |
106 extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" || | |
107 extension->id() == "clffjmdilanldobdnedchkdbofoimcgb" || | |
108 extension->id() == "nnckehldicaciogcbchegobnafnjkcne" || | |
109 extension->id() == "nbpagnldghgfoolbancepceaanlmhfmd" || | |
110 extension->id() == "jkghodnilhceideoidjikpgommlajknk" || | |
111 extension->id() == "gjaehgfemfahhmlgpdfknkhdnemmolop"; | |
112 } | 72 } |
113 | |
114 bool IsBuiltInExtension(const GURL& origin) { | |
115 return | |
116 // Feedback Extension. | |
117 origin.spec() == "chrome-extension://gfdkimpbcpahaombhbimeihdjnejgicl/"; | |
118 } | |
119 | |
120 // Returns true of the security origin is associated with casting. | |
121 bool IsOriginForCasting(const GURL& origin) { | |
122 // Whitelisted tab casting extensions. | |
123 return | |
124 // Dev | |
125 origin.spec() == "chrome-extension://enhhojjnijigcajfphajepfemndkmdlo/" || | |
126 // Canary | |
127 origin.spec() == "chrome-extension://hfaagokkkhdbgiakmmlclaapfelnkoah/" || | |
128 // Beta (internal) | |
129 origin.spec() == "chrome-extension://fmfcbgogabcbclcofgocippekhfcmgfj/" || | |
130 // Google Cast Beta | |
131 origin.spec() == "chrome-extension://dliochdbjfkdbacpmhlcpmleaejidimm/" || | |
132 // Google Cast Stable | |
133 origin.spec() == "chrome-extension://boadgeojelhgndaghljhdicfkmllpafd/" || | |
134 // http://crbug.com/457908 | |
135 origin.spec() == "chrome-extension://ekpaaapppgpmolpcldedioblbkmijaca/" || | |
136 origin.spec() == "chrome-extension://fjhoaacokmgbjemoflkofnenfaiekifl/"; | |
137 } | |
138 | |
139 bool IsExtensionWhitelistedForScreenCapture( | |
140 const extensions::Extension* extension) { | |
141 #if defined(OS_CHROMEOS) | |
142 std::string hash = base::SHA1HashString(extension->id()); | |
143 std::string hex_hash = base::HexEncode(hash.c_str(), hash.length()); | |
144 | |
145 // crbug.com/446688 | |
146 return hex_hash == "4F25792AF1AA7483936DE29C07806F203C7170A0" || | |
147 hex_hash == "BD8781D757D830FC2E85470A1B6E8A718B7EE0D9" || | |
148 hex_hash == "4AC2B6C63C6480D150DFDA13E4A5956EB1D0DDBB" || | |
149 hex_hash == "81986D4F846CEDDDB962643FA501D1780DD441BB"; | |
150 #else | |
151 return false; | |
152 #endif // defined(OS_CHROMEOS) | |
153 } | |
154 #endif // defined(ENABLE_EXTENSIONS) | |
155 | |
156 // Helper to get title of the calling application shown in the screen capture | |
157 // notification. | |
158 base::string16 GetApplicationTitle(content::WebContents* web_contents, | |
159 const extensions::Extension* extension) { | |
160 // Use extension name as title for extensions and host/origin for drive-by | |
161 // web. | |
162 std::string title; | |
163 #if defined(ENABLE_EXTENSIONS) | |
164 if (extension) { | |
165 title = extension->name(); | |
166 return base::UTF8ToUTF16(title); | |
167 } | |
168 #endif | |
169 GURL url = web_contents->GetURL(); | |
170 title = url.SchemeIsSecure() ? net::GetHostAndOptionalPort(url) | |
171 : url.GetOrigin().spec(); | |
172 return base::UTF8ToUTF16(title); | |
173 } | |
174 | |
175 // Helper to get list of media stream devices for desktop capture in |devices|. | |
176 // Registers to display notification if |display_notification| is true. | |
177 // Returns an instance of MediaStreamUI to be passed to content layer. | |
178 scoped_ptr<content::MediaStreamUI> GetDevicesForDesktopCapture( | |
179 content::MediaStreamDevices* devices, | |
180 content::DesktopMediaID media_id, | |
181 bool capture_audio, | |
182 bool display_notification, | |
183 const base::string16& application_title, | |
184 const base::string16& registered_extension_name) { | |
185 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
186 scoped_ptr<content::MediaStreamUI> ui; | |
187 | |
188 // Add selected desktop source to the list. | |
189 devices->push_back(content::MediaStreamDevice( | |
190 content::MEDIA_DESKTOP_VIDEO_CAPTURE, media_id.ToString(), "Screen")); | |
191 if (capture_audio) { | |
192 // Use the special loopback device ID for system audio capture. | |
193 devices->push_back(content::MediaStreamDevice( | |
194 content::MEDIA_DESKTOP_AUDIO_CAPTURE, | |
195 media::AudioManagerBase::kLoopbackInputDeviceId, "System Audio")); | |
196 } | |
197 | |
198 // If required, register to display the notification for stream capture. | |
199 if (display_notification) { | |
200 if (application_title == registered_extension_name) { | |
201 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16( | |
202 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT, | |
203 application_title)); | |
204 } else { | |
205 ui = ScreenCaptureNotificationUI::Create(l10n_util::GetStringFUTF16( | |
206 IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_TEXT_DELEGATED, | |
207 registered_extension_name, | |
208 application_title)); | |
209 } | |
210 } | |
211 | |
212 return ui.Pass(); | |
213 } | |
214 | |
215 #if !defined(OS_ANDROID) | |
216 // Find browser or app window from a given |web_contents|. | |
217 gfx::NativeWindow FindParentWindowForWebContents( | |
218 content::WebContents* web_contents) { | |
219 Browser* browser = chrome::FindBrowserWithWebContents(web_contents); | |
220 if (browser && browser->window()) | |
221 return browser->window()->GetNativeWindow(); | |
222 | |
223 const extensions::AppWindowRegistry::AppWindowList& window_list = | |
224 extensions::AppWindowRegistry::Get( | |
225 web_contents->GetBrowserContext())->app_windows(); | |
226 for (extensions::AppWindowRegistry::AppWindowList::const_iterator iter = | |
227 window_list.begin(); | |
228 iter != window_list.end(); ++iter) { | |
229 if ((*iter)->web_contents() == web_contents) | |
230 return (*iter)->GetNativeWindow(); | |
231 } | |
232 | |
233 return NULL; | |
234 } | |
235 #endif | |
236 | |
237 } // namespace | 73 } // namespace |
238 | 74 |
239 MediaCaptureDevicesDispatcher::PendingAccessRequest::PendingAccessRequest( | |
240 const content::MediaStreamRequest& request, | |
241 const content::MediaResponseCallback& callback) | |
242 : request(request), | |
243 callback(callback) { | |
244 } | |
245 | |
246 MediaCaptureDevicesDispatcher::PendingAccessRequest::~PendingAccessRequest() {} | |
247 | |
248 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() { | 75 MediaCaptureDevicesDispatcher* MediaCaptureDevicesDispatcher::GetInstance() { |
249 return Singleton<MediaCaptureDevicesDispatcher>::get(); | 76 return Singleton<MediaCaptureDevicesDispatcher>::get(); |
250 } | 77 } |
251 | 78 |
252 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() | 79 MediaCaptureDevicesDispatcher::MediaCaptureDevicesDispatcher() |
253 : is_device_enumeration_disabled_(false), | 80 : is_device_enumeration_disabled_(false), |
254 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) { | 81 media_stream_capture_indicator_(new MediaStreamCaptureIndicator()) { |
255 // MediaCaptureDevicesDispatcher is a singleton. It should be created on | |
256 // UI thread. Otherwise, it will not receive | |
257 // content::NOTIFICATION_WEB_CONTENTS_DESTROYED, and that will result in | |
258 // possible use after free. | |
259 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 82 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
260 notifications_registrar_.Add( | |
261 this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | |
262 content::NotificationService::AllSources()); | |
263 | 83 |
264 #if defined(OS_MACOSX) | 84 #if defined(OS_MACOSX) |
265 // AVFoundation is used for video/audio device monitoring and video capture. | 85 // AVFoundation is used for video/audio device monitoring and video capture. |
266 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( | 86 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( |
267 switches::kForceQTKit)) { | 87 switches::kForceQTKit)) { |
268 base::CommandLine::ForCurrentProcess()->AppendSwitch( | 88 base::CommandLine::ForCurrentProcess()->AppendSwitch( |
269 switches::kEnableAVFoundation); | 89 switches::kEnableAVFoundation); |
270 } | 90 } |
271 #endif | 91 #endif |
92 | |
93 #if defined(ENABLE_EXTENSIONS) | |
Sergey Ulanov
2015/06/01 23:39:17
Only PermissionBubbleMediaAccessHandler should be
changbin
2015/06/02 14:20:09
I was wrongly thought desktop capturing should als
| |
94 media_access_handlers_.push_back(new ExtensionMediaAccessHandler()); | |
95 #endif | |
96 media_access_handlers_.push_back(new DesktopCaptureAccessHandler()); | |
97 media_access_handlers_.push_back(new TabCaptureAccessHandler()); | |
98 media_access_handlers_.push_back(new PermissionBubbleMediaAccessHandler()); | |
272 } | 99 } |
273 | 100 |
274 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {} | 101 MediaCaptureDevicesDispatcher::~MediaCaptureDevicesDispatcher() {} |
275 | 102 |
276 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs( | 103 void MediaCaptureDevicesDispatcher::RegisterProfilePrefs( |
277 user_prefs::PrefRegistrySyncable* registry) { | 104 user_prefs::PrefRegistrySyncable* registry) { |
278 registry->RegisterStringPref(prefs::kDefaultAudioCaptureDevice, | 105 registry->RegisterStringPref(prefs::kDefaultAudioCaptureDevice, |
279 std::string()); | 106 std::string()); |
280 registry->RegisterStringPref(prefs::kDefaultVideoCaptureDevice, | 107 registry->RegisterStringPref(prefs::kDefaultVideoCaptureDevice, |
281 std::string()); | 108 std::string()); |
(...skipping 21 matching lines...) Expand all Loading... | |
303 | 130 |
304 const MediaStreamDevices& | 131 const MediaStreamDevices& |
305 MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() { | 132 MediaCaptureDevicesDispatcher::GetVideoCaptureDevices() { |
306 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 133 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
307 if (is_device_enumeration_disabled_ || !test_video_devices_.empty()) | 134 if (is_device_enumeration_disabled_ || !test_video_devices_.empty()) |
308 return test_video_devices_; | 135 return test_video_devices_; |
309 | 136 |
310 return MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(); | 137 return MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices(); |
311 } | 138 } |
312 | 139 |
313 void MediaCaptureDevicesDispatcher::Observe( | |
314 int type, | |
315 const content::NotificationSource& source, | |
316 const content::NotificationDetails& details) { | |
317 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
318 if (type == content::NOTIFICATION_WEB_CONTENTS_DESTROYED) { | |
319 content::WebContents* web_contents = | |
320 content::Source<content::WebContents>(source).ptr(); | |
321 pending_requests_.erase(web_contents); | |
322 } | |
323 } | |
324 | |
325 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest( | 140 void MediaCaptureDevicesDispatcher::ProcessMediaAccessRequest( |
326 content::WebContents* web_contents, | 141 content::WebContents* web_contents, |
327 const content::MediaStreamRequest& request, | 142 const content::MediaStreamRequest& request, |
328 const content::MediaResponseCallback& callback, | 143 const content::MediaResponseCallback& callback, |
329 const extensions::Extension* extension) { | 144 const extensions::Extension* extension) { |
330 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 145 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
331 | 146 |
332 if (request.video_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE || | 147 for (MediaAccessHandler* handler : media_access_handlers_) { |
333 request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE) { | 148 if (handler->SupportsStreamType(request.video_type, extension) || |
334 ProcessDesktopCaptureAccessRequest( | 149 handler->SupportsStreamType(request.audio_type, extension)) { |
335 web_contents, request, callback, extension); | 150 handler->HandleRequest(web_contents, request, callback, extension); |
Sergey Ulanov
2015/06/01 23:39:17
Return after this call. You don't want more than o
changbin
2015/06/02 14:20:09
Thanks, will remove.
| |
336 } else if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE || | |
337 request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) { | |
338 ProcessTabCaptureAccessRequest( | |
339 web_contents, request, callback, extension); | |
340 } else { | |
341 #if defined(ENABLE_EXTENSIONS) | |
342 bool is_whitelisted = | |
343 extension && (extension->is_platform_app() || | |
344 IsMediaRequestWhitelistedForExtension(extension)); | |
345 if (is_whitelisted) { | |
346 // For extensions access is approved based on extension permissions. | |
347 ProcessMediaAccessRequestFromPlatformAppOrExtension( | |
348 web_contents, request, callback, extension); | |
349 return; | |
350 } | 151 } |
351 #endif | |
352 ProcessRegularMediaAccessRequest(web_contents, request, callback); | |
353 } | 152 } |
Sergey Ulanov
2015/06/01 23:39:17
Reject the request if there is no appropriate hand
| |
354 } | 153 } |
355 | 154 |
356 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission( | 155 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission( |
357 content::WebContents* web_contents, | 156 content::WebContents* web_contents, |
358 const GURL& security_origin, | 157 const GURL& security_origin, |
359 content::MediaStreamType type) { | 158 content::MediaStreamType type) { |
360 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 159 return CheckMediaAccessPermission(web_contents, security_origin, type, NULL); |
Sergey Ulanov
2015/06/01 23:39:17
s/NULL/nullptr/
Sergey Ulanov
2015/06/01 23:39:17
Please keep DCHECK_CURRENTLY_ON(BrowserThread::UI)
| |
361 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE || | |
362 type == content::MEDIA_DEVICE_VIDEO_CAPTURE); | |
363 | |
364 Profile* profile = | |
365 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
366 | |
367 ContentSettingsType contentSettingsType = | |
368 type == content::MEDIA_DEVICE_AUDIO_CAPTURE | |
369 ? CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC | |
370 : CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA; | |
371 | |
372 if (CheckAllowAllMediaStreamContentForOrigin( | |
373 profile, security_origin, contentSettingsType)) { | |
374 return true; | |
375 } | |
376 | |
377 const char* policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE | |
378 ? prefs::kAudioCaptureAllowed | |
379 : prefs::kVideoCaptureAllowed; | |
380 const char* list_policy_name = type == content::MEDIA_DEVICE_AUDIO_CAPTURE | |
381 ? prefs::kAudioCaptureAllowedUrls | |
382 : prefs::kVideoCaptureAllowedUrls; | |
383 if (GetDevicePolicy( | |
384 profile, security_origin, policy_name, list_policy_name) == | |
385 ALWAYS_ALLOW) { | |
386 return true; | |
387 } | |
388 | |
389 // There's no secondary URL for these content types, hence duplicating | |
390 // |security_origin|. | |
391 if (profile->GetHostContentSettingsMap()->GetContentSetting( | |
392 security_origin, | |
393 security_origin, | |
394 contentSettingsType, | |
395 content_settings::ResourceIdentifier()) == CONTENT_SETTING_ALLOW) { | |
396 return true; | |
397 } | |
398 | |
399 return false; | |
400 } | 160 } |
401 | 161 |
402 #if defined(ENABLE_EXTENSIONS) | |
403 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission( | 162 bool MediaCaptureDevicesDispatcher::CheckMediaAccessPermission( |
404 content::WebContents* web_contents, | 163 content::WebContents* web_contents, |
405 const GURL& security_origin, | 164 const GURL& security_origin, |
406 content::MediaStreamType type, | 165 content::MediaStreamType type, |
407 const extensions::Extension* extension) { | 166 const extensions::Extension* extension) { |
408 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 167 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
409 DCHECK(type == content::MEDIA_DEVICE_AUDIO_CAPTURE || | 168 for (MediaAccessHandler* handler : media_access_handlers_) { |
410 type == content::MEDIA_DEVICE_VIDEO_CAPTURE); | 169 if (handler->SupportsStreamType(type, extension)) { |
411 | 170 return handler->CheckMediaAccessPermission(web_contents, security_origin, |
412 if (extension->is_platform_app() || | 171 type, extension); |
413 IsMediaRequestWhitelistedForExtension(extension)) { | |
414 return extension->permissions_data()->HasAPIPermission( | |
415 type == content::MEDIA_DEVICE_AUDIO_CAPTURE | |
416 ? extensions::APIPermission::kAudioCapture | |
417 : extensions::APIPermission::kVideoCapture); | |
418 } | |
419 | |
420 return CheckMediaAccessPermission(web_contents, security_origin, type); | |
421 } | |
422 #endif | |
423 | |
424 void MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest( | |
425 content::WebContents* web_contents, | |
426 const content::MediaStreamRequest& request, | |
427 const content::MediaResponseCallback& callback, | |
428 const extensions::Extension* extension) { | |
429 content::MediaStreamDevices devices; | |
430 scoped_ptr<content::MediaStreamUI> ui; | |
431 | |
432 if (request.video_type != content::MEDIA_DESKTOP_VIDEO_CAPTURE) { | |
433 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass()); | |
434 return; | |
435 } | |
436 | |
437 // If the device id wasn't specified then this is a screen capture request | |
438 // (i.e. chooseDesktopMedia() API wasn't used to generate device id). | |
439 if (request.requested_video_device_id.empty()) { | |
440 ProcessScreenCaptureAccessRequest( | |
441 web_contents, request, callback, extension); | |
442 return; | |
443 } | |
444 | |
445 // The extension name that the stream is registered with. | |
446 std::string original_extension_name; | |
447 // Resolve DesktopMediaID for the specified device id. | |
448 content::DesktopMediaID media_id; | |
449 // TODO(miu): Replace "main RenderFrame" IDs with the request's actual | |
450 // RenderFrame IDs once the desktop capture extension API implementation is | |
451 // fixed. http://crbug.com/304341 | |
452 content::WebContents* const web_contents_for_stream = | |
453 content::WebContents::FromRenderFrameHost( | |
454 content::RenderFrameHost::FromID(request.render_process_id, | |
455 request.render_frame_id)); | |
456 content::RenderFrameHost* const main_frame = web_contents_for_stream ? | |
457 web_contents_for_stream->GetMainFrame() : NULL; | |
458 if (main_frame) { | |
459 media_id = GetDesktopStreamsRegistry()->RequestMediaForStreamId( | |
460 request.requested_video_device_id, | |
461 main_frame->GetProcess()->GetID(), | |
462 main_frame->GetRoutingID(), | |
463 request.security_origin, | |
464 &original_extension_name); | |
465 } | |
466 | |
467 // Received invalid device id. | |
468 if (media_id.type == content::DesktopMediaID::TYPE_NONE) { | |
469 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass()); | |
470 return; | |
471 } | |
472 | |
473 bool loopback_audio_supported = false; | |
474 #if defined(USE_CRAS) || defined(OS_WIN) | |
475 // Currently loopback audio capture is supported only on Windows and ChromeOS. | |
476 loopback_audio_supported = true; | |
477 #endif | |
478 | |
479 // Audio is only supported for screen capture streams. | |
480 bool capture_audio = | |
481 (media_id.type == content::DesktopMediaID::TYPE_SCREEN && | |
482 request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE && | |
483 loopback_audio_supported); | |
484 | |
485 ui = GetDevicesForDesktopCapture( | |
486 &devices, media_id, capture_audio, true, | |
487 GetApplicationTitle(web_contents, extension), | |
488 base::UTF8ToUTF16(original_extension_name)); | |
489 | |
490 callback.Run(devices, content::MEDIA_DEVICE_OK, ui.Pass()); | |
491 } | |
492 | |
493 void MediaCaptureDevicesDispatcher::ProcessScreenCaptureAccessRequest( | |
494 content::WebContents* web_contents, | |
495 const content::MediaStreamRequest& request, | |
496 const content::MediaResponseCallback& callback, | |
497 const extensions::Extension* extension) { | |
498 content::MediaStreamDevices devices; | |
499 scoped_ptr<content::MediaStreamUI> ui; | |
500 | |
501 DCHECK_EQ(request.video_type, content::MEDIA_DESKTOP_VIDEO_CAPTURE); | |
502 | |
503 bool loopback_audio_supported = false; | |
504 #if defined(USE_CRAS) || defined(OS_WIN) | |
505 // Currently loopback audio capture is supported only on Windows and ChromeOS. | |
506 loopback_audio_supported = true; | |
507 #endif | |
508 | |
509 bool component_extension = false; | |
510 #if defined(ENABLE_EXTENSIONS) | |
511 component_extension = | |
512 extension && extension->location() == extensions::Manifest::COMPONENT; | |
513 #endif | |
514 | |
515 bool screen_capture_enabled = | |
516 base::CommandLine::ForCurrentProcess()->HasSwitch( | |
517 switches::kEnableUserMediaScreenCapturing); | |
518 #if defined(ENABLE_EXTENSIONS) | |
519 screen_capture_enabled |= | |
520 IsOriginForCasting(request.security_origin) || | |
521 IsExtensionWhitelistedForScreenCapture(extension) || | |
522 IsBuiltInExtension(request.security_origin); | |
523 #endif | |
524 | |
525 const bool origin_is_secure = | |
526 request.security_origin.SchemeIsSecure() || | |
527 request.security_origin.SchemeIs(extensions::kExtensionScheme) || | |
528 base::CommandLine::ForCurrentProcess()->HasSwitch( | |
529 switches::kAllowHttpScreenCapture); | |
530 | |
531 // If basic conditions (screen capturing is enabled and origin is secure) | |
532 // aren't fulfilled, we'll use "invalid state" as result. Otherwise, we set | |
533 // it after checking permission. | |
534 // TODO(grunell): It would be good to change this result for something else, | |
535 // probably a new one. | |
536 content::MediaStreamRequestResult result = | |
537 content::MEDIA_DEVICE_INVALID_STATE; | |
538 | |
539 // Approve request only when the following conditions are met: | |
540 // 1. Screen capturing is enabled via command line switch or white-listed for | |
541 // the given origin. | |
542 // 2. Request comes from a page with a secure origin or from an extension. | |
543 if (screen_capture_enabled && origin_is_secure) { | |
544 // Get title of the calling application prior to showing the message box. | |
545 // chrome::ShowMessageBox() starts a nested message loop which may allow | |
546 // |web_contents| to be destroyed on the UI thread before the message box | |
547 // is closed. See http://crbug.com/326690. | |
548 base::string16 application_title = | |
549 GetApplicationTitle(web_contents, extension); | |
550 #if !defined(OS_ANDROID) | |
551 gfx::NativeWindow parent_window = | |
552 FindParentWindowForWebContents(web_contents); | |
553 #else | |
554 gfx::NativeWindow parent_window = NULL; | |
555 #endif | |
556 web_contents = NULL; | |
557 | |
558 bool whitelisted_extension = false; | |
559 #if defined(ENABLE_EXTENSIONS) | |
560 whitelisted_extension = IsExtensionWhitelistedForScreenCapture( | |
561 extension); | |
562 #endif | |
563 | |
564 // For whitelisted or component extensions, bypass message box. | |
565 bool user_approved = false; | |
566 if (!whitelisted_extension && !component_extension) { | |
567 base::string16 application_name = | |
568 base::UTF8ToUTF16(request.security_origin.spec()); | |
569 #if defined(ENABLE_EXTENSIONS) | |
570 if (extension) | |
571 application_name = base::UTF8ToUTF16(extension->name()); | |
572 #endif | |
573 base::string16 confirmation_text = l10n_util::GetStringFUTF16( | |
574 request.audio_type == content::MEDIA_NO_SERVICE ? | |
575 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TEXT : | |
576 IDS_MEDIA_SCREEN_AND_AUDIO_CAPTURE_CONFIRMATION_TEXT, | |
577 application_name); | |
578 chrome::MessageBoxResult result = chrome::ShowMessageBox( | |
579 parent_window, | |
580 l10n_util::GetStringFUTF16( | |
581 IDS_MEDIA_SCREEN_CAPTURE_CONFIRMATION_TITLE, application_name), | |
582 confirmation_text, | |
583 chrome::MESSAGE_BOX_TYPE_QUESTION); | |
584 user_approved = (result == chrome::MESSAGE_BOX_RESULT_YES); | |
585 } | |
586 | |
587 if (user_approved || component_extension || whitelisted_extension) { | |
588 content::DesktopMediaID screen_id; | |
589 #if defined(OS_CHROMEOS) | |
590 screen_id = content::DesktopMediaID::RegisterAuraWindow( | |
591 ash::Shell::GetInstance()->GetPrimaryRootWindow()); | |
592 #else // defined(OS_CHROMEOS) | |
593 screen_id = | |
594 content::DesktopMediaID(content::DesktopMediaID::TYPE_SCREEN, | |
595 webrtc::kFullDesktopScreenId); | |
596 #endif // !defined(OS_CHROMEOS) | |
597 | |
598 bool capture_audio = | |
599 (request.audio_type == content::MEDIA_DESKTOP_AUDIO_CAPTURE && | |
600 loopback_audio_supported); | |
601 | |
602 // Unless we're being invoked from a component extension, register to | |
603 // display the notification for stream capture. | |
604 bool display_notification = !component_extension; | |
605 | |
606 ui = GetDevicesForDesktopCapture(&devices, screen_id, capture_audio, | |
607 display_notification, application_title, | |
608 application_title); | |
609 DCHECK(!devices.empty()); | |
610 } | |
611 | |
612 // The only case when devices can be empty is if the user has denied | |
613 // permission. | |
614 result = devices.empty() ? content::MEDIA_DEVICE_PERMISSION_DENIED | |
615 : content::MEDIA_DEVICE_OK; | |
616 } | |
617 | |
618 callback.Run(devices, result, ui.Pass()); | |
619 } | |
620 | |
621 void MediaCaptureDevicesDispatcher::ProcessTabCaptureAccessRequest( | |
622 content::WebContents* web_contents, | |
623 const content::MediaStreamRequest& request, | |
624 const content::MediaResponseCallback& callback, | |
625 const extensions::Extension* extension) { | |
626 content::MediaStreamDevices devices; | |
627 scoped_ptr<content::MediaStreamUI> ui; | |
628 | |
629 #if defined(ENABLE_EXTENSIONS) | |
630 Profile* profile = | |
631 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
632 extensions::TabCaptureRegistry* tab_capture_registry = | |
633 extensions::TabCaptureRegistry::Get(profile); | |
634 if (!tab_capture_registry) { | |
635 NOTREACHED(); | |
636 callback.Run(devices, content::MEDIA_DEVICE_INVALID_STATE, ui.Pass()); | |
637 return; | |
638 } | |
639 const bool tab_capture_allowed = tab_capture_registry->VerifyRequest( | |
640 request.render_process_id, request.render_frame_id, extension->id()); | |
641 | |
642 if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE && | |
643 tab_capture_allowed && | |
644 extension->permissions_data()->HasAPIPermission( | |
645 extensions::APIPermission::kTabCapture)) { | |
646 devices.push_back(content::MediaStreamDevice( | |
647 content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string())); | |
648 } | |
649 | |
650 if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE && | |
651 tab_capture_allowed && | |
652 extension->permissions_data()->HasAPIPermission( | |
653 extensions::APIPermission::kTabCapture)) { | |
654 devices.push_back(content::MediaStreamDevice( | |
655 content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string())); | |
656 } | |
657 | |
658 if (!devices.empty()) { | |
659 ui = media_stream_capture_indicator_->RegisterMediaStream( | |
660 web_contents, devices); | |
661 } | |
662 callback.Run( | |
663 devices, | |
664 devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE : | |
665 content::MEDIA_DEVICE_OK, | |
666 ui.Pass()); | |
667 #else // defined(ENABLE_EXTENSIONS) | |
668 callback.Run(devices, content::MEDIA_DEVICE_TAB_CAPTURE_FAILURE, ui.Pass()); | |
669 #endif // defined(ENABLE_EXTENSIONS) | |
670 } | |
671 | |
672 #if defined(ENABLE_EXTENSIONS) | |
673 void MediaCaptureDevicesDispatcher:: | |
674 ProcessMediaAccessRequestFromPlatformAppOrExtension( | |
675 content::WebContents* web_contents, | |
676 const content::MediaStreamRequest& request, | |
677 const content::MediaResponseCallback& callback, | |
678 const extensions::Extension* extension) { | |
679 // TODO(vrk): This code is largely duplicated in | |
680 // MediaStreamDevicesController::Accept(). Move this code into a shared method | |
681 // between the two classes. | |
682 | |
683 Profile* profile = | |
684 Profile::FromBrowserContext(web_contents->GetBrowserContext()); | |
685 | |
686 bool audio_allowed = | |
687 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE && | |
688 extension->permissions_data()->HasAPIPermission( | |
689 extensions::APIPermission::kAudioCapture) && | |
690 GetDevicePolicy(profile, extension->url(), | |
691 prefs::kAudioCaptureAllowed, | |
692 prefs::kAudioCaptureAllowedUrls) != ALWAYS_DENY; | |
693 bool video_allowed = | |
694 request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE && | |
695 extension->permissions_data()->HasAPIPermission( | |
696 extensions::APIPermission::kVideoCapture) && | |
697 GetDevicePolicy(profile, extension->url(), | |
698 prefs::kVideoCaptureAllowed, | |
699 prefs::kVideoCaptureAllowedUrls) != ALWAYS_DENY; | |
700 | |
701 bool get_default_audio_device = audio_allowed; | |
702 bool get_default_video_device = video_allowed; | |
703 | |
704 content::MediaStreamDevices devices; | |
705 | |
706 // Set an initial error result. If neither audio or video is allowed, we'll | |
707 // never try to get any device below but will just create |ui| and return an | |
708 // empty list with "invalid state" result. If at least one is allowed, we'll | |
709 // try to get device(s), and if failure, we want to return "no hardware" | |
710 // result. | |
711 // TODO(grunell): The invalid state result should be changed to a new denied | |
712 // result + a dcheck to ensure at least one of audio or video types is | |
713 // capture. | |
714 content::MediaStreamRequestResult result = | |
715 (audio_allowed || video_allowed) ? content::MEDIA_DEVICE_NO_HARDWARE | |
716 : content::MEDIA_DEVICE_INVALID_STATE; | |
717 | |
718 // Get the exact audio or video device if an id is specified. | |
719 // We only set any error result here and before running the callback change | |
720 // it to OK if we have any device. | |
721 if (audio_allowed && !request.requested_audio_device_id.empty()) { | |
722 const content::MediaStreamDevice* audio_device = | |
723 GetRequestedAudioDevice(request.requested_audio_device_id); | |
724 if (audio_device) { | |
725 devices.push_back(*audio_device); | |
726 get_default_audio_device = false; | |
727 } | 172 } |
728 } | 173 } |
729 if (video_allowed && !request.requested_video_device_id.empty()) { | 174 return false; |
730 const content::MediaStreamDevice* video_device = | |
731 GetRequestedVideoDevice(request.requested_video_device_id); | |
732 if (video_device) { | |
733 devices.push_back(*video_device); | |
734 get_default_video_device = false; | |
735 } | |
736 } | |
737 | |
738 // If either or both audio and video devices were requested but not | |
739 // specified by id, get the default devices. | |
740 if (get_default_audio_device || get_default_video_device) { | |
741 GetDefaultDevicesForProfile(profile, | |
742 get_default_audio_device, | |
743 get_default_video_device, | |
744 &devices); | |
745 } | |
746 | |
747 scoped_ptr<content::MediaStreamUI> ui; | |
748 if (!devices.empty()) { | |
749 result = content::MEDIA_DEVICE_OK; | |
750 ui = media_stream_capture_indicator_->RegisterMediaStream( | |
751 web_contents, devices); | |
752 } | |
753 | |
754 callback.Run(devices, result, ui.Pass()); | |
755 } | |
756 #endif | |
757 | |
758 void MediaCaptureDevicesDispatcher::ProcessRegularMediaAccessRequest( | |
759 content::WebContents* web_contents, | |
760 const content::MediaStreamRequest& request, | |
761 const content::MediaResponseCallback& callback) { | |
762 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
763 | |
764 RequestsQueue& queue = pending_requests_[web_contents]; | |
765 queue.push_back(PendingAccessRequest(request, callback)); | |
766 | |
767 // If this is the only request then show the infobar. | |
768 if (queue.size() == 1) | |
769 ProcessQueuedAccessRequest(web_contents); | |
770 } | |
771 | |
772 void MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest( | |
773 content::WebContents* web_contents) { | |
774 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
775 | |
776 std::map<content::WebContents*, RequestsQueue>::iterator it = | |
777 pending_requests_.find(web_contents); | |
778 | |
779 if (it == pending_requests_.end() || it->second.empty()) { | |
780 // Don't do anything if the tab was closed. | |
781 return; | |
782 } | |
783 | |
784 DCHECK(!it->second.empty()); | |
785 | |
786 if (PermissionBubbleManager::Enabled() || | |
787 MediaStreamPermissionBubbleExperimentEnabled()) { | |
788 scoped_ptr<MediaStreamDevicesController> controller( | |
789 new MediaStreamDevicesController(web_contents, | |
790 it->second.front().request, | |
791 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse, | |
792 base::Unretained(this), web_contents))); | |
793 if (controller->DismissInfoBarAndTakeActionOnSettings()) | |
794 return; | |
795 PermissionBubbleManager* bubble_manager = | |
796 PermissionBubbleManager::FromWebContents(web_contents); | |
797 if (bubble_manager) | |
798 bubble_manager->AddRequest(controller.release()); | |
799 return; | |
800 } | |
801 | |
802 // TODO(gbillock): delete this block and the MediaStreamInfoBarDelegate | |
803 // when we've transitioned to bubbles. (crbug/337458) | |
804 MediaStreamInfoBarDelegate::Create( | |
805 web_contents, it->second.front().request, | |
806 base::Bind(&MediaCaptureDevicesDispatcher::OnAccessRequestResponse, | |
807 base::Unretained(this), web_contents)); | |
808 } | |
809 | |
810 void MediaCaptureDevicesDispatcher::OnAccessRequestResponse( | |
811 content::WebContents* web_contents, | |
812 const content::MediaStreamDevices& devices, | |
813 content::MediaStreamRequestResult result, | |
814 scoped_ptr<content::MediaStreamUI> ui) { | |
815 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
816 | |
817 std::map<content::WebContents*, RequestsQueue>::iterator it = | |
818 pending_requests_.find(web_contents); | |
819 if (it == pending_requests_.end()) { | |
820 // WebContents has been destroyed. Don't need to do anything. | |
821 return; | |
822 } | |
823 | |
824 RequestsQueue& queue(it->second); | |
825 if (queue.empty()) | |
826 return; | |
827 | |
828 content::MediaResponseCallback callback = queue.front().callback; | |
829 queue.pop_front(); | |
830 | |
831 if (!queue.empty()) { | |
832 // Post a task to process next queued request. It has to be done | |
833 // asynchronously to make sure that calling infobar is not destroyed until | |
834 // after this function returns. | |
835 BrowserThread::PostTask( | |
836 BrowserThread::UI, FROM_HERE, | |
837 base::Bind(&MediaCaptureDevicesDispatcher::ProcessQueuedAccessRequest, | |
838 base::Unretained(this), web_contents)); | |
839 } | |
840 | |
841 callback.Run(devices, result, ui.Pass()); | |
842 } | 175 } |
843 | 176 |
844 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile( | 177 void MediaCaptureDevicesDispatcher::GetDefaultDevicesForProfile( |
845 Profile* profile, | 178 Profile* profile, |
846 bool audio, | 179 bool audio, |
847 bool video, | 180 bool video, |
848 content::MediaStreamDevices* devices) { | 181 content::MediaStreamDevices* devices) { |
849 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 182 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
850 DCHECK(audio || video); | 183 DCHECK(audio || video); |
851 | 184 |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
983 OnUpdateVideoDevices(devices)); | 316 OnUpdateVideoDevices(devices)); |
984 } | 317 } |
985 | 318 |
986 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread( | 319 void MediaCaptureDevicesDispatcher::UpdateMediaRequestStateOnUIThread( |
987 int render_process_id, | 320 int render_process_id, |
988 int render_frame_id, | 321 int render_frame_id, |
989 int page_request_id, | 322 int page_request_id, |
990 const GURL& security_origin, | 323 const GURL& security_origin, |
991 content::MediaStreamType stream_type, | 324 content::MediaStreamType stream_type, |
992 content::MediaRequestState state) { | 325 content::MediaRequestState state) { |
993 // Track desktop capture sessions. Tracking is necessary to avoid unbalanced | 326 for (MediaAccessHandler* handler : media_access_handlers_) { |
994 // session counts since not all requests will reach MEDIA_REQUEST_STATE_DONE, | 327 if (handler->SupportsStreamType(stream_type, NULL)) { |
995 // but they will all reach MEDIA_REQUEST_STATE_CLOSING. | 328 handler->UpdateMediaRequest(render_process_id, render_frame_id, |
Sergey Ulanov
2015/06/01 23:39:17
break after this call.
changbin
2015/06/02 14:20:09
Done.
| |
996 if (stream_type == content::MEDIA_DESKTOP_VIDEO_CAPTURE) { | 329 page_request_id, stream_type, state); |
997 if (state == content::MEDIA_REQUEST_STATE_DONE) { | |
998 DesktopCaptureSession session = { render_process_id, render_frame_id, | |
999 page_request_id }; | |
1000 desktop_capture_sessions_.push_back(session); | |
1001 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) { | |
1002 for (DesktopCaptureSessions::iterator it = | |
1003 desktop_capture_sessions_.begin(); | |
1004 it != desktop_capture_sessions_.end(); | |
1005 ++it) { | |
1006 if (it->render_process_id == render_process_id && | |
1007 it->render_frame_id == render_frame_id && | |
1008 it->page_request_id == page_request_id) { | |
1009 desktop_capture_sessions_.erase(it); | |
1010 break; | |
1011 } | |
1012 } | |
1013 } | 330 } |
1014 } | 331 } |
1015 | 332 |
1016 // Cancel the request. | |
1017 if (state == content::MEDIA_REQUEST_STATE_CLOSING) { | |
1018 bool found = false; | |
1019 for (RequestsQueues::iterator rqs_it = pending_requests_.begin(); | |
1020 rqs_it != pending_requests_.end(); ++rqs_it) { | |
1021 RequestsQueue& queue = rqs_it->second; | |
1022 for (RequestsQueue::iterator it = queue.begin(); | |
1023 it != queue.end(); ++it) { | |
1024 if (it->request.render_process_id == render_process_id && | |
1025 it->request.render_frame_id == render_frame_id && | |
1026 it->request.page_request_id == page_request_id) { | |
1027 queue.erase(it); | |
1028 found = true; | |
1029 break; | |
1030 } | |
1031 } | |
1032 if (found) | |
1033 break; | |
1034 } | |
1035 } | |
1036 | |
1037 #if defined(OS_CHROMEOS) | 333 #if defined(OS_CHROMEOS) |
1038 if (IsOriginForCasting(security_origin) && IsVideoMediaType(stream_type)) { | 334 if (IsOriginForCasting(security_origin) && IsVideoMediaType(stream_type)) { |
Sergey Ulanov
2015/06/01 23:39:17
I think this won't compile for chrome os because y
changbin
2015/06/02 14:20:09
Yeah, my fault then. Will fix this.
| |
1039 // Notify ash that casting state has changed. | 335 // Notify ash that casting state has changed. |
1040 if (state == content::MEDIA_REQUEST_STATE_DONE) { | 336 if (state == content::MEDIA_REQUEST_STATE_DONE) { |
1041 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(true); | 337 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(true); |
1042 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) { | 338 } else if (state == content::MEDIA_REQUEST_STATE_CLOSING) { |
1043 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(false); | 339 ash::Shell::GetInstance()->OnCastingSessionStartedOrStopped(false); |
1044 } | 340 } |
1045 } | 341 } |
1046 #endif | 342 #endif |
1047 | 343 |
1048 FOR_EACH_OBSERVER(Observer, observers_, | 344 FOR_EACH_OBSERVER(Observer, observers_, |
1049 OnRequestUpdate(render_process_id, | 345 OnRequestUpdate(render_process_id, |
1050 render_frame_id, | 346 render_frame_id, |
1051 stream_type, | 347 stream_type, |
1052 state)); | 348 state)); |
1053 } | 349 } |
1054 | 350 |
1055 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread( | 351 void MediaCaptureDevicesDispatcher::OnCreatingAudioStreamOnUIThread( |
1056 int render_process_id, | 352 int render_process_id, |
1057 int render_frame_id) { | 353 int render_frame_id) { |
1058 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 354 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
1059 FOR_EACH_OBSERVER(Observer, observers_, | 355 FOR_EACH_OBSERVER(Observer, observers_, |
1060 OnCreatingAudioStream(render_process_id, render_frame_id)); | 356 OnCreatingAudioStream(render_process_id, render_frame_id)); |
1061 } | 357 } |
1062 | 358 |
1063 bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() { | 359 bool MediaCaptureDevicesDispatcher::IsDesktopCaptureInProgress() { |
1064 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 360 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
1065 return desktop_capture_sessions_.size() > 0; | 361 for (MediaAccessHandler* handler : media_access_handlers_) { |
362 if (handler->SupportsStreamType(content::MEDIA_DESKTOP_VIDEO_CAPTURE, | |
363 NULL)) { | |
364 return ToDesktopCaptureAccessHandler(handler)->IsCaptureInProgress(); | |
365 } | |
366 } | |
367 return false; | |
1066 } | 368 } |
1067 | 369 |
1068 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices( | 370 void MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices( |
1069 const MediaStreamDevices& devices) { | 371 const MediaStreamDevices& devices) { |
1070 test_audio_devices_ = devices; | 372 test_audio_devices_ = devices; |
1071 } | 373 } |
1072 | 374 |
1073 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices( | 375 void MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices( |
1074 const MediaStreamDevices& devices) { | 376 const MediaStreamDevices& devices) { |
1075 test_video_devices_ = devices; | 377 test_video_devices_ = devices; |
1076 } | 378 } |
OLD | NEW |