Refactor WebsiteSettings to operate on a SecurityInfo
[chromium-blink-merge.git] / chrome / browser / media / media_stream_devices_controller.cc
blobef876bae3e02778d4552ca82041a58b1cf8d289b
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/host_content_settings_map_factory.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_permission.h"
15 #include "chrome/browser/media/media_stream_capture_indicator.h"
16 #include "chrome/browser/media/media_stream_device_permissions.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "components/content_settings/core/browser/host_content_settings_map.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 "content/public/common/origin_util.h"
29 #include "extensions/common/constants.h"
30 #include "grit/theme_resources.h"
31 #include "ui/base/l10n/l10n_util.h"
33 #if defined(OS_ANDROID)
34 #include "chrome/browser/android/preferences/pref_service_bridge.h"
35 #include "content/public/browser/android/content_view_core.h"
36 #include "ui/android/window_android.h"
37 #endif // OS_ANDROID
39 using content::BrowserThread;
41 namespace {
43 enum DevicePermissionActions {
44 kAllowHttps = 0,
45 kAllowHttp,
46 kDeny,
47 kCancel,
48 kPermissionActionsMax // Must always be last!
51 // Returns true if the given ContentSettingsType is being requested in
52 // |request|.
53 bool ContentTypeIsRequested(ContentSettingsType type,
54 const content::MediaStreamRequest& request) {
55 if (request.request_type == content::MEDIA_OPEN_DEVICE)
56 return true;
58 if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)
59 return request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE;
61 if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)
62 return request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE;
64 return false;
67 } // namespace
69 MediaStreamDevicesController::MediaStreamDevicesController(
70 content::WebContents* web_contents,
71 const content::MediaStreamRequest& request,
72 const content::MediaResponseCallback& callback)
73 : web_contents_(web_contents),
74 request_(request),
75 callback_(callback) {
76 if (request_.request_type == content::MEDIA_OPEN_DEVICE) {
77 UMA_HISTOGRAM_BOOLEAN("Pepper.SecureOrigin.MediaStreamRequest",
78 content::IsOriginSecure(request_.security_origin));
80 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext());
81 content_settings_ = TabSpecificContentSettings::FromWebContents(web_contents);
83 content::MediaStreamRequestResult denial_reason = content::MEDIA_DEVICE_OK;
84 old_audio_setting_ = GetContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
85 request_, &denial_reason);
86 old_video_setting_ = GetContentSetting(
87 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, request_, &denial_reason);
89 // If either setting is ask, we show the infobar.
90 if (old_audio_setting_ == CONTENT_SETTING_ASK ||
91 old_video_setting_ == CONTENT_SETTING_ASK) {
92 return;
95 // Otherwise we can run the callback immediately.
96 RunCallback(old_audio_setting_, old_video_setting_, denial_reason);
99 MediaStreamDevicesController::~MediaStreamDevicesController() {
100 if (!callback_.is_null()) {
101 callback_.Run(content::MediaStreamDevices(),
102 content::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
103 scoped_ptr<content::MediaStreamUI>());
107 // static
108 void MediaStreamDevicesController::RegisterProfilePrefs(
109 user_prefs::PrefRegistrySyncable* prefs) {
110 prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed, true);
111 prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed, true);
112 prefs->RegisterListPref(prefs::kVideoCaptureAllowedUrls);
113 prefs->RegisterListPref(prefs::kAudioCaptureAllowedUrls);
116 bool MediaStreamDevicesController::IsAskingForAudio() const {
117 return old_audio_setting_ == CONTENT_SETTING_ASK;
120 bool MediaStreamDevicesController::IsAskingForVideo() const {
121 return old_video_setting_ == CONTENT_SETTING_ASK;
124 const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const {
125 return request_.security_origin.spec();
128 int MediaStreamDevicesController::GetIconId() const {
129 if (IsAskingForVideo())
130 return IDR_INFOBAR_MEDIA_STREAM_CAMERA;
132 return IDR_INFOBAR_MEDIA_STREAM_MIC;
135 base::string16 MediaStreamDevicesController::GetMessageText() const {
136 int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO;
137 if (!IsAskingForAudio())
138 message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY;
139 else if (!IsAskingForVideo())
140 message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY;
141 return l10n_util::GetStringFUTF16(
142 message_id, base::UTF8ToUTF16(GetSecurityOriginSpec()));
145 base::string16 MediaStreamDevicesController::GetMessageTextFragment() const {
146 int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_PERMISSION_FRAGMENT;
147 if (!IsAskingForAudio())
148 message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_FRAGMENT;
149 else if (!IsAskingForVideo())
150 message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_FRAGMENT;
151 return l10n_util::GetStringUTF16(message_id);
154 bool MediaStreamDevicesController::HasUserGesture() const {
155 return request_.user_gesture;
158 GURL MediaStreamDevicesController::GetRequestingHostname() const {
159 return request_.security_origin;
162 void MediaStreamDevicesController::PermissionGranted() {
163 GURL origin(GetSecurityOriginSpec());
164 if (content::IsOriginSecure(origin)) {
165 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
166 kAllowHttps, kPermissionActionsMax);
167 } else {
168 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
169 kAllowHttp, kPermissionActionsMax);
171 RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
172 old_audio_setting_, CONTENT_SETTING_ALLOW),
173 GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
174 old_video_setting_, CONTENT_SETTING_ALLOW),
175 content::MEDIA_DEVICE_PERMISSION_DENIED);
178 void MediaStreamDevicesController::PermissionDenied() {
179 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
180 kDeny, kPermissionActionsMax);
181 RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
182 old_audio_setting_, CONTENT_SETTING_BLOCK),
183 GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
184 old_video_setting_, CONTENT_SETTING_BLOCK),
185 content::MEDIA_DEVICE_PERMISSION_DENIED);
188 void MediaStreamDevicesController::Cancelled() {
189 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
190 kCancel, kPermissionActionsMax);
191 RunCallback(old_audio_setting_, old_video_setting_,
192 content::MEDIA_DEVICE_PERMISSION_DISMISSED);
195 void MediaStreamDevicesController::RequestFinished() {
196 delete this;
199 content::MediaStreamDevices MediaStreamDevicesController::GetDevices(
200 ContentSetting audio_setting,
201 ContentSetting video_setting) {
202 bool audio_allowed = audio_setting == CONTENT_SETTING_ALLOW;
203 bool video_allowed = video_setting == CONTENT_SETTING_ALLOW;
205 if (!audio_allowed && !video_allowed)
206 return content::MediaStreamDevices();
208 content::MediaStreamDevices devices;
209 switch (request_.request_type) {
210 case content::MEDIA_OPEN_DEVICE: {
211 const content::MediaStreamDevice* device = NULL;
212 // For open device request, when requested device_id is empty, pick
213 // the first available of the given type. If requested device_id is
214 // not empty, return the desired device if it's available. Otherwise,
215 // return no device.
216 if (audio_allowed &&
217 request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
218 DCHECK_EQ(content::MEDIA_NO_SERVICE, request_.video_type);
219 if (!request_.requested_audio_device_id.empty()) {
220 device =
221 MediaCaptureDevicesDispatcher::GetInstance()
222 ->GetRequestedAudioDevice(request_.requested_audio_device_id);
223 } else {
224 device = MediaCaptureDevicesDispatcher::GetInstance()
225 ->GetFirstAvailableAudioDevice();
227 } else if (video_allowed &&
228 request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) {
229 DCHECK_EQ(content::MEDIA_NO_SERVICE, request_.audio_type);
230 // Pepper API opens only one device at a time.
231 if (!request_.requested_video_device_id.empty()) {
232 device =
233 MediaCaptureDevicesDispatcher::GetInstance()
234 ->GetRequestedVideoDevice(request_.requested_video_device_id);
235 } else {
236 device = MediaCaptureDevicesDispatcher::GetInstance()
237 ->GetFirstAvailableVideoDevice();
240 if (device)
241 devices.push_back(*device);
242 break;
244 case content::MEDIA_GENERATE_STREAM: {
245 bool get_default_audio_device = audio_allowed;
246 bool get_default_video_device = video_allowed;
248 // Get the exact audio or video device if an id is specified.
249 if (audio_allowed && !request_.requested_audio_device_id.empty()) {
250 const content::MediaStreamDevice* audio_device =
251 MediaCaptureDevicesDispatcher::GetInstance()
252 ->GetRequestedAudioDevice(request_.requested_audio_device_id);
253 if (audio_device) {
254 devices.push_back(*audio_device);
255 get_default_audio_device = false;
258 if (video_allowed && !request_.requested_video_device_id.empty()) {
259 const content::MediaStreamDevice* video_device =
260 MediaCaptureDevicesDispatcher::GetInstance()
261 ->GetRequestedVideoDevice(request_.requested_video_device_id);
262 if (video_device) {
263 devices.push_back(*video_device);
264 get_default_video_device = false;
268 // If either or both audio and video devices were requested but not
269 // specified by id, get the default devices.
270 if (get_default_audio_device || get_default_video_device) {
271 MediaCaptureDevicesDispatcher::GetInstance()
272 ->GetDefaultDevicesForProfile(profile_, get_default_audio_device,
273 get_default_video_device, &devices);
275 break;
277 case content::MEDIA_DEVICE_ACCESS: {
278 // Get the default devices for the request.
279 MediaCaptureDevicesDispatcher::GetInstance()->GetDefaultDevicesForProfile(
280 profile_, audio_allowed, video_allowed, &devices);
281 break;
283 case content::MEDIA_ENUMERATE_DEVICES: {
284 // Do nothing.
285 NOTREACHED();
286 break;
288 } // switch
290 if (audio_allowed) {
291 HostContentSettingsMapFactory::GetForProfile(profile_)
292 ->UpdateLastUsageByPattern(
293 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin),
294 ContentSettingsPattern::Wildcard(),
295 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC);
297 if (video_allowed) {
298 HostContentSettingsMapFactory::GetForProfile(profile_)
299 ->UpdateLastUsageByPattern(
300 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin),
301 ContentSettingsPattern::Wildcard(),
302 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
305 return devices;
308 void MediaStreamDevicesController::RunCallback(
309 ContentSetting audio_setting,
310 ContentSetting video_setting,
311 content::MediaStreamRequestResult denial_reason) {
312 StorePermission(audio_setting, video_setting);
313 UpdateTabSpecificContentSettings(audio_setting, video_setting);
315 content::MediaStreamDevices devices =
316 GetDevices(audio_setting, video_setting);
318 // If either audio or video are allowed then the callback should report
319 // success, otherwise we report |denial_reason|.
320 content::MediaStreamRequestResult request_result = content::MEDIA_DEVICE_OK;
321 if (audio_setting != CONTENT_SETTING_ALLOW &&
322 video_setting != CONTENT_SETTING_ALLOW) {
323 DCHECK_NE(content::MEDIA_DEVICE_OK, denial_reason);
324 request_result = denial_reason;
325 } else if (devices.empty()) {
326 // Even if one of the content settings was allowed, if there are no devices
327 // at this point we still report a failure.
328 request_result = content::MEDIA_DEVICE_NO_HARDWARE;
331 scoped_ptr<content::MediaStreamUI> ui;
332 if (!devices.empty()) {
333 ui = MediaCaptureDevicesDispatcher::GetInstance()
334 ->GetMediaStreamCaptureIndicator()
335 ->RegisterMediaStream(web_contents_, devices);
337 content::MediaResponseCallback cb = callback_;
338 callback_.Reset();
339 cb.Run(devices, request_result, ui.Pass());
342 void MediaStreamDevicesController::StorePermission(
343 ContentSetting new_audio_setting,
344 ContentSetting new_video_setting) const {
345 DCHECK_CURRENTLY_ON(BrowserThread::UI);
346 ContentSettingsPattern primary_pattern =
347 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin);
349 bool is_pepper_request = request_.request_type == content::MEDIA_OPEN_DEVICE;
351 if (IsAskingForAudio() && new_audio_setting != CONTENT_SETTING_ASK) {
352 if (ShouldPersistContentSetting(new_audio_setting, request_.security_origin,
353 is_pepper_request)) {
354 HostContentSettingsMapFactory::GetForProfile(profile_)->SetContentSetting(
355 primary_pattern, ContentSettingsPattern::Wildcard(),
356 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, std::string(),
357 new_audio_setting);
360 if (IsAskingForVideo() && new_video_setting != CONTENT_SETTING_ASK) {
361 if (ShouldPersistContentSetting(new_video_setting, request_.security_origin,
362 is_pepper_request)) {
363 HostContentSettingsMapFactory::GetForProfile(profile_)->SetContentSetting(
364 primary_pattern, ContentSettingsPattern::Wildcard(),
365 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, std::string(),
366 new_video_setting);
371 void MediaStreamDevicesController::UpdateTabSpecificContentSettings(
372 ContentSetting audio_setting,
373 ContentSetting video_setting) const {
374 if (!content_settings_)
375 return;
377 TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state =
378 TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED;
379 std::string selected_audio_device;
380 std::string selected_video_device;
381 std::string requested_audio_device = request_.requested_audio_device_id;
382 std::string requested_video_device = request_.requested_video_device_id;
384 // TODO(raymes): Why do we use the defaults here for the selected devices?
385 // Shouldn't we just use the devices that were actually selected?
386 PrefService* prefs = Profile::FromBrowserContext(
387 web_contents_->GetBrowserContext())->GetPrefs();
388 if (audio_setting != CONTENT_SETTING_DEFAULT) {
389 selected_audio_device =
390 requested_audio_device.empty()
391 ? prefs->GetString(prefs::kDefaultAudioCaptureDevice)
392 : requested_audio_device;
393 microphone_camera_state |=
394 TabSpecificContentSettings::MICROPHONE_ACCESSED |
395 (audio_setting == CONTENT_SETTING_ALLOW
397 : TabSpecificContentSettings::MICROPHONE_BLOCKED);
400 if (video_setting != CONTENT_SETTING_DEFAULT) {
401 selected_video_device =
402 requested_video_device.empty()
403 ? prefs->GetString(prefs::kDefaultVideoCaptureDevice)
404 : requested_video_device;
405 microphone_camera_state |=
406 TabSpecificContentSettings::CAMERA_ACCESSED |
407 (video_setting == CONTENT_SETTING_ALLOW
409 : TabSpecificContentSettings::CAMERA_BLOCKED);
412 content_settings_->OnMediaStreamPermissionSet(
413 request_.security_origin, microphone_camera_state, selected_audio_device,
414 selected_video_device, requested_audio_device, requested_video_device);
417 ContentSetting MediaStreamDevicesController::GetContentSetting(
418 ContentSettingsType content_type,
419 const content::MediaStreamRequest& request,
420 content::MediaStreamRequestResult* denial_reason) const {
421 DCHECK(content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
422 content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
424 std::string requested_device_id;
425 if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)
426 requested_device_id = request.requested_audio_device_id;
427 else if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)
428 requested_device_id = request.requested_video_device_id;
430 if (!IsUserAcceptAllowed(content_type)) {
431 *denial_reason = content::MEDIA_DEVICE_PERMISSION_DENIED;
432 return CONTENT_SETTING_BLOCK;
435 if (ContentTypeIsRequested(content_type, request)) {
436 MediaPermission permission(
437 content_type, request.request_type, request.security_origin,
438 web_contents_->GetLastCommittedURL().GetOrigin(), profile_);
439 return permission.GetPermissionStatusWithDeviceRequired(requested_device_id,
440 denial_reason);
442 // Return the default content setting if the device is not requested.
443 return CONTENT_SETTING_DEFAULT;
446 ContentSetting MediaStreamDevicesController::GetNewSetting(
447 ContentSettingsType content_type,
448 ContentSetting old_setting,
449 ContentSetting user_decision) const {
450 DCHECK(user_decision == CONTENT_SETTING_ALLOW ||
451 user_decision == CONTENT_SETTING_BLOCK);
452 ContentSetting result = old_setting;
453 if (old_setting == CONTENT_SETTING_ASK) {
454 if (user_decision == CONTENT_SETTING_ALLOW &&
455 IsUserAcceptAllowed(content_type)) {
456 result = CONTENT_SETTING_ALLOW;
457 } else if (user_decision == CONTENT_SETTING_BLOCK) {
458 result = CONTENT_SETTING_BLOCK;
461 return result;
464 bool MediaStreamDevicesController::IsUserAcceptAllowed(
465 ContentSettingsType content_type) const {
466 #if defined(OS_ANDROID)
467 content::ContentViewCore* cvc =
468 content::ContentViewCore::FromWebContents(web_contents_);
469 if (!cvc)
470 return false;
472 ui::WindowAndroid* window_android = cvc->GetWindowAndroid();
473 if (!window_android)
474 return false;
476 std::string android_permission =
477 PrefServiceBridge::GetAndroidPermissionForContentSetting(content_type);
478 bool android_permission_blocked = false;
479 if (!android_permission.empty()) {
480 android_permission_blocked =
481 !window_android->HasPermission(android_permission) &&
482 !window_android->CanRequestPermission(android_permission);
484 if (android_permission_blocked)
485 return false;
487 // Don't approve device requests if the tab was hidden.
488 // TODO(qinmin): Add a test for this. http://crbug.com/396869.
489 // TODO(raymes): Shouldn't this apply to all permissions not just audio/video?
490 return web_contents_->GetRenderWidgetHostView()->IsShowing();
491 #endif
492 return true;