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 #if defined(OS_ANDROID)
491 // We do not support sticky operations on Android yet.
494 ContentSettingsPattern primary_pattern =
495 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin);
496 // Check the pattern is valid or not. When the request is from a file access,
497 // no exception will be made.
498 if (!primary_pattern.IsValid())
501 ContentSetting content_setting = allowed ?
502 CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
503 if (request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE) !=
504 request_permissions_.end()) {
505 profile_->GetHostContentSettingsMap()->SetContentSetting(
507 ContentSettingsPattern::Wildcard(),
508 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
512 if (request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE) !=
513 request_permissions_.end()) {
514 profile_->GetHostContentSettingsMap()->SetContentSetting(
516 ContentSettingsPattern::Wildcard(),
517 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
523 void MediaStreamDevicesController::NotifyUIRequestAccepted() const {
524 if (!content_settings_)
527 content_settings_->OnMediaStreamPermissionSet(request_.security_origin,
528 request_permissions_);
531 void MediaStreamDevicesController::NotifyUIRequestDenied() {
532 if (!content_settings_)
535 if (IsDeviceAudioCaptureRequestedAndAllowed()) {
536 request_permissions_[content::MEDIA_DEVICE_AUDIO_CAPTURE].permission =
537 MEDIA_BLOCKED_BY_USER;
539 if (IsDeviceVideoCaptureRequestedAndAllowed()) {
540 request_permissions_[content::MEDIA_DEVICE_VIDEO_CAPTURE].permission =
541 MEDIA_BLOCKED_BY_USER;
544 content_settings_->OnMediaStreamPermissionSet(request_.security_origin,
545 request_permissions_);
548 bool MediaStreamDevicesController::IsDeviceAudioCaptureRequestedAndAllowed()
550 MediaStreamTypeSettingsMap::const_iterator it =
551 request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE);
552 return (it != request_permissions_.end() &&
553 it->second.permission == MEDIA_ALLOWED);
556 bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed()
558 MediaStreamTypeSettingsMap::const_iterator it =
559 request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE);
560 return (it != request_permissions_.end() &&
561 it->second.permission == MEDIA_ALLOWED);