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
32using content::BrowserThread;
33
34namespace {
35
36bool 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
79enum DevicePermissionActions {
80  kAllowHttps = 0,
81  kAllowHttp,
82  kDeny,
83  kCancel,
84  kPermissionActionsMax  // Must always be last!
85};
86
87}  // namespace
88
89MediaStreamDevicesController::MediaStreamTypeSettings::MediaStreamTypeSettings(
90    Permission permission, const std::string& requested_device_id):
91    permission(permission), requested_device_id(requested_device_id) {}
92
93MediaStreamDevicesController::MediaStreamTypeSettings::
94    MediaStreamTypeSettings(): permission(MEDIA_NONE) {}
95
96MediaStreamDevicesController::MediaStreamTypeSettings::
97    ~MediaStreamTypeSettings() {}
98
99MediaStreamDevicesController::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
153MediaStreamDevicesController::~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
162void 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.
178bool 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
225bool MediaStreamDevicesController::HasAudio() const {
226  return IsDeviceAudioCaptureRequestedAndAllowed();
227}
228
229bool MediaStreamDevicesController::HasVideo() const {
230  return IsDeviceVideoCaptureRequestedAndAllowed();
231}
232
233const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const {
234  return request_.security_origin.spec();
235}
236
237void 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
365void 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
383int MediaStreamDevicesController::GetIconID() const {
384  if (HasVideo())
385    return IDR_INFOBAR_MEDIA_STREAM_CAMERA;
386
387  return IDR_INFOBAR_MEDIA_STREAM_MIC;
388}
389
390base::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
400base::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
409bool MediaStreamDevicesController::HasUserGesture() const {
410  return request_.user_gesture;
411}
412
413GURL MediaStreamDevicesController::GetRequestingHostname() const {
414  return request_.security_origin;
415}
416
417void 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
429void MediaStreamDevicesController::PermissionDenied() {
430  UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
431                            kDeny, kPermissionActionsMax);
432  Deny(true, content::MEDIA_DEVICE_PERMISSION_DENIED);
433}
434
435void MediaStreamDevicesController::Cancelled() {
436  UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
437                            kCancel, kPermissionActionsMax);
438  Deny(false, content::MEDIA_DEVICE_PERMISSION_DISMISSED);
439}
440
441void MediaStreamDevicesController::RequestFinished() {
442  delete this;
443}
444
445bool 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
499int 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
531bool 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
542bool MediaStreamDevicesController::IsSchemeSecure() const {
543  return request_.security_origin.SchemeIsSecure() ||
544      request_.security_origin.SchemeIs(extensions::kExtensionScheme);
545}
546
547void 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
578void MediaStreamDevicesController::NotifyUIRequestAccepted() const {
579  if (!content_settings_)
580    return;
581
582  content_settings_->OnMediaStreamPermissionSet(request_.security_origin,
583                                                request_permissions_);
584}
585
586void 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
603bool 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
611bool 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
619bool 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