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.
5 #include "chrome/browser/media/media_stream_devices_controller.h"
7 #include "base/command_line.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/prefs/scoped_user_pref_update.h"
10 #include "base/values.h"
11 #include "chrome/browser/content_settings/content_settings_provider.h"
12 #include "chrome/browser/content_settings/host_content_settings_map.h"
13 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
14 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
15 #include "chrome/browser/media/media_stream_capture_indicator.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/common/content_settings.h"
20 #include "chrome/common/content_settings_pattern.h"
21 #include "chrome/common/pref_names.h"
22 #include "components/user_prefs/pref_registry_syncable.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/common/media_stream_request.h"
25 #include "extensions/common/constants.h"
27 #if defined(OS_CHROMEOS)
28 #include "chrome/browser/chromeos/login/user_manager.h"
31 using content::BrowserThread
;
35 bool HasAnyAvailableDevice() {
36 const content::MediaStreamDevices
& audio_devices
=
37 MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices();
38 const content::MediaStreamDevices
& video_devices
=
39 MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices();
41 return !audio_devices
.empty() || !video_devices
.empty();
44 bool IsInKioskMode() {
45 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode
))
48 #if defined(OS_CHROMEOS)
49 const chromeos::UserManager
* user_manager
= chromeos::UserManager::Get();
50 return user_manager
&& user_manager
->IsLoggedInAsKioskApp();
58 MediaStreamDevicesController::MediaStreamTypeSettings::MediaStreamTypeSettings(
59 Permission permission
, const std::string
& requested_device_id
):
60 permission(permission
), requested_device_id(requested_device_id
) {}
62 MediaStreamDevicesController::MediaStreamTypeSettings::
63 MediaStreamTypeSettings(): permission(MEDIA_NONE
) {}
65 MediaStreamDevicesController::MediaStreamTypeSettings::
66 ~MediaStreamTypeSettings() {}
68 MediaStreamDevicesController::MediaStreamDevicesController(
69 content::WebContents
* web_contents
,
70 const content::MediaStreamRequest
& request
,
71 const content::MediaResponseCallback
& callback
)
72 : web_contents_(web_contents
),
75 profile_
= Profile::FromBrowserContext(web_contents
->GetBrowserContext());
76 content_settings_
= TabSpecificContentSettings::FromWebContents(web_contents
);
78 // For MEDIA_OPEN_DEVICE requests (Pepper) we always request both webcam
79 // and microphone to avoid popping two infobars.
80 // We start with setting the requested media type to allowed or blocked
81 // depending on the policy. If not blocked by policy it may be blocked later
82 // in the two remaining filtering steps (by user setting or by user when
83 // clicking the infobar).
84 // TODO(grunell): It's not the nicest solution to let the MEDIA_OPEN_DEVICE
85 // case take a ride on the MEDIA_DEVICE_*_CAPTURE permission. Should be fixed.
86 if (request
.audio_type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
||
87 request
.request_type
== content::MEDIA_OPEN_DEVICE
) {
88 if (GetDevicePolicy(prefs::kAudioCaptureAllowed
,
89 prefs::kAudioCaptureAllowedUrls
) == ALWAYS_DENY
) {
90 request_permissions_
.insert(std::make_pair(
91 content::MEDIA_DEVICE_AUDIO_CAPTURE
,
92 MediaStreamTypeSettings(MEDIA_BLOCKED_BY_POLICY
,
93 request
.requested_audio_device_id
)));
95 request_permissions_
.insert(std::make_pair(
96 content::MEDIA_DEVICE_AUDIO_CAPTURE
,
97 MediaStreamTypeSettings(MEDIA_ALLOWED
,
98 request
.requested_audio_device_id
)));
101 if (request
.video_type
== content::MEDIA_DEVICE_VIDEO_CAPTURE
||
102 request
.request_type
== content::MEDIA_OPEN_DEVICE
) {
103 if (GetDevicePolicy(prefs::kVideoCaptureAllowed
,
104 prefs::kVideoCaptureAllowedUrls
) == ALWAYS_DENY
) {
105 request_permissions_
.insert(std::make_pair(
106 content::MEDIA_DEVICE_VIDEO_CAPTURE
,
107 MediaStreamTypeSettings(MEDIA_BLOCKED_BY_POLICY
,
108 request
.requested_video_device_id
)));
110 request_permissions_
.insert(std::make_pair(
111 content::MEDIA_DEVICE_VIDEO_CAPTURE
,
112 MediaStreamTypeSettings(MEDIA_ALLOWED
,
113 request
.requested_video_device_id
)));
118 MediaStreamDevicesController::~MediaStreamDevicesController() {
119 if (!callback_
.is_null()) {
120 callback_
.Run(content::MediaStreamDevices(),
121 scoped_ptr
<content::MediaStreamUI
>());
126 void MediaStreamDevicesController::RegisterProfilePrefs(
127 user_prefs::PrefRegistrySyncable
* prefs
) {
128 prefs
->RegisterBooleanPref(prefs::kVideoCaptureAllowed
,
130 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
131 prefs
->RegisterBooleanPref(prefs::kAudioCaptureAllowed
,
133 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
134 prefs
->RegisterListPref(prefs::kVideoCaptureAllowedUrls
,
135 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
136 prefs
->RegisterListPref(prefs::kAudioCaptureAllowedUrls
,
137 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF
);
141 bool MediaStreamDevicesController::DismissInfoBarAndTakeActionOnSettings() {
142 // Tab capture is allowed for extensions only and infobar is not shown for
144 if (request_
.audio_type
== content::MEDIA_TAB_AUDIO_CAPTURE
||
145 request_
.video_type
== content::MEDIA_TAB_VIDEO_CAPTURE
) {
150 // Deny the request if the security origin is empty, this happens with
151 // file access without |--allow-file-access-from-files| flag.
152 if (request_
.security_origin
.is_empty()) {
157 // Deny the request if there is no device attached to the OS.
158 if (!HasAnyAvailableDevice()) {
163 // Check if any allow exception has been made for this request.
164 if (IsRequestAllowedByDefault()) {
169 // Filter any parts of the request that have been blocked by default and deny
170 // it if nothing is left to accept.
171 if (FilterBlockedByDefaultDevices() == 0) {
176 // Check if the media default setting is set to block.
177 if (IsDefaultMediaAccessBlocked()) {
182 if (request_
.request_type
== content::MEDIA_OPEN_DEVICE
) {
183 bool no_matched_audio_device
=
184 (request_
.audio_type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
&&
185 !request_
.requested_audio_device_id
.empty() &&
186 MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedAudioDevice(
187 request_
.requested_audio_device_id
) == NULL
);
188 bool no_matched_video_device
=
189 (request_
.video_type
== content::MEDIA_DEVICE_VIDEO_CAPTURE
&&
190 !request_
.requested_video_device_id
.empty() &&
191 MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedVideoDevice(
192 request_
.requested_video_device_id
) == NULL
);
193 if (no_matched_audio_device
|| no_matched_video_device
) {
203 bool MediaStreamDevicesController::HasAudio() const {
204 return IsDeviceAudioCaptureRequestedAndAllowed();
207 bool MediaStreamDevicesController::HasVideo() const {
208 return IsDeviceVideoCaptureRequestedAndAllowed();
211 const std::string
& MediaStreamDevicesController::GetSecurityOriginSpec() const {
212 return request_
.security_origin
.spec();
215 void MediaStreamDevicesController::Accept(bool update_content_setting
) {
216 NotifyUIRequestAccepted();
218 // Get the default devices for the request.
219 content::MediaStreamDevices devices
;
220 bool audio_allowed
= IsDeviceAudioCaptureRequestedAndAllowed();
221 bool video_allowed
= IsDeviceVideoCaptureRequestedAndAllowed();
222 if (audio_allowed
|| video_allowed
) {
223 switch (request_
.request_type
) {
224 case content::MEDIA_OPEN_DEVICE
: {
225 const content::MediaStreamDevice
* device
= NULL
;
226 // For open device request, when requested device_id is empty, pick
227 // the first available of the given type. If requested device_id is
228 // not empty, return the desired device if it's available. Otherwise,
231 request_
.audio_type
== content::MEDIA_DEVICE_AUDIO_CAPTURE
) {
232 if (!request_
.requested_audio_device_id
.empty()) {
233 device
= MediaCaptureDevicesDispatcher::GetInstance()->
234 GetRequestedAudioDevice(request_
.requested_audio_device_id
);
236 device
= MediaCaptureDevicesDispatcher::GetInstance()->
237 GetFirstAvailableAudioDevice();
239 } else if (video_allowed
&&
240 request_
.video_type
== content::MEDIA_DEVICE_VIDEO_CAPTURE
) {
241 // Pepper API opens only one device at a time.
242 if (!request_
.requested_video_device_id
.empty()) {
243 device
= MediaCaptureDevicesDispatcher::GetInstance()->
244 GetRequestedVideoDevice(request_
.requested_video_device_id
);
246 device
= MediaCaptureDevicesDispatcher::GetInstance()->
247 GetFirstAvailableVideoDevice();
251 devices
.push_back(*device
);
254 case content::MEDIA_GENERATE_STREAM
: {
255 bool get_default_audio_device
= audio_allowed
;
256 bool get_default_video_device
= video_allowed
;
258 // Get the exact audio or video device if an id is specified.
259 if (audio_allowed
&& !request_
.requested_audio_device_id
.empty()) {
260 const content::MediaStreamDevice
* audio_device
=
261 MediaCaptureDevicesDispatcher::GetInstance()->
262 GetRequestedAudioDevice(request_
.requested_audio_device_id
);
264 devices
.push_back(*audio_device
);
265 get_default_audio_device
= false;
268 if (video_allowed
&& !request_
.requested_video_device_id
.empty()) {
269 const content::MediaStreamDevice
* video_device
=
270 MediaCaptureDevicesDispatcher::GetInstance()->
271 GetRequestedVideoDevice(request_
.requested_video_device_id
);
273 devices
.push_back(*video_device
);
274 get_default_video_device
= false;
278 // If either or both audio and video devices were requested but not
279 // specified by id, get the default devices.
280 if (get_default_audio_device
|| get_default_video_device
) {
281 MediaCaptureDevicesDispatcher::GetInstance()->
282 GetDefaultDevicesForProfile(profile_
,
283 get_default_audio_device
,
284 get_default_video_device
,
289 case content::MEDIA_DEVICE_ACCESS
: {
290 // Get the default devices for the request.
291 MediaCaptureDevicesDispatcher::GetInstance()->
292 GetDefaultDevicesForProfile(profile_
,
298 case content::MEDIA_ENUMERATE_DEVICES
: {
305 // TODO(raymes): We currently set the content permission for non-https
306 // websites for Pepper requests as well. This is temporary and should be
308 if (update_content_setting
) {
309 if ((IsSchemeSecure() && !devices
.empty()) ||
310 request_
.request_type
== content::MEDIA_OPEN_DEVICE
) {
316 scoped_ptr
<content::MediaStreamUI
> ui
;
317 if (!devices
.empty()) {
318 ui
= MediaCaptureDevicesDispatcher::GetInstance()->
319 GetMediaStreamCaptureIndicator()->RegisterMediaStream(
320 web_contents_
, devices
);
322 content::MediaResponseCallback cb
= callback_
;
324 cb
.Run(devices
, ui
.Pass());
327 void MediaStreamDevicesController::Deny(bool update_content_setting
) {
328 NotifyUIRequestDenied();
330 if (update_content_setting
)
331 SetPermission(false);
333 content::MediaResponseCallback cb
= callback_
;
335 cb
.Run(content::MediaStreamDevices(), scoped_ptr
<content::MediaStreamUI
>());
338 MediaStreamDevicesController::DevicePolicy
339 MediaStreamDevicesController::GetDevicePolicy(
340 const char* policy_name
,
341 const char* whitelist_policy_name
) const {
342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
344 // If the security origin policy matches a value in the whitelist, allow it.
345 // Otherwise, check the |policy_name| master switch for the default behavior.
347 PrefService
* prefs
= profile_
->GetPrefs();
349 // TODO(tommi): Remove the kiosk mode check when the whitelist below
350 // is visible in the media exceptions UI.
351 // See discussion here: https://codereview.chromium.org/15738004/
352 if (IsInKioskMode()) {
353 const base::ListValue
* list
= prefs
->GetList(whitelist_policy_name
);
355 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
356 if (list
->GetString(i
, &value
)) {
357 ContentSettingsPattern pattern
=
358 ContentSettingsPattern::FromString(value
);
359 if (pattern
== ContentSettingsPattern::Wildcard()) {
360 DLOG(WARNING
) << "Ignoring wildcard URL pattern: " << value
;
363 DLOG_IF(ERROR
, !pattern
.IsValid()) << "Invalid URL pattern: " << value
;
364 if (pattern
.IsValid() && pattern
.Matches(request_
.security_origin
))
370 // If a match was not found, check if audio capture is otherwise disallowed
371 // or if the user should be prompted. Setting the policy value to "true"
372 // is equal to not setting it at all, so from hereon out, we will return
373 // either POLICY_NOT_SET (prompt) or ALWAYS_DENY (no prompt, no access).
374 if (!prefs
->GetBoolean(policy_name
))
377 return POLICY_NOT_SET
;
380 bool MediaStreamDevicesController::IsRequestAllowedByDefault() const {
381 // The request from internal objects like chrome://URLs is always allowed.
382 if (ShouldAlwaysAllowOrigin())
387 const char* policy_name
;
388 const char* list_policy_name
;
389 ContentSettingsType settings_type
;
390 } device_checks
[] = {
391 { IsDeviceAudioCaptureRequestedAndAllowed(), prefs::kAudioCaptureAllowed
,
392 prefs::kAudioCaptureAllowedUrls
, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
},
393 { IsDeviceVideoCaptureRequestedAndAllowed(), prefs::kVideoCaptureAllowed
,
394 prefs::kVideoCaptureAllowedUrls
,
395 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
},
398 for (size_t i
= 0; i
< ARRAYSIZE_UNSAFE(device_checks
); ++i
) {
399 if (!device_checks
[i
].has_capability
)
402 DevicePolicy policy
= GetDevicePolicy(device_checks
[i
].policy_name
,
403 device_checks
[i
].list_policy_name
);
405 if (policy
== ALWAYS_DENY
)
408 if (policy
== POLICY_NOT_SET
) {
409 // Only load content settings from secure origins unless it is a
410 // content::MEDIA_OPEN_DEVICE (Pepper) request.
411 if (!IsSchemeSecure() &&
412 request_
.request_type
!= content::MEDIA_OPEN_DEVICE
) {
415 if (profile_
->GetHostContentSettingsMap()->GetContentSetting(
416 request_
.security_origin
, request_
.security_origin
,
417 device_checks
[i
].settings_type
, NO_RESOURCE_IDENTIFIER
) !=
418 CONTENT_SETTING_ALLOW
) {
422 // If we get here, then either policy is set to ALWAYS_ALLOW or the content
423 // settings allow the request by default.
429 int MediaStreamDevicesController::FilterBlockedByDefaultDevices() {
430 int requested_devices
= 0;
432 if (IsDeviceAudioCaptureRequestedAndAllowed()) {
433 if (profile_
->GetHostContentSettingsMap()->GetContentSetting(
434 request_
.security_origin
,
435 request_
.security_origin
,
436 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
,
437 NO_RESOURCE_IDENTIFIER
) == CONTENT_SETTING_BLOCK
) {
438 request_permissions_
[content::MEDIA_DEVICE_AUDIO_CAPTURE
].permission
=
439 MEDIA_BLOCKED_BY_USER_SETTING
;
445 if (IsDeviceVideoCaptureRequestedAndAllowed()) {
446 if (profile_
->GetHostContentSettingsMap()->GetContentSetting(
447 request_
.security_origin
,
448 request_
.security_origin
,
449 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
,
450 NO_RESOURCE_IDENTIFIER
) == CONTENT_SETTING_BLOCK
) {
451 request_permissions_
[content::MEDIA_DEVICE_VIDEO_CAPTURE
].permission
=
452 MEDIA_BLOCKED_BY_USER_SETTING
;
458 return requested_devices
;
461 bool MediaStreamDevicesController::IsDefaultMediaAccessBlocked() const {
462 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
463 // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the
464 // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and
465 // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA.
466 ContentSetting current_setting
=
467 profile_
->GetHostContentSettingsMap()->GetDefaultContentSetting(
468 CONTENT_SETTINGS_TYPE_MEDIASTREAM
, NULL
);
469 return (current_setting
== CONTENT_SETTING_BLOCK
);
472 bool MediaStreamDevicesController::IsSchemeSecure() const {
473 return request_
.security_origin
.SchemeIsSecure() ||
474 request_
.security_origin
.SchemeIs(extensions::kExtensionScheme
) ||
475 CommandLine::ForCurrentProcess()->HasSwitch(
476 switches::kDisableUserMediaSecurity
);
479 bool MediaStreamDevicesController::ShouldAlwaysAllowOrigin() const {
480 // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the
481 // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and
482 // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA.
483 return profile_
->GetHostContentSettingsMap()->ShouldAllowAllContent(
484 request_
.security_origin
, request_
.security_origin
,
485 CONTENT_SETTINGS_TYPE_MEDIASTREAM
);
488 void MediaStreamDevicesController::SetPermission(bool allowed
) const {
489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
490 ContentSettingsPattern primary_pattern
=
491 ContentSettingsPattern::FromURLNoWildcard(request_
.security_origin
);
492 // Check the pattern is valid or not. When the request is from a file access,
493 // no exception will be made.
494 if (!primary_pattern
.IsValid())
497 ContentSetting content_setting
= allowed
?
498 CONTENT_SETTING_ALLOW
: CONTENT_SETTING_BLOCK
;
499 if (request_permissions_
.find(content::MEDIA_DEVICE_AUDIO_CAPTURE
) !=
500 request_permissions_
.end()) {
501 profile_
->GetHostContentSettingsMap()->SetContentSetting(
503 ContentSettingsPattern::Wildcard(),
504 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC
,
508 if (request_permissions_
.find(content::MEDIA_DEVICE_VIDEO_CAPTURE
) !=
509 request_permissions_
.end()) {
510 profile_
->GetHostContentSettingsMap()->SetContentSetting(
512 ContentSettingsPattern::Wildcard(),
513 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA
,
519 void MediaStreamDevicesController::NotifyUIRequestAccepted() const {
520 if (!content_settings_
)
523 content_settings_
->OnMediaStreamPermissionSet(request_
.security_origin
,
524 request_permissions_
);
527 void MediaStreamDevicesController::NotifyUIRequestDenied() {
528 if (!content_settings_
)
531 if (IsDeviceAudioCaptureRequestedAndAllowed()) {
532 request_permissions_
[content::MEDIA_DEVICE_AUDIO_CAPTURE
].permission
=
533 MEDIA_BLOCKED_BY_USER
;
535 if (IsDeviceVideoCaptureRequestedAndAllowed()) {
536 request_permissions_
[content::MEDIA_DEVICE_VIDEO_CAPTURE
].permission
=
537 MEDIA_BLOCKED_BY_USER
;
540 content_settings_
->OnMediaStreamPermissionSet(request_
.security_origin
,
541 request_permissions_
);
544 bool MediaStreamDevicesController::IsDeviceAudioCaptureRequestedAndAllowed()
546 MediaStreamTypeSettingsMap::const_iterator it
=
547 request_permissions_
.find(content::MEDIA_DEVICE_AUDIO_CAPTURE
);
548 return (it
!= request_permissions_
.end() &&
549 it
->second
.permission
== MEDIA_ALLOWED
);
552 bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed()
554 MediaStreamTypeSettingsMap::const_iterator it
=
555 request_permissions_
.find(content::MEDIA_DEVICE_VIDEO_CAPTURE
);
556 return (it
!= request_permissions_
.end() &&
557 it
->second
.permission
== MEDIA_ALLOWED
);