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 | |
perkj_chrome
2014/05/02 07:14:37
are
tommi (sloooow) - chröme
2014/05/02 12:01:26
not sure what you mean.... "either of those lists
perkj_chrome
2014/05/05 07:28:25
Humm, seems like like I need an English lesson. ht
| |
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; | |
perkj_chrome
2014/05/02 07:14:37
What does this mean for tab or screen capture? Can
tommi (sloooow) - chröme
2014/05/02 12:01:26
It means that a permission dialog will be shown if
| |
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 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
180 Deny(false, content::MEDIA_DEVICE_NO_HARDWARE); | 289 Deny(false, content::MEDIA_DEVICE_NO_HARDWARE); |
181 return true; | 290 return true; |
182 } | 291 } |
183 | 292 |
184 // Check if any allow exception has been made for this request. | 293 // Check if any allow exception has been made for this request. |
185 if (IsRequestAllowedByDefault()) { | 294 if (IsRequestAllowedByDefault()) { |
186 Accept(false); | 295 Accept(false); |
187 return true; | 296 return true; |
188 } | 297 } |
189 | 298 |
299 // Check if the navigation entry has previously been granted access. | |
300 content::NavigationEntry* navigation_entry = | |
perkj_chrome
2014/05/02 07:14:37
How is this done for HTTPS? Why is there are diffe
tommi (sloooow) - chröme
2014/05/02 12:01:26
https is handled where IsSchemeSecure() is checked
| |
301 web_contents_->GetController().GetVisibleEntry(); | |
302 if (IsRequestAllowedByNavigationEntry(navigation_entry, request_)) { | |
303 Accept(false); | |
304 return true; | |
305 } | |
306 | |
190 // Filter any parts of the request that have been blocked by default and deny | 307 // Filter any parts of the request that have been blocked by default and deny |
191 // it if nothing is left to accept. | 308 // it if nothing is left to accept. |
192 if (FilterBlockedByDefaultDevices() == 0) { | 309 if (FilterBlockedByDefaultDevices() == 0) { |
193 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED); | 310 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED); |
194 return true; | 311 return true; |
195 } | 312 } |
196 | 313 |
197 // Check if the media default setting is set to block. | 314 // Check if the media default setting is set to block. |
198 if (IsDefaultMediaAccessBlocked()) { | 315 if (IsDefaultMediaAccessBlocked()) { |
199 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED); | 316 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED); |
200 return true; | 317 return true; |
201 } | 318 } |
202 | 319 |
203 if (request_.request_type == content::MEDIA_OPEN_DEVICE) { | |
204 bool no_matched_audio_device = | |
205 (request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE && | |
206 !request_.requested_audio_device_id.empty() && | |
207 MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedAudioDevice( | |
208 request_.requested_audio_device_id) == NULL); | |
209 bool no_matched_video_device = | |
210 (request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE && | |
211 !request_.requested_video_device_id.empty() && | |
212 MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedVideoDevice( | |
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 } | |
219 | |
220 // Show the infobar. | 320 // Show the infobar. |
221 return false; | 321 return false; |
222 } | 322 } |
223 | 323 |
224 bool MediaStreamDevicesController::HasAudio() const { | 324 bool MediaStreamDevicesController::HasAudio() const { |
225 return IsDeviceAudioCaptureRequestedAndAllowed(); | 325 return IsDeviceAudioCaptureRequestedAndAllowed(); |
226 } | 326 } |
227 | 327 |
228 bool MediaStreamDevicesController::HasVideo() const { | 328 bool MediaStreamDevicesController::HasVideo() const { |
229 return IsDeviceVideoCaptureRequestedAndAllowed(); | 329 return IsDeviceVideoCaptureRequestedAndAllowed(); |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
298 | 398 |
299 // If either or both audio and video devices were requested but not | 399 // If either or both audio and video devices were requested but not |
300 // specified by id, get the default devices. | 400 // specified by id, get the default devices. |
301 if (get_default_audio_device || get_default_video_device) { | 401 if (get_default_audio_device || get_default_video_device) { |
302 MediaCaptureDevicesDispatcher::GetInstance()-> | 402 MediaCaptureDevicesDispatcher::GetInstance()-> |
303 GetDefaultDevicesForProfile(profile_, | 403 GetDefaultDevicesForProfile(profile_, |
304 get_default_audio_device, | 404 get_default_audio_device, |
305 get_default_video_device, | 405 get_default_video_device, |
306 &devices); | 406 &devices); |
307 } | 407 } |
408 | |
409 // Tag this navigation entry with the granted permissions. | |
410 // This avoids repeated prompts for requests accessed via http. | |
411 content::NavigationEntry* navigation_entry = | |
412 web_contents_->GetController().GetVisibleEntry(); | |
413 if (navigation_entry) { | |
414 SetMediaPermissionsForNavigationEntry( | |
415 navigation_entry, request_, audio_allowed, video_allowed); | |
416 } | |
308 break; | 417 break; |
309 } | 418 } |
310 case content::MEDIA_DEVICE_ACCESS: { | 419 case content::MEDIA_DEVICE_ACCESS: { |
311 // Get the default devices for the request. | 420 // Get the default devices for the request. |
312 MediaCaptureDevicesDispatcher::GetInstance()-> | 421 MediaCaptureDevicesDispatcher::GetInstance()-> |
313 GetDefaultDevicesForProfile(profile_, | 422 GetDefaultDevicesForProfile(profile_, |
314 audio_allowed, | 423 audio_allowed, |
315 video_allowed, | 424 video_allowed, |
316 &devices); | 425 &devices); |
317 break; | 426 break; |
(...skipping 29 matching lines...) Expand all Loading... | |
347 content::MEDIA_DEVICE_NO_HARDWARE : content::MEDIA_DEVICE_OK, | 456 content::MEDIA_DEVICE_NO_HARDWARE : content::MEDIA_DEVICE_OK, |
348 ui.Pass()); | 457 ui.Pass()); |
349 } | 458 } |
350 | 459 |
351 void MediaStreamDevicesController::Deny( | 460 void MediaStreamDevicesController::Deny( |
352 bool update_content_setting, | 461 bool update_content_setting, |
353 content::MediaStreamRequestResult result) { | 462 content::MediaStreamRequestResult result) { |
354 DLOG(WARNING) << "MediaStreamDevicesController::Deny: " << result; | 463 DLOG(WARNING) << "MediaStreamDevicesController::Deny: " << result; |
355 NotifyUIRequestDenied(); | 464 NotifyUIRequestDenied(); |
356 | 465 |
466 // Clear previously allowed permissions from the navigation entry if any. | |
467 content::NavigationEntry* navigation_entry = | |
468 web_contents_->GetController().GetVisibleEntry(); | |
469 if (navigation_entry) { | |
470 SetMediaPermissionsForNavigationEntry( | |
471 navigation_entry, request_, false, false); | |
472 } | |
473 | |
357 if (update_content_setting) | 474 if (update_content_setting) |
358 SetPermission(false); | 475 SetPermission(false); |
359 | 476 |
360 content::MediaResponseCallback cb = callback_; | 477 content::MediaResponseCallback cb = callback_; |
361 callback_.Reset(); | 478 callback_.Reset(); |
362 cb.Run(content::MediaStreamDevices(), | 479 cb.Run(content::MediaStreamDevices(), |
363 result, | 480 result, |
364 scoped_ptr<content::MediaStreamUI>()); | 481 scoped_ptr<content::MediaStreamUI>()); |
365 } | 482 } |
366 | 483 |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
582 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin); | 699 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin); |
583 // Check the pattern is valid or not. When the request is from a file access, | 700 // Check the pattern is valid or not. When the request is from a file access, |
584 // no exception will be made. | 701 // no exception will be made. |
585 if (!primary_pattern.IsValid()) | 702 if (!primary_pattern.IsValid()) |
586 return; | 703 return; |
587 | 704 |
588 ContentSetting content_setting = allowed ? | 705 ContentSetting content_setting = allowed ? |
589 CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; | 706 CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; |
590 if (request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE) != | 707 if (request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE) != |
591 request_permissions_.end()) { | 708 request_permissions_.end()) { |
592 profile_->GetHostContentSettingsMap()->SetContentSetting( | 709 profile_->GetHostContentSettingsMap()->SetContentSetting( |
593 primary_pattern, | 710 primary_pattern, |
594 ContentSettingsPattern::Wildcard(), | 711 ContentSettingsPattern::Wildcard(), |
595 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, | 712 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, |
596 std::string(), | 713 std::string(), |
597 content_setting); | 714 content_setting); |
598 } | 715 } |
599 if (request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE) != | 716 if (request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE) != |
600 request_permissions_.end()) { | 717 request_permissions_.end()) { |
601 profile_->GetHostContentSettingsMap()->SetContentSetting( | 718 profile_->GetHostContentSettingsMap()->SetContentSetting( |
602 primary_pattern, | 719 primary_pattern, |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
640 it->second.permission == MEDIA_ALLOWED); | 757 it->second.permission == MEDIA_ALLOWED); |
641 } | 758 } |
642 | 759 |
643 bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed() | 760 bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed() |
644 const { | 761 const { |
645 MediaStreamTypeSettingsMap::const_iterator it = | 762 MediaStreamTypeSettingsMap::const_iterator it = |
646 request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE); | 763 request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE); |
647 return (it != request_permissions_.end() && | 764 return (it != request_permissions_.end() && |
648 it->second.permission == MEDIA_ALLOWED); | 765 it->second.permission == MEDIA_ALLOWED); |
649 } | 766 } |
OLD | NEW |