Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / media / media_stream_devices_controller.cc
blob6da7f54a920f4cb32056d4278974d1f4be677e7c
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 "grit/theme_resources.h"
28 #include "ui/base/l10n/l10n_util.h"
30 #if defined(OS_ANDROID)
31 #include "chrome/browser/android/preferences/pref_service_bridge.h"
32 #include "content/public/browser/android/content_view_core.h"
33 #include "ui/android/window_android.h"
34 #endif // OS_ANDROID
36 using content::BrowserThread;
38 namespace {
40 enum DevicePermissionActions {
41 kAllowHttps = 0,
42 kAllowHttp,
43 kDeny,
44 kCancel,
45 kPermissionActionsMax // Must always be last!
48 // Returns true if the given ContentSettingsType is being requested in
49 // |request|.
50 bool ContentTypeIsRequested(ContentSettingsType type,
51 const content::MediaStreamRequest& request) {
52 if (request.request_type == content::MEDIA_OPEN_DEVICE)
53 return true;
55 if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)
56 return request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE;
58 if (type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)
59 return request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE;
61 return false;
64 } // namespace
66 MediaStreamDevicesController::MediaStreamDevicesController(
67 content::WebContents* web_contents,
68 const content::MediaStreamRequest& request,
69 const content::MediaResponseCallback& callback)
70 : web_contents_(web_contents),
71 request_(request),
72 callback_(callback) {
73 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext());
74 content_settings_ = TabSpecificContentSettings::FromWebContents(web_contents);
76 content::MediaStreamRequestResult denial_reason = content::MEDIA_DEVICE_OK;
77 old_audio_setting_ = GetContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
78 request_, &denial_reason);
79 old_video_setting_ = GetContentSetting(
80 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, request_, &denial_reason);
82 // If either setting is ask, we show the infobar.
83 if (old_audio_setting_ == CONTENT_SETTING_ASK ||
84 old_video_setting_ == CONTENT_SETTING_ASK) {
85 return;
88 // Otherwise we can run the callback immediately.
89 RunCallback(old_audio_setting_, old_video_setting_, denial_reason);
92 MediaStreamDevicesController::~MediaStreamDevicesController() {
93 if (!callback_.is_null()) {
94 callback_.Run(content::MediaStreamDevices(),
95 content::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
96 scoped_ptr<content::MediaStreamUI>());
100 // static
101 void MediaStreamDevicesController::RegisterProfilePrefs(
102 user_prefs::PrefRegistrySyncable* prefs) {
103 prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed, true);
104 prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed, true);
105 prefs->RegisterListPref(prefs::kVideoCaptureAllowedUrls);
106 prefs->RegisterListPref(prefs::kAudioCaptureAllowedUrls);
109 bool MediaStreamDevicesController::IsAskingForAudio() const {
110 return old_audio_setting_ == CONTENT_SETTING_ASK;
113 bool MediaStreamDevicesController::IsAskingForVideo() const {
114 return old_video_setting_ == CONTENT_SETTING_ASK;
117 const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const {
118 return request_.security_origin.spec();
121 int MediaStreamDevicesController::GetIconID() const {
122 if (IsAskingForVideo())
123 return IDR_INFOBAR_MEDIA_STREAM_CAMERA;
125 return IDR_INFOBAR_MEDIA_STREAM_MIC;
128 base::string16 MediaStreamDevicesController::GetMessageText() const {
129 int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO;
130 if (!IsAskingForAudio())
131 message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY;
132 else if (!IsAskingForVideo())
133 message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY;
134 return l10n_util::GetStringFUTF16(
135 message_id, base::UTF8ToUTF16(GetSecurityOriginSpec()));
138 base::string16 MediaStreamDevicesController::GetMessageTextFragment() const {
139 int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_PERMISSION_FRAGMENT;
140 if (!IsAskingForAudio())
141 message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_FRAGMENT;
142 else if (!IsAskingForVideo())
143 message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_FRAGMENT;
144 return l10n_util::GetStringUTF16(message_id);
147 bool MediaStreamDevicesController::HasUserGesture() const {
148 return request_.user_gesture;
151 GURL MediaStreamDevicesController::GetRequestingHostname() const {
152 return request_.security_origin;
155 void MediaStreamDevicesController::PermissionGranted() {
156 GURL origin(GetSecurityOriginSpec());
157 if (origin.SchemeIsSecure()) {
158 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
159 kAllowHttps, kPermissionActionsMax);
160 } else {
161 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
162 kAllowHttp, kPermissionActionsMax);
164 RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
165 old_audio_setting_, CONTENT_SETTING_ALLOW),
166 GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
167 old_video_setting_, CONTENT_SETTING_ALLOW),
168 content::MEDIA_DEVICE_PERMISSION_DENIED);
171 void MediaStreamDevicesController::PermissionDenied() {
172 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
173 kDeny, kPermissionActionsMax);
174 RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
175 old_audio_setting_, CONTENT_SETTING_BLOCK),
176 GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
177 old_video_setting_, CONTENT_SETTING_BLOCK),
178 content::MEDIA_DEVICE_PERMISSION_DENIED);
181 void MediaStreamDevicesController::Cancelled() {
182 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
183 kCancel, kPermissionActionsMax);
184 RunCallback(old_audio_setting_, old_video_setting_,
185 content::MEDIA_DEVICE_PERMISSION_DISMISSED);
188 void MediaStreamDevicesController::RequestFinished() {
189 delete this;
192 content::MediaStreamDevices MediaStreamDevicesController::GetDevices(
193 ContentSetting audio_setting,
194 ContentSetting video_setting) {
195 bool audio_allowed = audio_setting == CONTENT_SETTING_ALLOW;
196 bool video_allowed = video_setting == CONTENT_SETTING_ALLOW;
198 if (!audio_allowed && !video_allowed)
199 return content::MediaStreamDevices();
201 content::MediaStreamDevices devices;
202 switch (request_.request_type) {
203 case content::MEDIA_OPEN_DEVICE: {
204 const content::MediaStreamDevice* device = NULL;
205 // For open device request, when requested device_id is empty, pick
206 // the first available of the given type. If requested device_id is
207 // not empty, return the desired device if it's available. Otherwise,
208 // return no device.
209 if (audio_allowed &&
210 request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
211 DCHECK_EQ(content::MEDIA_NO_SERVICE, request_.video_type);
212 if (!request_.requested_audio_device_id.empty()) {
213 device =
214 MediaCaptureDevicesDispatcher::GetInstance()
215 ->GetRequestedAudioDevice(request_.requested_audio_device_id);
216 } else {
217 device = MediaCaptureDevicesDispatcher::GetInstance()
218 ->GetFirstAvailableAudioDevice();
220 } else if (video_allowed &&
221 request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) {
222 DCHECK_EQ(content::MEDIA_NO_SERVICE, request_.audio_type);
223 // Pepper API opens only one device at a time.
224 if (!request_.requested_video_device_id.empty()) {
225 device =
226 MediaCaptureDevicesDispatcher::GetInstance()
227 ->GetRequestedVideoDevice(request_.requested_video_device_id);
228 } else {
229 device = MediaCaptureDevicesDispatcher::GetInstance()
230 ->GetFirstAvailableVideoDevice();
233 if (device)
234 devices.push_back(*device);
235 break;
237 case content::MEDIA_GENERATE_STREAM: {
238 bool get_default_audio_device = audio_allowed;
239 bool get_default_video_device = video_allowed;
241 // Get the exact audio or video device if an id is specified.
242 if (audio_allowed && !request_.requested_audio_device_id.empty()) {
243 const content::MediaStreamDevice* audio_device =
244 MediaCaptureDevicesDispatcher::GetInstance()
245 ->GetRequestedAudioDevice(request_.requested_audio_device_id);
246 if (audio_device) {
247 devices.push_back(*audio_device);
248 get_default_audio_device = false;
251 if (video_allowed && !request_.requested_video_device_id.empty()) {
252 const content::MediaStreamDevice* video_device =
253 MediaCaptureDevicesDispatcher::GetInstance()
254 ->GetRequestedVideoDevice(request_.requested_video_device_id);
255 if (video_device) {
256 devices.push_back(*video_device);
257 get_default_video_device = false;
261 // If either or both audio and video devices were requested but not
262 // specified by id, get the default devices.
263 if (get_default_audio_device || get_default_video_device) {
264 MediaCaptureDevicesDispatcher::GetInstance()
265 ->GetDefaultDevicesForProfile(profile_, get_default_audio_device,
266 get_default_video_device, &devices);
268 break;
270 case content::MEDIA_DEVICE_ACCESS: {
271 // Get the default devices for the request.
272 MediaCaptureDevicesDispatcher::GetInstance()->GetDefaultDevicesForProfile(
273 profile_, audio_allowed, video_allowed, &devices);
274 break;
276 case content::MEDIA_ENUMERATE_DEVICES: {
277 // Do nothing.
278 NOTREACHED();
279 break;
281 } // switch
283 if (audio_allowed) {
284 profile_->GetHostContentSettingsMap()->UpdateLastUsageByPattern(
285 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin),
286 ContentSettingsPattern::Wildcard(),
287 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC);
289 if (video_allowed) {
290 profile_->GetHostContentSettingsMap()->UpdateLastUsageByPattern(
291 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin),
292 ContentSettingsPattern::Wildcard(),
293 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
296 return devices;
299 void MediaStreamDevicesController::RunCallback(
300 ContentSetting audio_setting,
301 ContentSetting video_setting,
302 content::MediaStreamRequestResult denial_reason) {
303 StorePermission(audio_setting, video_setting);
304 UpdateTabSpecificContentSettings(audio_setting, video_setting);
306 content::MediaStreamDevices devices =
307 GetDevices(audio_setting, video_setting);
309 // If either audio or video are allowed then the callback should report
310 // success, otherwise we report |denial_reason|.
311 content::MediaStreamRequestResult request_result = content::MEDIA_DEVICE_OK;
312 if (audio_setting != CONTENT_SETTING_ALLOW &&
313 video_setting != CONTENT_SETTING_ALLOW) {
314 DCHECK_NE(content::MEDIA_DEVICE_OK, denial_reason);
315 request_result = denial_reason;
316 } else if (devices.empty()) {
317 // Even if one of the content settings was allowed, if there are no devices
318 // at this point we still report a failure.
319 request_result = content::MEDIA_DEVICE_NO_HARDWARE;
322 scoped_ptr<content::MediaStreamUI> ui;
323 if (!devices.empty()) {
324 ui = MediaCaptureDevicesDispatcher::GetInstance()
325 ->GetMediaStreamCaptureIndicator()
326 ->RegisterMediaStream(web_contents_, devices);
328 content::MediaResponseCallback cb = callback_;
329 callback_.Reset();
330 cb.Run(devices, request_result, ui.Pass());
333 void MediaStreamDevicesController::StorePermission(
334 ContentSetting new_audio_setting,
335 ContentSetting new_video_setting) const {
336 DCHECK_CURRENTLY_ON(BrowserThread::UI);
338 ContentSettingsPattern primary_pattern =
339 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin);
341 if (IsAskingForAudio() && new_audio_setting != CONTENT_SETTING_ASK) {
342 if (ShouldPersistContentSetting(new_audio_setting, request_.security_origin,
343 request_.request_type)) {
344 profile_->GetHostContentSettingsMap()->SetContentSetting(
345 primary_pattern, ContentSettingsPattern::Wildcard(),
346 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, std::string(),
347 new_audio_setting);
350 if (IsAskingForVideo() && new_video_setting != CONTENT_SETTING_ASK) {
351 if (ShouldPersistContentSetting(new_video_setting, request_.security_origin,
352 request_.request_type)) {
353 profile_->GetHostContentSettingsMap()->SetContentSetting(
354 primary_pattern, ContentSettingsPattern::Wildcard(),
355 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, std::string(),
356 new_video_setting);
361 void MediaStreamDevicesController::UpdateTabSpecificContentSettings(
362 ContentSetting audio_setting,
363 ContentSetting video_setting) const {
364 if (!content_settings_)
365 return;
367 TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state =
368 TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED;
369 std::string selected_audio_device;
370 std::string selected_video_device;
371 std::string requested_audio_device = request_.requested_audio_device_id;
372 std::string requested_video_device = request_.requested_video_device_id;
374 // TODO(raymes): Why do we use the defaults here for the selected devices?
375 // Shouldn't we just use the devices that were actually selected?
376 PrefService* prefs = Profile::FromBrowserContext(
377 web_contents_->GetBrowserContext())->GetPrefs();
378 if (audio_setting != CONTENT_SETTING_DEFAULT) {
379 selected_audio_device =
380 requested_audio_device.empty()
381 ? prefs->GetString(prefs::kDefaultAudioCaptureDevice)
382 : requested_audio_device;
383 microphone_camera_state |=
384 TabSpecificContentSettings::MICROPHONE_ACCESSED |
385 (audio_setting == CONTENT_SETTING_ALLOW
387 : TabSpecificContentSettings::MICROPHONE_BLOCKED);
390 if (video_setting != CONTENT_SETTING_DEFAULT) {
391 selected_video_device =
392 requested_video_device.empty()
393 ? prefs->GetString(prefs::kDefaultVideoCaptureDevice)
394 : requested_video_device;
395 microphone_camera_state |=
396 TabSpecificContentSettings::CAMERA_ACCESSED |
397 (video_setting == CONTENT_SETTING_ALLOW
399 : TabSpecificContentSettings::CAMERA_BLOCKED);
402 content_settings_->OnMediaStreamPermissionSet(
403 request_.security_origin, microphone_camera_state, selected_audio_device,
404 selected_video_device, requested_audio_device, requested_video_device);
407 ContentSetting MediaStreamDevicesController::GetContentSetting(
408 ContentSettingsType content_type,
409 const content::MediaStreamRequest& request,
410 content::MediaStreamRequestResult* denial_reason) const {
411 DCHECK(content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
412 content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
414 std::string requested_device_id;
415 if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)
416 requested_device_id = request.requested_audio_device_id;
417 else if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)
418 requested_device_id = request.requested_video_device_id;
420 if (!IsUserAcceptAllowed(content_type)) {
421 *denial_reason = content::MEDIA_DEVICE_PERMISSION_DENIED;
422 return CONTENT_SETTING_BLOCK;
425 if (ContentTypeIsRequested(content_type, request)) {
426 MediaPermission permission(content_type, request.request_type,
427 request.security_origin, requested_device_id,
428 profile_);
429 return permission.GetPermissionStatus(denial_reason);
431 // Return the default content setting if the device is not requested.
432 return CONTENT_SETTING_DEFAULT;
435 ContentSetting MediaStreamDevicesController::GetNewSetting(
436 ContentSettingsType content_type,
437 ContentSetting old_setting,
438 ContentSetting user_decision) const {
439 DCHECK(user_decision == CONTENT_SETTING_ALLOW ||
440 user_decision == CONTENT_SETTING_BLOCK);
441 ContentSetting result = old_setting;
442 if (old_setting == CONTENT_SETTING_ASK) {
443 if (user_decision == CONTENT_SETTING_ALLOW &&
444 IsUserAcceptAllowed(content_type))
445 result = CONTENT_SETTING_ALLOW;
446 else if (user_decision == CONTENT_SETTING_BLOCK)
447 result = CONTENT_SETTING_BLOCK;
449 return result;
452 bool MediaStreamDevicesController::IsUserAcceptAllowed(
453 ContentSettingsType content_type) const {
454 #if defined(OS_ANDROID)
455 content::ContentViewCore* cvc =
456 content::ContentViewCore::FromWebContents(web_contents_);
457 if (!cvc)
458 return false;
460 ui::WindowAndroid* window_android = cvc->GetWindowAndroid();
461 if (!window_android)
462 return false;
464 std::string android_permission =
465 PrefServiceBridge::GetAndroidPermissionForContentSetting(content_type);
466 bool android_permission_blocked = false;
467 if (!android_permission.empty()) {
468 android_permission_blocked =
469 !window_android->HasPermission(android_permission) &&
470 !window_android->CanRequestPermission(android_permission);
472 if (android_permission_blocked)
473 return false;
475 // Don't approve device requests if the tab was hidden.
476 // TODO(qinmin): Add a test for this. http://crbug.com/396869.
477 // TODO(raymes): Shouldn't this apply to all permissions not just audio/video?
478 return web_contents_->GetRenderWidgetHostView()->IsShowing();
479 #endif
480 return true;