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.
4
5 #include "chrome/browser/media/media_stream_devices_controller.h"
6
7 #include "base/metrics/histogram.h"
8 #include "base/prefs/scoped_user_pref_update.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "chrome/browser/content_settings/host_content_settings_map.h"
12 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
13 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
14 #include "chrome/browser/media/media_stream_capture_indicator.h"
15 #include "chrome/browser/media/media_stream_device_permissions.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/pref_names.h"
20 #include "chrome/grit/generated_resources.h"
21 #include "components/content_settings/core/browser/content_settings_provider.h"
22 #include "components/content_settings/core/common/content_settings.h"
23 #include "components/content_settings/core/common/content_settings_pattern.h"
24 #include "components/pref_registry/pref_registry_syncable.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/render_widget_host_view.h"
27 #include "content/public/common/media_stream_request.h"
28 #include "extensions/common/constants.h"
29 #include "grit/theme_resources.h"
30 #include "ui/base/l10n/l10n_util.h"
31
32 using content::BrowserThread;
33
34 namespace {
35
HasAvailableDevicesForRequest(const content::MediaStreamRequest & request)36 bool HasAvailableDevicesForRequest(const content::MediaStreamRequest& request) {
37 const content::MediaStreamDevices* audio_devices =
38 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE ?
39 &MediaCaptureDevicesDispatcher::GetInstance()
40 ->GetAudioCaptureDevices() :
41 NULL;
42
43 const content::MediaStreamDevices* video_devices =
44 request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE ?
45 &MediaCaptureDevicesDispatcher::GetInstance()
46 ->GetVideoCaptureDevices() :
47 NULL;
48
49 // Check if we're being asked for audio and/or video and that either of those
50 // lists is empty. If they are, we do not have devices available for the
51 // request.
52 // TODO(tommi): It's kind of strange to have this here since if we fail this
53 // test, there'll be a UI shown that indicates to the user that access to
54 // non-existing audio/video devices has been denied. The user won't have
55 // any way to change that but there will be a UI shown which indicates that
56 // access is blocked.
57 if ((audio_devices != NULL && audio_devices->empty()) ||
58 (video_devices != NULL && video_devices->empty())) {
59 return false;
60 }
61
62 // Note: we check requested_[audio|video]_device_id before dereferencing
63 // [audio|video]_devices. If the requested device id is non-empty, then
64 // the corresponding device list must not be NULL.
65
66 if (!request.requested_audio_device_id.empty() &&
67 !audio_devices->FindById(request.requested_audio_device_id)) {
68 return false;
69 }
70
71 if (!request.requested_video_device_id.empty() &&
72 !video_devices->FindById(request.requested_video_device_id)) {
73 return false;
74 }
75
76 return true;
77 }
78
79 enum DevicePermissionActions {
80 kAllowHttps = 0,
81 kAllowHttp,
82 kDeny,
83 kCancel,
84 kPermissionActionsMax // Must always be last!
85 };
86
87 } // namespace
88
MediaStreamTypeSettings(Permission permission,const std::string & requested_device_id)89 MediaStreamDevicesController::MediaStreamTypeSettings::MediaStreamTypeSettings(
90 Permission permission, const std::string& requested_device_id):
91 permission(permission), requested_device_id(requested_device_id) {}
92
93 MediaStreamDevicesController::MediaStreamTypeSettings::
MediaStreamTypeSettings()94 MediaStreamTypeSettings(): permission(MEDIA_NONE) {}
95
96 MediaStreamDevicesController::MediaStreamTypeSettings::
~MediaStreamTypeSettings()97 ~MediaStreamTypeSettings() {}
98
MediaStreamDevicesController(content::WebContents * web_contents,const content::MediaStreamRequest & request,const content::MediaResponseCallback & callback)99 MediaStreamDevicesController::MediaStreamDevicesController(
100 content::WebContents* web_contents,
101 const content::MediaStreamRequest& request,
102 const content::MediaResponseCallback& callback)
103 : web_contents_(web_contents),
104 request_(request),
105 callback_(callback) {
106 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext());
107 content_settings_ = TabSpecificContentSettings::FromWebContents(web_contents);
108
109 // For MEDIA_OPEN_DEVICE requests (Pepper) we always request both webcam
110 // and microphone to avoid popping two infobars.
111 // We start with setting the requested media type to allowed or blocked
112 // depending on the policy. If not blocked by policy it may be blocked later
113 // in the two remaining filtering steps (by user setting or by user when
114 // clicking the infobar).
115 // TODO(grunell): It's not the nicest solution to let the MEDIA_OPEN_DEVICE
116 // case take a ride on the MEDIA_DEVICE_*_CAPTURE permission. Should be fixed.
117 if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
118 request.request_type == content::MEDIA_OPEN_DEVICE) {
119 if (GetDevicePolicy(profile_,
120 request_.security_origin,
121 prefs::kAudioCaptureAllowed,
122 prefs::kAudioCaptureAllowedUrls) == ALWAYS_DENY) {
123 request_permissions_.insert(std::make_pair(
124 content::MEDIA_DEVICE_AUDIO_CAPTURE,
125 MediaStreamTypeSettings(MEDIA_BLOCKED_BY_POLICY,
126 request.requested_audio_device_id)));
127 } else {
128 request_permissions_.insert(std::make_pair(
129 content::MEDIA_DEVICE_AUDIO_CAPTURE,
130 MediaStreamTypeSettings(MEDIA_ALLOWED,
131 request.requested_audio_device_id)));
132 }
133 }
134 if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE ||
135 request.request_type == content::MEDIA_OPEN_DEVICE) {
136 if (GetDevicePolicy(profile_,
137 request_.security_origin,
138 prefs::kVideoCaptureAllowed,
139 prefs::kVideoCaptureAllowedUrls) == ALWAYS_DENY) {
140 request_permissions_.insert(std::make_pair(
141 content::MEDIA_DEVICE_VIDEO_CAPTURE,
142 MediaStreamTypeSettings(MEDIA_BLOCKED_BY_POLICY,
143 request.requested_video_device_id)));
144 } else {
145 request_permissions_.insert(std::make_pair(
146 content::MEDIA_DEVICE_VIDEO_CAPTURE,
147 MediaStreamTypeSettings(MEDIA_ALLOWED,
148 request.requested_video_device_id)));
149 }
150 }
151 }
152
~MediaStreamDevicesController()153 MediaStreamDevicesController::~MediaStreamDevicesController() {
154 if (!callback_.is_null()) {
155 callback_.Run(content::MediaStreamDevices(),
156 content::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
157 scoped_ptr<content::MediaStreamUI>());
158 }
159 }
160
161 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * prefs)162 void MediaStreamDevicesController::RegisterProfilePrefs(
163 user_prefs::PrefRegistrySyncable* prefs) {
164 prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed,
165 true,
166 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
167 prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed,
168 true,
169 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
170 prefs->RegisterListPref(prefs::kVideoCaptureAllowedUrls,
171 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
172 prefs->RegisterListPref(prefs::kAudioCaptureAllowedUrls,
173 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
174 }
175
176 // TODO(gbillock): rename? doesn't actually dismiss. More of a 'check profile
177 // and system for compatibility' thing.
DismissInfoBarAndTakeActionOnSettings()178 bool MediaStreamDevicesController::DismissInfoBarAndTakeActionOnSettings() {
179 // Tab capture is allowed for extensions only and infobar is not shown for
180 // extensions.
181 if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE ||
182 request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE) {
183 Deny(false, content::MEDIA_DEVICE_INVALID_STATE);
184 return true;
185 }
186
187 // Deny the request if the security origin is empty, this happens with
188 // file access without |--allow-file-access-from-files| flag.
189 if (request_.security_origin.is_empty()) {
190 Deny(false, content::MEDIA_DEVICE_INVALID_SECURITY_ORIGIN);
191 return true;
192 }
193
194 // Deny the request if there is no device attached to the OS of the
195 // requested type. If both audio and video is requested, both types must be
196 // available.
197 if (!HasAvailableDevicesForRequest(request_)) {
198 Deny(false, content::MEDIA_DEVICE_NO_HARDWARE);
199 return true;
200 }
201
202 // Check if any allow exception has been made for this request.
203 if (IsRequestAllowedByDefault()) {
204 Accept(false);
205 return true;
206 }
207
208 // Filter any parts of the request that have been blocked by default and deny
209 // it if nothing is left to accept.
210 if (FilterBlockedByDefaultDevices() == 0) {
211 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED);
212 return true;
213 }
214
215 // Check if the media default setting is set to block.
216 if (IsDefaultMediaAccessBlocked()) {
217 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED);
218 return true;
219 }
220
221 // Show the infobar.
222 return false;
223 }
224
HasAudio() const225 bool MediaStreamDevicesController::HasAudio() const {
226 return IsDeviceAudioCaptureRequestedAndAllowed();
227 }
228
HasVideo() const229 bool MediaStreamDevicesController::HasVideo() const {
230 return IsDeviceVideoCaptureRequestedAndAllowed();
231 }
232
GetSecurityOriginSpec() const233 const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const {
234 return request_.security_origin.spec();
235 }
236
Accept(bool update_content_setting)237 void MediaStreamDevicesController::Accept(bool update_content_setting) {
238 NotifyUIRequestAccepted();
239
240 // Get the default devices for the request.
241 content::MediaStreamDevices devices;
242 bool audio_allowed = IsDeviceAudioCaptureRequestedAndAllowed();
243 bool video_allowed = IsDeviceVideoCaptureRequestedAndAllowed();
244 if (audio_allowed || video_allowed) {
245 switch (request_.request_type) {
246 case content::MEDIA_OPEN_DEVICE: {
247 const content::MediaStreamDevice* device = NULL;
248 // For open device request, when requested device_id is empty, pick
249 // the first available of the given type. If requested device_id is
250 // not empty, return the desired device if it's available. Otherwise,
251 // return no device.
252 if (audio_allowed &&
253 request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
254 if (!request_.requested_audio_device_id.empty()) {
255 device = MediaCaptureDevicesDispatcher::GetInstance()->
256 GetRequestedAudioDevice(request_.requested_audio_device_id);
257 } else {
258 device = MediaCaptureDevicesDispatcher::GetInstance()->
259 GetFirstAvailableAudioDevice();
260 }
261 } else if (video_allowed &&
262 request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) {
263 // Pepper API opens only one device at a time.
264 if (!request_.requested_video_device_id.empty()) {
265 device = MediaCaptureDevicesDispatcher::GetInstance()->
266 GetRequestedVideoDevice(request_.requested_video_device_id);
267 } else {
268 device = MediaCaptureDevicesDispatcher::GetInstance()->
269 GetFirstAvailableVideoDevice();
270 }
271 }
272 if (device)
273 devices.push_back(*device);
274 break;
275 }
276 case content::MEDIA_GENERATE_STREAM: {
277 bool get_default_audio_device = audio_allowed;
278 bool get_default_video_device = video_allowed;
279
280 // Get the exact audio or video device if an id is specified.
281 if (audio_allowed && !request_.requested_audio_device_id.empty()) {
282 const content::MediaStreamDevice* audio_device =
283 MediaCaptureDevicesDispatcher::GetInstance()->
284 GetRequestedAudioDevice(request_.requested_audio_device_id);
285 if (audio_device) {
286 devices.push_back(*audio_device);
287 get_default_audio_device = false;
288 }
289 }
290 if (video_allowed && !request_.requested_video_device_id.empty()) {
291 const content::MediaStreamDevice* video_device =
292 MediaCaptureDevicesDispatcher::GetInstance()->
293 GetRequestedVideoDevice(request_.requested_video_device_id);
294 if (video_device) {
295 devices.push_back(*video_device);
296 get_default_video_device = false;
297 }
298 }
299
300 // If either or both audio and video devices were requested but not
301 // specified by id, get the default devices.
302 if (get_default_audio_device || get_default_video_device) {
303 MediaCaptureDevicesDispatcher::GetInstance()->
304 GetDefaultDevicesForProfile(profile_,
305 get_default_audio_device,
306 get_default_video_device,
307 &devices);
308 }
309 break;
310 }
311 case content::MEDIA_DEVICE_ACCESS: {
312 // Get the default devices for the request.
313 MediaCaptureDevicesDispatcher::GetInstance()->
314 GetDefaultDevicesForProfile(profile_,
315 audio_allowed,
316 video_allowed,
317 &devices);
318 break;
319 }
320 case content::MEDIA_ENUMERATE_DEVICES: {
321 // Do nothing.
322 NOTREACHED();
323 break;
324 }
325 } // switch
326
327 // TODO(raymes): We currently set the content permission for non-https
328 // websites for Pepper requests as well. This is temporary and should be
329 // removed.
330 if (update_content_setting) {
331 if ((IsSchemeSecure() && !devices.empty()) ||
332 request_.request_type == content::MEDIA_OPEN_DEVICE) {
333 SetPermission(true);
334 }
335 }
336
337 if (audio_allowed) {
338 profile_->GetHostContentSettingsMap()->UpdateLastUsageByPattern(
339 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin),
340 ContentSettingsPattern::Wildcard(),
341 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC);
342 }
343 if (video_allowed) {
344 profile_->GetHostContentSettingsMap()->UpdateLastUsageByPattern(
345 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin),
346 ContentSettingsPattern::Wildcard(),
347 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
348 }
349 }
350
351 scoped_ptr<content::MediaStreamUI> ui;
352 if (!devices.empty()) {
353 ui = MediaCaptureDevicesDispatcher::GetInstance()->
354 GetMediaStreamCaptureIndicator()->RegisterMediaStream(
355 web_contents_, devices);
356 }
357 content::MediaResponseCallback cb = callback_;
358 callback_.Reset();
359 cb.Run(devices,
360 devices.empty() ?
361 content::MEDIA_DEVICE_NO_HARDWARE : content::MEDIA_DEVICE_OK,
362 ui.Pass());
363 }
364
Deny(bool update_content_setting,content::MediaStreamRequestResult result)365 void MediaStreamDevicesController::Deny(
366 bool update_content_setting,
367 content::MediaStreamRequestResult result) {
368 DLOG(WARNING) << "MediaStreamDevicesController::Deny: " << result;
369 NotifyUIRequestDenied();
370
371 if (update_content_setting) {
372 CHECK_EQ(content::MEDIA_DEVICE_PERMISSION_DENIED, result);
373 SetPermission(false);
374 }
375
376 content::MediaResponseCallback cb = callback_;
377 callback_.Reset();
378 cb.Run(content::MediaStreamDevices(),
379 result,
380 scoped_ptr<content::MediaStreamUI>());
381 }
382
GetIconID() const383 int MediaStreamDevicesController::GetIconID() const {
384 if (HasVideo())
385 return IDR_INFOBAR_MEDIA_STREAM_CAMERA;
386
387 return IDR_INFOBAR_MEDIA_STREAM_MIC;
388 }
389
GetMessageText() const390 base::string16 MediaStreamDevicesController::GetMessageText() const {
391 int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO;
392 if (!HasAudio())
393 message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY;
394 else if (!HasVideo())
395 message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY;
396 return l10n_util::GetStringFUTF16(
397 message_id, base::UTF8ToUTF16(GetSecurityOriginSpec()));
398 }
399
GetMessageTextFragment() const400 base::string16 MediaStreamDevicesController::GetMessageTextFragment() const {
401 int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_PERMISSION_FRAGMENT;
402 if (!HasAudio())
403 message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_FRAGMENT;
404 else if (!HasVideo())
405 message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_FRAGMENT;
406 return l10n_util::GetStringUTF16(message_id);
407 }
408
HasUserGesture() const409 bool MediaStreamDevicesController::HasUserGesture() const {
410 return request_.user_gesture;
411 }
412
GetRequestingHostname() const413 GURL MediaStreamDevicesController::GetRequestingHostname() const {
414 return request_.security_origin;
415 }
416
PermissionGranted()417 void MediaStreamDevicesController::PermissionGranted() {
418 GURL origin(GetSecurityOriginSpec());
419 if (origin.SchemeIsSecure()) {
420 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
421 kAllowHttps, kPermissionActionsMax);
422 } else {
423 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
424 kAllowHttp, kPermissionActionsMax);
425 }
426 Accept(true);
427 }
428
PermissionDenied()429 void MediaStreamDevicesController::PermissionDenied() {
430 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
431 kDeny, kPermissionActionsMax);
432 Deny(true, content::MEDIA_DEVICE_PERMISSION_DENIED);
433 }
434
Cancelled()435 void MediaStreamDevicesController::Cancelled() {
436 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
437 kCancel, kPermissionActionsMax);
438 Deny(false, content::MEDIA_DEVICE_PERMISSION_DISMISSED);
439 }
440
RequestFinished()441 void MediaStreamDevicesController::RequestFinished() {
442 delete this;
443 }
444
IsRequestAllowedByDefault() const445 bool MediaStreamDevicesController::IsRequestAllowedByDefault() const {
446 // The request from internal objects like chrome://URLs is always allowed.
447 if (CheckAllowAllMediaStreamContentForOrigin(profile_,
448 request_.security_origin)) {
449 return true;
450 }
451
452 struct {
453 bool has_capability;
454 const char* policy_name;
455 const char* list_policy_name;
456 ContentSettingsType settings_type;
457 } device_checks[] = {
458 { IsDeviceAudioCaptureRequestedAndAllowed(), prefs::kAudioCaptureAllowed,
459 prefs::kAudioCaptureAllowedUrls, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC },
460 { IsDeviceVideoCaptureRequestedAndAllowed(), prefs::kVideoCaptureAllowed,
461 prefs::kVideoCaptureAllowedUrls,
462 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA },
463 };
464
465 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(device_checks); ++i) {
466 if (!device_checks[i].has_capability)
467 continue;
468
469 MediaStreamDevicePolicy policy =
470 GetDevicePolicy(profile_,
471 request_.security_origin,
472 device_checks[i].policy_name,
473 device_checks[i].list_policy_name);
474
475 if (policy == ALWAYS_DENY)
476 return false;
477
478 if (policy == POLICY_NOT_SET) {
479 // Only load content settings from secure origins unless it is a
480 // content::MEDIA_OPEN_DEVICE (Pepper) request.
481 if (!IsSchemeSecure() &&
482 request_.request_type != content::MEDIA_OPEN_DEVICE) {
483 return false;
484 }
485 if (profile_->GetHostContentSettingsMap()->GetContentSetting(
486 request_.security_origin, request_.security_origin,
487 device_checks[i].settings_type, NO_RESOURCE_IDENTIFIER) !=
488 CONTENT_SETTING_ALLOW) {
489 return false;
490 }
491 }
492 // If we get here, then either policy is set to ALWAYS_ALLOW or the content
493 // settings allow the request by default.
494 }
495
496 return true;
497 }
498
FilterBlockedByDefaultDevices()499 int MediaStreamDevicesController::FilterBlockedByDefaultDevices() {
500 int requested_devices = 0;
501
502 if (IsDeviceAudioCaptureRequestedAndAllowed()) {
503 if (profile_->GetHostContentSettingsMap()->GetContentSetting(
504 request_.security_origin,
505 request_.security_origin,
506 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
507 NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_BLOCK) {
508 request_permissions_[content::MEDIA_DEVICE_AUDIO_CAPTURE].permission =
509 MEDIA_BLOCKED_BY_USER_SETTING;
510 } else {
511 ++requested_devices;
512 }
513 }
514
515 if (IsDeviceVideoCaptureRequestedAndAllowed()) {
516 if (profile_->GetHostContentSettingsMap()->GetContentSetting(
517 request_.security_origin,
518 request_.security_origin,
519 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
520 NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_BLOCK) {
521 request_permissions_[content::MEDIA_DEVICE_VIDEO_CAPTURE].permission =
522 MEDIA_BLOCKED_BY_USER_SETTING;
523 } else {
524 ++requested_devices;
525 }
526 }
527
528 return requested_devices;
529 }
530
IsDefaultMediaAccessBlocked() const531 bool MediaStreamDevicesController::IsDefaultMediaAccessBlocked() const {
532 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
533 // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the
534 // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and
535 // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA.
536 ContentSetting current_setting =
537 profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
538 CONTENT_SETTINGS_TYPE_MEDIASTREAM, NULL);
539 return (current_setting == CONTENT_SETTING_BLOCK);
540 }
541
IsSchemeSecure() const542 bool MediaStreamDevicesController::IsSchemeSecure() const {
543 return request_.security_origin.SchemeIsSecure() ||
544 request_.security_origin.SchemeIs(extensions::kExtensionScheme);
545 }
546
SetPermission(bool allowed) const547 void MediaStreamDevicesController::SetPermission(bool allowed) const {
548 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
549 ContentSettingsPattern primary_pattern =
550 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin);
551 // Check the pattern is valid or not. When the request is from a file access,
552 // no exception will be made.
553 if (!primary_pattern.IsValid())
554 return;
555
556 ContentSetting content_setting = allowed ?
557 CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
558 if (request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE) !=
559 request_permissions_.end()) {
560 profile_->GetHostContentSettingsMap()->SetContentSetting(
561 primary_pattern,
562 ContentSettingsPattern::Wildcard(),
563 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
564 std::string(),
565 content_setting);
566 }
567 if (request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE) !=
568 request_permissions_.end()) {
569 profile_->GetHostContentSettingsMap()->SetContentSetting(
570 primary_pattern,
571 ContentSettingsPattern::Wildcard(),
572 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
573 std::string(),
574 content_setting);
575 }
576 }
577
NotifyUIRequestAccepted() const578 void MediaStreamDevicesController::NotifyUIRequestAccepted() const {
579 if (!content_settings_)
580 return;
581
582 content_settings_->OnMediaStreamPermissionSet(request_.security_origin,
583 request_permissions_);
584 }
585
NotifyUIRequestDenied()586 void MediaStreamDevicesController::NotifyUIRequestDenied() {
587 if (!content_settings_)
588 return;
589
590 if (IsDeviceAudioCaptureRequestedAndAllowed()) {
591 request_permissions_[content::MEDIA_DEVICE_AUDIO_CAPTURE].permission =
592 MEDIA_BLOCKED_BY_USER;
593 }
594 if (IsDeviceVideoCaptureRequestedAndAllowed()) {
595 request_permissions_[content::MEDIA_DEVICE_VIDEO_CAPTURE].permission =
596 MEDIA_BLOCKED_BY_USER;
597 }
598
599 content_settings_->OnMediaStreamPermissionSet(request_.security_origin,
600 request_permissions_);
601 }
602
IsDeviceAudioCaptureRequestedAndAllowed() const603 bool MediaStreamDevicesController::IsDeviceAudioCaptureRequestedAndAllowed()
604 const {
605 MediaStreamTypeSettingsMap::const_iterator it =
606 request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE);
607 return (it != request_permissions_.end() && IsCaptureDeviceRequestAllowed() &&
608 it->second.permission == MEDIA_ALLOWED);
609 }
610
IsDeviceVideoCaptureRequestedAndAllowed() const611 bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed()
612 const {
613 MediaStreamTypeSettingsMap::const_iterator it =
614 request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE);
615 return (it != request_permissions_.end() && IsCaptureDeviceRequestAllowed() &&
616 it->second.permission == MEDIA_ALLOWED);
617 }
618
IsCaptureDeviceRequestAllowed() const619 bool MediaStreamDevicesController::IsCaptureDeviceRequestAllowed() const {
620 #if defined(OS_ANDROID)
621 // Don't approve device requests if the tab was hidden.
622 // TODO(qinmin): Add a test for this. http://crbug.com/396869.
623 return web_contents_->GetRenderWidgetHostView()->IsShowing();
624 #endif
625 return true;
626 }
627