Switch media stream permissions to use IsOriginSecure() instead of SchemeIsSecure().
[chromium-blink-merge.git] / chrome / browser / media / media_stream_devices_controller.cc
blob4a9339c995ca9bc77f7b26249600141bad86c50c
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.
5 #include "chrome/browser/media/media_stream_devices_controller.h"
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/tab_specific_content_settings.h"
12 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
13 #include "chrome/browser/media/media_permission.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/host_content_settings_map.h"
22 #include "components/content_settings/core/common/content_settings_pattern.h"
23 #include "components/pref_registry/pref_registry_syncable.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/render_widget_host_view.h"
26 #include "content/public/common/media_stream_request.h"
27 #include "content/public/common/origin_util.h"
28 #include "extensions/common/constants.h"
29 #include "grit/theme_resources.h"
30 #include "ui/base/l10n/l10n_util.h"
32 #if defined(OS_ANDROID)
33 #include "chrome/browser/android/preferences/pref_service_bridge.h"
34 #include "content/public/browser/android/content_view_core.h"
35 #include "ui/android/window_android.h"
36 #endif // OS_ANDROID
38 using content::BrowserThread;
40 namespace {
42 enum DevicePermissionActions {
43 kAllowHttps = 0,
44 kAllowHttp,
45 kDeny,
46 kCancel,
47 kPermissionActionsMax // Must always be last!
50 // Returns true if the given ContentSettingsType is being requested in
51 // |request|.
52 bool ContentTypeIsRequested(ContentSettingsType type,
53 const content::MediaStreamRequest& request) {
54 if (request.request_type == content::MEDIA_OPEN_DEVICE)
55 return true;
57 if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)
58 return request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE;
60 if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)
61 return request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE;
63 return false;
66 } // namespace
68 MediaStreamDevicesController::MediaStreamDevicesController(
69 content::WebContents* web_contents,
70 const content::MediaStreamRequest& request,
71 const content::MediaResponseCallback& callback)
72 : web_contents_(web_contents),
73 request_(request),
74 callback_(callback) {
75 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext());
76 content_settings_ = TabSpecificContentSettings::FromWebContents(web_contents);
78 content::MediaStreamRequestResult denial_reason = content::MEDIA_DEVICE_OK;
79 old_audio_setting_ = GetContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
80 request_, &denial_reason);
81 old_video_setting_ = GetContentSetting(
82 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, request_, &denial_reason);
84 // If either setting is ask, we show the infobar.
85 if (old_audio_setting_ == CONTENT_SETTING_ASK ||
86 old_video_setting_ == CONTENT_SETTING_ASK) {
87 return;
90 // Otherwise we can run the callback immediately.
91 RunCallback(old_audio_setting_, old_video_setting_, denial_reason);
94 MediaStreamDevicesController::~MediaStreamDevicesController() {
95 if (!callback_.is_null()) {
96 callback_.Run(content::MediaStreamDevices(),
97 content::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
98 scoped_ptr<content::MediaStreamUI>());
102 // static
103 void MediaStreamDevicesController::RegisterProfilePrefs(
104 user_prefs::PrefRegistrySyncable* prefs) {
105 prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed, true);
106 prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed, true);
107 prefs->RegisterListPref(prefs::kVideoCaptureAllowedUrls);
108 prefs->RegisterListPref(prefs::kAudioCaptureAllowedUrls);
111 bool MediaStreamDevicesController::IsAskingForAudio() const {
112 return old_audio_setting_ == CONTENT_SETTING_ASK;
115 bool MediaStreamDevicesController::IsAskingForVideo() const {
116 return old_video_setting_ == CONTENT_SETTING_ASK;
119 const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const {
120 return request_.security_origin.spec();
123 int MediaStreamDevicesController::GetIconID() const {
124 if (IsAskingForVideo())
125 return IDR_INFOBAR_MEDIA_STREAM_CAMERA;
127 return IDR_INFOBAR_MEDIA_STREAM_MIC;
130 base::string16 MediaStreamDevicesController::GetMessageText() const {
131 int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO;
132 if (!IsAskingForAudio())
133 message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY;
134 else if (!IsAskingForVideo())
135 message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY;
136 return l10n_util::GetStringFUTF16(
137 message_id, base::UTF8ToUTF16(GetSecurityOriginSpec()));
140 base::string16 MediaStreamDevicesController::GetMessageTextFragment() const {
141 int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_PERMISSION_FRAGMENT;
142 if (!IsAskingForAudio())
143 message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_FRAGMENT;
144 else if (!IsAskingForVideo())
145 message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_FRAGMENT;
146 return l10n_util::GetStringUTF16(message_id);
149 bool MediaStreamDevicesController::HasUserGesture() const {
150 return request_.user_gesture;
153 GURL MediaStreamDevicesController::GetRequestingHostname() const {
154 return request_.security_origin;
157 void MediaStreamDevicesController::PermissionGranted() {
158 GURL origin(GetSecurityOriginSpec());
159 if (content::IsOriginSecure(origin)) {
160 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
161 kAllowHttps, kPermissionActionsMax);
162 } else {
163 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
164 kAllowHttp, kPermissionActionsMax);
166 RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
167 old_audio_setting_, CONTENT_SETTING_ALLOW),
168 GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
169 old_video_setting_, CONTENT_SETTING_ALLOW),
170 content::MEDIA_DEVICE_PERMISSION_DENIED);
173 void MediaStreamDevicesController::PermissionDenied() {
174 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
175 kDeny, kPermissionActionsMax);
176 RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
177 old_audio_setting_, CONTENT_SETTING_BLOCK),
178 GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
179 old_video_setting_, CONTENT_SETTING_BLOCK),
180 content::MEDIA_DEVICE_PERMISSION_DENIED);
183 void MediaStreamDevicesController::Cancelled() {
184 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
185 kCancel, kPermissionActionsMax);
186 RunCallback(old_audio_setting_, old_video_setting_,
187 content::MEDIA_DEVICE_PERMISSION_DISMISSED);
190 void MediaStreamDevicesController::RequestFinished() {
191 delete this;
194 content::MediaStreamDevices MediaStreamDevicesController::GetDevices(
195 ContentSetting audio_setting,
196 ContentSetting video_setting) {
197 bool audio_allowed = audio_setting == CONTENT_SETTING_ALLOW;
198 bool video_allowed = video_setting == CONTENT_SETTING_ALLOW;
200 if (!audio_allowed && !video_allowed)
201 return content::MediaStreamDevices();
203 content::MediaStreamDevices devices;
204 switch (request_.request_type) {
205 case content::MEDIA_OPEN_DEVICE: {
206 const content::MediaStreamDevice* device = NULL;
207 // For open device request, when requested device_id is empty, pick
208 // the first available of the given type. If requested device_id is
209 // not empty, return the desired device if it's available. Otherwise,
210 // return no device.
211 if (audio_allowed &&
212 request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
213 DCHECK_EQ(content::MEDIA_NO_SERVICE, request_.video_type);
214 if (!request_.requested_audio_device_id.empty()) {
215 device =
216 MediaCaptureDevicesDispatcher::GetInstance()
217 ->GetRequestedAudioDevice(request_.requested_audio_device_id);
218 } else {
219 device = MediaCaptureDevicesDispatcher::GetInstance()
220 ->GetFirstAvailableAudioDevice();
222 } else if (video_allowed &&
223 request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) {
224 DCHECK_EQ(content::MEDIA_NO_SERVICE, request_.audio_type);
225 // Pepper API opens only one device at a time.
226 if (!request_.requested_video_device_id.empty()) {
227 device =
228 MediaCaptureDevicesDispatcher::GetInstance()
229 ->GetRequestedVideoDevice(request_.requested_video_device_id);
230 } else {
231 device = MediaCaptureDevicesDispatcher::GetInstance()
232 ->GetFirstAvailableVideoDevice();
235 if (device)
236 devices.push_back(*device);
237 break;
239 case content::MEDIA_GENERATE_STREAM: {
240 bool get_default_audio_device = audio_allowed;
241 bool get_default_video_device = video_allowed;
243 // Get the exact audio or video device if an id is specified.
244 if (audio_allowed && !request_.requested_audio_device_id.empty()) {
245 const content::MediaStreamDevice* audio_device =
246 MediaCaptureDevicesDispatcher::GetInstance()
247 ->GetRequestedAudioDevice(request_.requested_audio_device_id);
248 if (audio_device) {
249 devices.push_back(*audio_device);
250 get_default_audio_device = false;
253 if (video_allowed && !request_.requested_video_device_id.empty()) {
254 const content::MediaStreamDevice* video_device =
255 MediaCaptureDevicesDispatcher::GetInstance()
256 ->GetRequestedVideoDevice(request_.requested_video_device_id);
257 if (video_device) {
258 devices.push_back(*video_device);
259 get_default_video_device = false;
263 // If either or both audio and video devices were requested but not
264 // specified by id, get the default devices.
265 if (get_default_audio_device || get_default_video_device) {
266 MediaCaptureDevicesDispatcher::GetInstance()
267 ->GetDefaultDevicesForProfile(profile_, get_default_audio_device,
268 get_default_video_device, &devices);
270 break;
272 case content::MEDIA_DEVICE_ACCESS: {
273 // Get the default devices for the request.
274 MediaCaptureDevicesDispatcher::GetInstance()->GetDefaultDevicesForProfile(
275 profile_, audio_allowed, video_allowed, &devices);
276 break;
278 case content::MEDIA_ENUMERATE_DEVICES: {
279 // Do nothing.
280 NOTREACHED();
281 break;
283 } // switch
285 if (audio_allowed) {
286 profile_->GetHostContentSettingsMap()->UpdateLastUsageByPattern(
287 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin),
288 ContentSettingsPattern::Wildcard(),
289 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC);
291 if (video_allowed) {
292 profile_->GetHostContentSettingsMap()->UpdateLastUsageByPattern(
293 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin),
294 ContentSettingsPattern::Wildcard(),
295 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
298 return devices;
301 void MediaStreamDevicesController::RunCallback(
302 ContentSetting audio_setting,
303 ContentSetting video_setting,
304 content::MediaStreamRequestResult denial_reason) {
305 StorePermission(audio_setting, video_setting);
306 UpdateTabSpecificContentSettings(audio_setting, video_setting);
308 content::MediaStreamDevices devices =
309 GetDevices(audio_setting, video_setting);
311 // If either audio or video are allowed then the callback should report
312 // success, otherwise we report |denial_reason|.
313 content::MediaStreamRequestResult request_result = content::MEDIA_DEVICE_OK;
314 if (audio_setting != CONTENT_SETTING_ALLOW &&
315 video_setting != CONTENT_SETTING_ALLOW) {
316 DCHECK_NE(content::MEDIA_DEVICE_OK, denial_reason);
317 request_result = denial_reason;
318 } else if (devices.empty()) {
319 // Even if one of the content settings was allowed, if there are no devices
320 // at this point we still report a failure.
321 request_result = content::MEDIA_DEVICE_NO_HARDWARE;
324 scoped_ptr<content::MediaStreamUI> ui;
325 if (!devices.empty()) {
326 ui = MediaCaptureDevicesDispatcher::GetInstance()
327 ->GetMediaStreamCaptureIndicator()
328 ->RegisterMediaStream(web_contents_, devices);
330 content::MediaResponseCallback cb = callback_;
331 callback_.Reset();
332 cb.Run(devices, request_result, ui.Pass());
335 void MediaStreamDevicesController::StorePermission(
336 ContentSetting new_audio_setting,
337 ContentSetting new_video_setting) const {
338 DCHECK_CURRENTLY_ON(BrowserThread::UI);
339 ContentSettingsPattern primary_pattern =
340 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin);
342 if (IsAskingForAudio() && new_audio_setting != CONTENT_SETTING_ASK) {
343 if (ShouldPersistContentSetting(new_audio_setting, request_.security_origin,
344 request_.request_type)) {
345 profile_->GetHostContentSettingsMap()->SetContentSetting(
346 primary_pattern, ContentSettingsPattern::Wildcard(),
347 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, std::string(),
348 new_audio_setting);
351 if (IsAskingForVideo() && new_video_setting != CONTENT_SETTING_ASK) {
352 if (ShouldPersistContentSetting(new_video_setting, request_.security_origin,
353 request_.request_type)) {
354 profile_->GetHostContentSettingsMap()->SetContentSetting(
355 primary_pattern, ContentSettingsPattern::Wildcard(),
356 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, std::string(),
357 new_video_setting);
362 void MediaStreamDevicesController::UpdateTabSpecificContentSettings(
363 ContentSetting audio_setting,
364 ContentSetting video_setting) const {
365 if (!content_settings_)
366 return;
368 TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state =
369 TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED;
370 std::string selected_audio_device;
371 std::string selected_video_device;
372 std::string requested_audio_device = request_.requested_audio_device_id;
373 std::string requested_video_device = request_.requested_video_device_id;
375 // TODO(raymes): Why do we use the defaults here for the selected devices?
376 // Shouldn't we just use the devices that were actually selected?
377 PrefService* prefs = Profile::FromBrowserContext(
378 web_contents_->GetBrowserContext())->GetPrefs();
379 if (audio_setting != CONTENT_SETTING_DEFAULT) {
380 selected_audio_device =
381 requested_audio_device.empty()
382 ? prefs->GetString(prefs::kDefaultAudioCaptureDevice)
383 : requested_audio_device;
384 microphone_camera_state |=
385 TabSpecificContentSettings::MICROPHONE_ACCESSED |
386 (audio_setting == CONTENT_SETTING_ALLOW
388 : TabSpecificContentSettings::MICROPHONE_BLOCKED);
391 if (video_setting != CONTENT_SETTING_DEFAULT) {
392 selected_video_device =
393 requested_video_device.empty()
394 ? prefs->GetString(prefs::kDefaultVideoCaptureDevice)
395 : requested_video_device;
396 microphone_camera_state |=
397 TabSpecificContentSettings::CAMERA_ACCESSED |
398 (video_setting == CONTENT_SETTING_ALLOW
400 : TabSpecificContentSettings::CAMERA_BLOCKED);
403 content_settings_->OnMediaStreamPermissionSet(
404 request_.security_origin, microphone_camera_state, selected_audio_device,
405 selected_video_device, requested_audio_device, requested_video_device);
408 ContentSetting MediaStreamDevicesController::GetContentSetting(
409 ContentSettingsType content_type,
410 const content::MediaStreamRequest& request,
411 content::MediaStreamRequestResult* denial_reason) const {
412 DCHECK(content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
413 content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
415 std::string requested_device_id;
416 if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)
417 requested_device_id = request.requested_audio_device_id;
418 else if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)
419 requested_device_id = request.requested_video_device_id;
421 if (!IsUserAcceptAllowed(content_type)) {
422 *denial_reason = content::MEDIA_DEVICE_PERMISSION_DENIED;
423 return CONTENT_SETTING_BLOCK;
426 if (ContentTypeIsRequested(content_type, request)) {
427 MediaPermission permission(content_type, request.request_type,
428 request.security_origin, requested_device_id,
429 profile_);
430 return permission.GetPermissionStatus(denial_reason);
432 // Return the default content setting if the device is not requested.
433 return CONTENT_SETTING_DEFAULT;
436 ContentSetting MediaStreamDevicesController::GetNewSetting(
437 ContentSettingsType content_type,
438 ContentSetting old_setting,
439 ContentSetting user_decision) const {
440 DCHECK(user_decision == CONTENT_SETTING_ALLOW ||
441 user_decision == CONTENT_SETTING_BLOCK);
442 ContentSetting result = old_setting;
443 if (old_setting == CONTENT_SETTING_ASK) {
444 if (user_decision == CONTENT_SETTING_ALLOW &&
445 IsUserAcceptAllowed(content_type)) {
446 result = CONTENT_SETTING_ALLOW;
447 } else if (user_decision == CONTENT_SETTING_BLOCK) {
448 result = CONTENT_SETTING_BLOCK;
451 return result;
454 bool MediaStreamDevicesController::IsUserAcceptAllowed(
455 ContentSettingsType content_type) const {
456 #if defined(OS_ANDROID)
457 content::ContentViewCore* cvc =
458 content::ContentViewCore::FromWebContents(web_contents_);
459 if (!cvc)
460 return false;
462 ui::WindowAndroid* window_android = cvc->GetWindowAndroid();
463 if (!window_android)
464 return false;
466 std::string android_permission =
467 PrefServiceBridge::GetAndroidPermissionForContentSetting(content_type);
468 bool android_permission_blocked = false;
469 if (!android_permission.empty()) {
470 android_permission_blocked =
471 !window_android->HasPermission(android_permission) &&
472 !window_android->CanRequestPermission(android_permission);
474 if (android_permission_blocked)
475 return false;
477 // Don't approve device requests if the tab was hidden.
478 // TODO(qinmin): Add a test for this. http://crbug.com/396869.
479 // TODO(raymes): Shouldn't this apply to all permissions not just audio/video?
480 return web_contents_->GetRenderWidgetHostView()->IsShowing();
481 #endif
482 return true;