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