// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/media/media_stream_devices_controller.h" #include "base/command_line.h" #include "base/prefs/pref_service.h" #include "base/values.h" #include "chrome/browser/content_settings/content_settings_provider.h" #include "chrome/browser/content_settings/host_content_settings_map.h" #include "chrome/browser/content_settings/tab_specific_content_settings.h" #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry_factory.h" #include "chrome/browser/media/media_capture_devices_dispatcher.h" #include "chrome/browser/prefs/pref_registry_syncable.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/content_settings.h" #include "chrome/common/pref_names.h" #include "content/public/browser/browser_thread.h" #include "content/public/common/media_stream_request.h" using content::BrowserThread; namespace { bool HasAnyAvailableDevice() { const content::MediaStreamDevices& audio_devices = MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices(); const content::MediaStreamDevices& video_devices = MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices(); return !audio_devices.empty() || !video_devices.empty(); }; } // namespace MediaStreamDevicesController::MediaStreamDevicesController( Profile* profile, TabSpecificContentSettings* content_settings, const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback) : profile_(profile), content_settings_(content_settings), request_(request), callback_(callback), microphone_requested_( request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE), webcam_requested_( request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) { // Don't call GetDevicePolicy from the initializer list since the // implementation depends on member variables. if (microphone_requested_ && GetDevicePolicy(prefs::kAudioCaptureAllowed) == ALWAYS_DENY) { microphone_requested_ = false; } if (webcam_requested_ && GetDevicePolicy(prefs::kVideoCaptureAllowed) == ALWAYS_DENY) { webcam_requested_ = false; } } MediaStreamDevicesController::~MediaStreamDevicesController() {} // static void MediaStreamDevicesController::RegisterUserPrefs( PrefRegistrySyncable* prefs) { prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed, true, PrefRegistrySyncable::UNSYNCABLE_PREF); prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed, true, PrefRegistrySyncable::UNSYNCABLE_PREF); } bool MediaStreamDevicesController::DismissInfoBarAndTakeActionOnSettings() { // If this is a no UI check for policies only go straight to accept - policy // check will be done automatically on the way. if (request_.request_type == content::MEDIA_OPEN_DEVICE) { Accept(false); return true; } if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE || request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE) { HandleTabMediaRequest(); return true; } // Deny the request if the security origin is empty, this happens with // file access without |--allow-file-access-from-files| flag. if (request_.security_origin.is_empty()) { Deny(false); return true; } // Deny the request if there is no device attached to the OS. if (!HasAnyAvailableDevice()) { Deny(false); return true; } // Check if any allow exception has been made for this request. if (IsRequestAllowedByDefault()) { Accept(false); return true; } // Check if any block exception has been made for this request. if (IsRequestBlockedByDefault()) { Deny(false); return true; } // Check if the media default setting is set to block. if (IsDefaultMediaAccessBlocked()) { Deny(false); return true; } // Show the infobar. return false; } const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const { return request_.security_origin.spec(); } void MediaStreamDevicesController::Accept(bool update_content_setting) { if (content_settings_) content_settings_->OnMediaStreamAccessed(); // Get the default devices for the request. content::MediaStreamDevices devices; if (microphone_requested_ || webcam_requested_) { switch (request_.request_type) { case content::MEDIA_OPEN_DEVICE: // For open device request pick the desired device or fall back to the // first available of the given type. MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedDevice( request_.requested_device_id, microphone_requested_, webcam_requested_, &devices); break; case content::MEDIA_DEVICE_ACCESS: case content::MEDIA_GENERATE_STREAM: case content::MEDIA_ENUMERATE_DEVICES: // Get the default devices for the request. MediaCaptureDevicesDispatcher::GetInstance()-> GetDefaultDevicesForProfile(profile_, microphone_requested_, webcam_requested_, &devices); break; } if (update_content_setting && IsSchemeSecure() && !devices.empty()) SetPermission(true); } callback_.Run(devices); } void MediaStreamDevicesController::Deny(bool update_content_setting) { // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA. if (content_settings_) { content_settings_->OnContentBlocked(CONTENT_SETTINGS_TYPE_MEDIASTREAM, std::string()); } if (update_content_setting) SetPermission(false); callback_.Run(content::MediaStreamDevices()); } MediaStreamDevicesController::DevicePolicy MediaStreamDevicesController::GetDevicePolicy(const char* policy_name) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); PrefService* prefs = profile_->GetPrefs(); if (!prefs->IsManagedPreference(policy_name)) return POLICY_NOT_SET; return prefs->GetBoolean(policy_name) ? ALWAYS_ALLOW : ALWAYS_DENY; } bool MediaStreamDevicesController::IsRequestAllowedByDefault() const { // The request from internal objects like chrome://URLs is always allowed. if (ShouldAlwaysAllowOrigin()) return true; struct { bool has_capability; const char* policy_name; ContentSettingsType settings_type; } device_checks[] = { { microphone_requested_, prefs::kAudioCaptureAllowed, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC }, { webcam_requested_, prefs::kVideoCaptureAllowed, CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA }, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(device_checks); ++i) { if (!device_checks[i].has_capability) continue; DevicePolicy policy = GetDevicePolicy(device_checks[i].policy_name); if (policy == ALWAYS_DENY || (policy == POLICY_NOT_SET && profile_->GetHostContentSettingsMap()->GetContentSetting( request_.security_origin, request_.security_origin, device_checks[i].settings_type, NO_RESOURCE_IDENTIFIER) != CONTENT_SETTING_ALLOW)) { return false; } // If we get here, then either policy is set to ALWAYS_ALLOW or the content // settings allow the request by default. } return true; } bool MediaStreamDevicesController::IsRequestBlockedByDefault() const { if (microphone_requested_ && profile_->GetHostContentSettingsMap()->GetContentSetting( request_.security_origin, request_.security_origin, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, NO_RESOURCE_IDENTIFIER) != CONTENT_SETTING_BLOCK) { return false; } if (webcam_requested_ && profile_->GetHostContentSettingsMap()->GetContentSetting( request_.security_origin, request_.security_origin, CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, NO_RESOURCE_IDENTIFIER) != CONTENT_SETTING_BLOCK) { return false; } return true; } bool MediaStreamDevicesController::IsDefaultMediaAccessBlocked() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA. ContentSetting current_setting = profile_->GetHostContentSettingsMap()->GetDefaultContentSetting( CONTENT_SETTINGS_TYPE_MEDIASTREAM, NULL); return (current_setting == CONTENT_SETTING_BLOCK); } void MediaStreamDevicesController::HandleTabMediaRequest() { #if defined(OS_ANDROID) Deny(false); #else // For tab media requests, we need to make sure the request came from the // extension API, so we check the registry here. extensions::TabCaptureRegistry* registry = extensions::TabCaptureRegistryFactory::GetForProfile(profile_); if (!registry->VerifyRequest(request_.render_process_id, request_.render_view_id)) { Deny(false); } else { content::MediaStreamDevices devices; if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) { devices.push_back(content::MediaStreamDevice( content::MEDIA_TAB_AUDIO_CAPTURE, "", "")); } if (request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE) { devices.push_back(content::MediaStreamDevice( content::MEDIA_TAB_VIDEO_CAPTURE, "", "")); } callback_.Run(devices); } #endif } bool MediaStreamDevicesController::IsSchemeSecure() const { return (request_.security_origin.SchemeIsSecure()); } bool MediaStreamDevicesController::ShouldAlwaysAllowOrigin() const { // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA. return profile_->GetHostContentSettingsMap()->ShouldAllowAllContent( request_.security_origin, request_.security_origin, CONTENT_SETTINGS_TYPE_MEDIASTREAM); } void MediaStreamDevicesController::SetPermission(bool allowed) const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); ContentSettingsPattern primary_pattern = ContentSettingsPattern::FromURLNoWildcard(request_.security_origin); // Check the pattern is valid or not. When the request is from a file access, // no exception will be made. if (!primary_pattern.IsValid()) return; ContentSetting content_setting = allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; if (microphone_requested_) { profile_->GetHostContentSettingsMap()->SetContentSetting( primary_pattern, ContentSettingsPattern::Wildcard(), CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, std::string(), content_setting); } if (webcam_requested_) { profile_->GetHostContentSettingsMap()->SetContentSetting( primary_pattern, ContentSettingsPattern::Wildcard(), CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, std::string(), content_setting); } }