| 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_stream_devices_controller.h" | 5 #include "chrome/browser/media/media_stream_devices_controller.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "base/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
| 9 #include "base/prefs/pref_service.h" | 9 #include "base/prefs/pref_service.h" |
| 10 #include "base/prefs/scoped_user_pref_update.h" | 10 #include "base/prefs/scoped_user_pref_update.h" |
| 11 #include "base/strings/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
| 12 #include "base/values.h" | 12 #include "base/values.h" |
| 13 #include "chrome/browser/content_settings/content_settings_provider.h" | 13 #include "chrome/browser/content_settings/content_settings_provider.h" |
| 14 #include "chrome/browser/content_settings/host_content_settings_map.h" | 14 #include "chrome/browser/content_settings/host_content_settings_map.h" |
| 15 #include "chrome/browser/content_settings/tab_specific_content_settings.h" | 15 #include "chrome/browser/content_settings/tab_specific_content_settings.h" |
| 16 #include "chrome/browser/media/media_capture_devices_dispatcher.h" | 16 #include "chrome/browser/media/media_capture_devices_dispatcher.h" |
| 17 #include "chrome/browser/media/media_stream_capture_indicator.h" | 17 #include "chrome/browser/media/media_stream_capture_indicator.h" |
| 18 #include "chrome/browser/profiles/profile.h" | 18 #include "chrome/browser/profiles/profile.h" |
| 19 #include "chrome/browser/ui/browser.h" | 19 #include "chrome/browser/ui/browser.h" |
| 20 #include "chrome/common/chrome_switches.h" | 20 #include "chrome/common/chrome_switches.h" |
| 21 #include "chrome/common/content_settings.h" | 21 #include "chrome/common/content_settings.h" |
| 22 #include "chrome/common/content_settings_pattern.h" | 22 #include "chrome/common/content_settings_pattern.h" |
| 23 #include "chrome/common/pref_names.h" | 23 #include "chrome/common/pref_names.h" |
| 24 #include "components/user_prefs/pref_registry_syncable.h" | 24 #include "components/user_prefs/pref_registry_syncable.h" |
| 25 #include "content/public/browser/browser_thread.h" | 25 #include "content/public/browser/browser_thread.h" |
| 26 #include "content/public/browser/navigation_entry.h" |
| 26 #include "content/public/common/media_stream_request.h" | 27 #include "content/public/common/media_stream_request.h" |
| 27 #include "extensions/common/constants.h" | 28 #include "extensions/common/constants.h" |
| 28 #include "grit/generated_resources.h" | 29 #include "grit/generated_resources.h" |
| 29 #include "grit/theme_resources.h" | 30 #include "grit/theme_resources.h" |
| 30 #include "ui/base/l10n/l10n_util.h" | 31 #include "ui/base/l10n/l10n_util.h" |
| 31 | 32 |
| 32 #if defined(OS_CHROMEOS) | 33 #if defined(OS_CHROMEOS) |
| 33 #include "chrome/browser/chromeos/login/user_manager.h" | 34 #include "chrome/browser/chromeos/login/user_manager.h" |
| 34 #endif | 35 #endif |
| 35 | 36 |
| 36 using content::BrowserThread; | 37 using content::BrowserThread; |
| 37 | 38 |
| 38 namespace { | 39 namespace { |
| 39 | 40 |
| 41 // This prefix is combined with request security origins to store media access |
| 42 // permissions that the user has granted a specific page navigation instance. |
| 43 // The string value stored with the navigation instance will contain one or more |
| 44 // kMediaPermissionXxx constants that indicates the permission(s) that the user |
| 45 // has granted the page. |
| 46 const char kMediaPermissionKeyPrefix[] = "media_permissions#"; |
| 47 const base::char16 kMediaPermissionAudio = static_cast<base::char16>('a'); |
| 48 const base::char16 kMediaPermissionVideo = static_cast<base::char16>('v'); |
| 49 |
| 40 bool HasAvailableDevicesForRequest(const content::MediaStreamRequest& request) { | 50 bool HasAvailableDevicesForRequest(const content::MediaStreamRequest& request) { |
| 41 bool has_audio_device = | 51 const content::MediaStreamDevices* audio_devices = |
| 42 request.audio_type == content::MEDIA_NO_SERVICE || | 52 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE ? |
| 43 !MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices() | 53 &MediaCaptureDevicesDispatcher::GetInstance() |
| 44 .empty(); | 54 ->GetAudioCaptureDevices() : |
| 45 bool has_video_device = | 55 NULL; |
| 46 request.video_type == content::MEDIA_NO_SERVICE || | |
| 47 !MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices() | |
| 48 .empty(); | |
| 49 | 56 |
| 50 return has_audio_device && has_video_device; | 57 const content::MediaStreamDevices* video_devices = |
| 58 request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE ? |
| 59 &MediaCaptureDevicesDispatcher::GetInstance() |
| 60 ->GetVideoCaptureDevices() : |
| 61 NULL; |
| 62 |
| 63 // Check if we're being asked for audio and/or video and that either of those |
| 64 // lists is empty. If they are, we do not have devices available for the |
| 65 // request. |
| 66 // TODO(tommi): It's kind of strange to have this here since if we fail this |
| 67 // test, there'll be a UI shown that indicates to the user that access to |
| 68 // non-existing audio/video devices has been denied. The user won't have |
| 69 // any way to change that but there will be a UI shown which indicates that |
| 70 // access is blocked. |
| 71 if ((audio_devices != NULL && audio_devices->empty()) || |
| 72 (video_devices != NULL && video_devices->empty())) { |
| 73 return false; |
| 74 } |
| 75 |
| 76 // Note: we check requested_[audio|video]_device_id before dereferencing |
| 77 // [audio|video]_devices. If the requested device id is non-empty, then |
| 78 // the corresponding device list must not be NULL. |
| 79 |
| 80 if (!request.requested_audio_device_id.empty() && |
| 81 !audio_devices->FindById(request.requested_audio_device_id)) { |
| 82 return false; |
| 83 } |
| 84 |
| 85 if (!request.requested_video_device_id.empty() && |
| 86 !video_devices->FindById(request.requested_video_device_id)) { |
| 87 return false; |
| 88 } |
| 89 |
| 90 return true; |
| 51 } | 91 } |
| 52 | 92 |
| 93 base::string16 GetMediaPermissionsFromNavigationEntry( |
| 94 content::NavigationEntry* navigation_entry, |
| 95 const content::MediaStreamRequest& request) { |
| 96 const std::string key(kMediaPermissionKeyPrefix + |
| 97 request.security_origin.spec()); |
| 98 |
| 99 base::string16 permissions; |
| 100 if (!navigation_entry->GetExtraData(key, &permissions)) { |
| 101 DCHECK(permissions.empty()); |
| 102 } |
| 103 |
| 104 return permissions; |
| 105 } |
| 106 |
| 107 void SetMediaPermissionsForNavigationEntry( |
| 108 content::NavigationEntry* navigation_entry, |
| 109 const content::MediaStreamRequest& request, |
| 110 const base::string16& permissions) { |
| 111 const std::string key(kMediaPermissionKeyPrefix + |
| 112 request.security_origin.spec()); |
| 113 permissions.empty() ? |
| 114 navigation_entry->ClearExtraData(key) : |
| 115 navigation_entry->SetExtraData(key, permissions); |
| 116 } |
| 117 |
| 118 void SetMediaPermissionsForNavigationEntry( |
| 119 content::NavigationEntry* navigation_entry, |
| 120 const content::MediaStreamRequest& request, |
| 121 bool allow_audio, |
| 122 bool allow_video) { |
| 123 base::string16 permissions; |
| 124 if (allow_audio) |
| 125 permissions += kMediaPermissionAudio; |
| 126 if (allow_video) |
| 127 permissions += kMediaPermissionVideo; |
| 128 SetMediaPermissionsForNavigationEntry(navigation_entry, request, permissions); |
| 129 } |
| 130 |
| 131 bool IsRequestAllowedByNavigationEntry( |
| 132 content::NavigationEntry* navigation_entry, |
| 133 const content::MediaStreamRequest& request) { |
| 134 using content::MEDIA_NO_SERVICE; |
| 135 using content::MEDIA_DEVICE_AUDIO_CAPTURE; |
| 136 using content::MEDIA_DEVICE_VIDEO_CAPTURE; |
| 137 |
| 138 // If we aren't being asked for at least one of these two, fail right away. |
| 139 if (!navigation_entry || |
| 140 (request.audio_type != MEDIA_DEVICE_AUDIO_CAPTURE && |
| 141 request.video_type != MEDIA_DEVICE_VIDEO_CAPTURE)) { |
| 142 return false; |
| 143 } |
| 144 |
| 145 base::string16 permissions = |
| 146 GetMediaPermissionsFromNavigationEntry(navigation_entry, request); |
| 147 |
| 148 bool audio_requested_and_granted = |
| 149 request.audio_type == MEDIA_DEVICE_AUDIO_CAPTURE && |
| 150 permissions.find(kMediaPermissionAudio) != base::string16::npos; |
| 151 |
| 152 bool video_requested_and_granted = |
| 153 request.video_type == MEDIA_DEVICE_VIDEO_CAPTURE && |
| 154 permissions.find(kMediaPermissionVideo) != base::string16::npos; |
| 155 |
| 156 return |
| 157 (audio_requested_and_granted || request.audio_type == MEDIA_NO_SERVICE) && |
| 158 (video_requested_and_granted || request.video_type == MEDIA_NO_SERVICE); |
| 159 } |
| 160 |
| 161 |
| 53 bool IsInKioskMode() { | 162 bool IsInKioskMode() { |
| 54 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode)) | 163 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode)) |
| 55 return true; | 164 return true; |
| 56 | 165 |
| 57 #if defined(OS_CHROMEOS) | 166 #if defined(OS_CHROMEOS) |
| 58 const chromeos::UserManager* user_manager = chromeos::UserManager::Get(); | 167 const chromeos::UserManager* user_manager = chromeos::UserManager::Get(); |
| 59 return user_manager && user_manager->IsLoggedInAsKioskApp(); | 168 return user_manager && user_manager->IsLoggedInAsKioskApp(); |
| 60 #else | 169 #else |
| 61 return false; | 170 return false; |
| 62 #endif | 171 #endif |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 193 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED); | 302 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED); |
| 194 return true; | 303 return true; |
| 195 } | 304 } |
| 196 | 305 |
| 197 // Check if the media default setting is set to block. | 306 // Check if the media default setting is set to block. |
| 198 if (IsDefaultMediaAccessBlocked()) { | 307 if (IsDefaultMediaAccessBlocked()) { |
| 199 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED); | 308 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED); |
| 200 return true; | 309 return true; |
| 201 } | 310 } |
| 202 | 311 |
| 203 if (request_.request_type == content::MEDIA_OPEN_DEVICE) { | 312 // Check if the navigation entry has previously been granted access. |
| 204 bool no_matched_audio_device = | 313 // We do this after the IsDefaultMediaAccessBlocked check to handle the use |
| 205 (request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE && | 314 // case where the user modifies the content settings to 'deny' after having |
| 206 !request_.requested_audio_device_id.empty() && | 315 // previously granted the page access and the permissions in the |
| 207 MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedAudioDevice( | 316 // NavigationEntry are out of date. |
| 208 request_.requested_audio_device_id) == NULL); | 317 content::NavigationEntry* navigation_entry = |
| 209 bool no_matched_video_device = | 318 web_contents_->GetController().GetVisibleEntry(); |
| 210 (request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE && | 319 if (IsRequestAllowedByNavigationEntry(navigation_entry, request_)) { |
| 211 !request_.requested_video_device_id.empty() && | 320 Accept(false); |
| 212 MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedVideoDevice( | 321 return true; |
| 213 request_.requested_video_device_id) == NULL); | |
| 214 if (no_matched_audio_device || no_matched_video_device) { | |
| 215 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED); | |
| 216 return true; | |
| 217 } | |
| 218 } | 322 } |
| 219 | 323 |
| 220 // Show the infobar. | 324 // Show the infobar. |
| 221 return false; | 325 return false; |
| 222 } | 326 } |
| 223 | 327 |
| 224 bool MediaStreamDevicesController::HasAudio() const { | 328 bool MediaStreamDevicesController::HasAudio() const { |
| 225 return IsDeviceAudioCaptureRequestedAndAllowed(); | 329 return IsDeviceAudioCaptureRequestedAndAllowed(); |
| 226 } | 330 } |
| 227 | 331 |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 298 | 402 |
| 299 // If either or both audio and video devices were requested but not | 403 // If either or both audio and video devices were requested but not |
| 300 // specified by id, get the default devices. | 404 // specified by id, get the default devices. |
| 301 if (get_default_audio_device || get_default_video_device) { | 405 if (get_default_audio_device || get_default_video_device) { |
| 302 MediaCaptureDevicesDispatcher::GetInstance()-> | 406 MediaCaptureDevicesDispatcher::GetInstance()-> |
| 303 GetDefaultDevicesForProfile(profile_, | 407 GetDefaultDevicesForProfile(profile_, |
| 304 get_default_audio_device, | 408 get_default_audio_device, |
| 305 get_default_video_device, | 409 get_default_video_device, |
| 306 &devices); | 410 &devices); |
| 307 } | 411 } |
| 412 |
| 413 // Tag this navigation entry with the granted permissions. |
| 414 // This avoids repeated prompts for requests accessed via http. |
| 415 content::NavigationEntry* navigation_entry = |
| 416 web_contents_->GetController().GetVisibleEntry(); |
| 417 if (navigation_entry) { |
| 418 SetMediaPermissionsForNavigationEntry( |
| 419 navigation_entry, request_, audio_allowed, video_allowed); |
| 420 } |
| 308 break; | 421 break; |
| 309 } | 422 } |
| 310 case content::MEDIA_DEVICE_ACCESS: { | 423 case content::MEDIA_DEVICE_ACCESS: { |
| 311 // Get the default devices for the request. | 424 // Get the default devices for the request. |
| 312 MediaCaptureDevicesDispatcher::GetInstance()-> | 425 MediaCaptureDevicesDispatcher::GetInstance()-> |
| 313 GetDefaultDevicesForProfile(profile_, | 426 GetDefaultDevicesForProfile(profile_, |
| 314 audio_allowed, | 427 audio_allowed, |
| 315 video_allowed, | 428 video_allowed, |
| 316 &devices); | 429 &devices); |
| 317 break; | 430 break; |
| (...skipping 29 matching lines...) Expand all Loading... |
| 347 content::MEDIA_DEVICE_NO_HARDWARE : content::MEDIA_DEVICE_OK, | 460 content::MEDIA_DEVICE_NO_HARDWARE : content::MEDIA_DEVICE_OK, |
| 348 ui.Pass()); | 461 ui.Pass()); |
| 349 } | 462 } |
| 350 | 463 |
| 351 void MediaStreamDevicesController::Deny( | 464 void MediaStreamDevicesController::Deny( |
| 352 bool update_content_setting, | 465 bool update_content_setting, |
| 353 content::MediaStreamRequestResult result) { | 466 content::MediaStreamRequestResult result) { |
| 354 DLOG(WARNING) << "MediaStreamDevicesController::Deny: " << result; | 467 DLOG(WARNING) << "MediaStreamDevicesController::Deny: " << result; |
| 355 NotifyUIRequestDenied(); | 468 NotifyUIRequestDenied(); |
| 356 | 469 |
| 470 // Clear previously allowed permissions from the navigation entry if any. |
| 471 content::NavigationEntry* navigation_entry = |
| 472 web_contents_->GetController().GetVisibleEntry(); |
| 473 if (navigation_entry) { |
| 474 SetMediaPermissionsForNavigationEntry( |
| 475 navigation_entry, request_, false, false); |
| 476 } |
| 477 |
| 357 if (update_content_setting) { | 478 if (update_content_setting) { |
| 358 CHECK_EQ(content::MEDIA_DEVICE_PERMISSION_DENIED, result); | 479 CHECK_EQ(content::MEDIA_DEVICE_PERMISSION_DENIED, result); |
| 359 SetPermission(false); | 480 SetPermission(false); |
| 360 } | 481 } |
| 361 | 482 |
| 362 content::MediaResponseCallback cb = callback_; | 483 content::MediaResponseCallback cb = callback_; |
| 363 callback_.Reset(); | 484 callback_.Reset(); |
| 364 cb.Run(content::MediaStreamDevices(), | 485 cb.Run(content::MediaStreamDevices(), |
| 365 result, | 486 result, |
| 366 scoped_ptr<content::MediaStreamUI>()); | 487 scoped_ptr<content::MediaStreamUI>()); |
| (...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 584 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin); | 705 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin); |
| 585 // Check the pattern is valid or not. When the request is from a file access, | 706 // Check the pattern is valid or not. When the request is from a file access, |
| 586 // no exception will be made. | 707 // no exception will be made. |
| 587 if (!primary_pattern.IsValid()) | 708 if (!primary_pattern.IsValid()) |
| 588 return; | 709 return; |
| 589 | 710 |
| 590 ContentSetting content_setting = allowed ? | 711 ContentSetting content_setting = allowed ? |
| 591 CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; | 712 CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; |
| 592 if (request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE) != | 713 if (request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE) != |
| 593 request_permissions_.end()) { | 714 request_permissions_.end()) { |
| 594 profile_->GetHostContentSettingsMap()->SetContentSetting( | 715 profile_->GetHostContentSettingsMap()->SetContentSetting( |
| 595 primary_pattern, | 716 primary_pattern, |
| 596 ContentSettingsPattern::Wildcard(), | 717 ContentSettingsPattern::Wildcard(), |
| 597 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, | 718 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, |
| 598 std::string(), | 719 std::string(), |
| 599 content_setting); | 720 content_setting); |
| 600 } | 721 } |
| 601 if (request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE) != | 722 if (request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE) != |
| 602 request_permissions_.end()) { | 723 request_permissions_.end()) { |
| 603 profile_->GetHostContentSettingsMap()->SetContentSetting( | 724 profile_->GetHostContentSettingsMap()->SetContentSetting( |
| 604 primary_pattern, | 725 primary_pattern, |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 642 it->second.permission == MEDIA_ALLOWED); | 763 it->second.permission == MEDIA_ALLOWED); |
| 643 } | 764 } |
| 644 | 765 |
| 645 bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed() | 766 bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed() |
| 646 const { | 767 const { |
| 647 MediaStreamTypeSettingsMap::const_iterator it = | 768 MediaStreamTypeSettingsMap::const_iterator it = |
| 648 request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE); | 769 request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE); |
| 649 return (it != request_permissions_.end() && | 770 return (it != request_permissions_.end() && |
| 650 it->second.permission == MEDIA_ALLOWED); | 771 it->second.permission == MEDIA_ALLOWED); |
| 651 } | 772 } |
| OLD | NEW |