Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / media / media_stream_devices_controller.cc
blob9ce4f62414b6d8c1b1c40507fd6ed335d6619729
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 if (request_.request_type == content::MEDIA_OPEN_DEVICE) {
76 UMA_HISTOGRAM_BOOLEAN("Pepper.SecureOrigin.MediaStreamRequest",
77 content::IsOriginSecure(request_.security_origin));
79 profile_ = Profile::FromBrowserContext(web_contents->GetBrowserContext());
80 content_settings_ = TabSpecificContentSettings::FromWebContents(web_contents);
82 content::MediaStreamRequestResult denial_reason = content::MEDIA_DEVICE_OK;
83 old_audio_setting_ = GetContentSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
84 request_, &denial_reason);
85 old_video_setting_ = GetContentSetting(
86 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, request_, &denial_reason);
88 // If either setting is ask, we show the infobar.
89 if (old_audio_setting_ == CONTENT_SETTING_ASK ||
90 old_video_setting_ == CONTENT_SETTING_ASK) {
91 return;
94 // Otherwise we can run the callback immediately.
95 RunCallback(old_audio_setting_, old_video_setting_, denial_reason);
98 MediaStreamDevicesController::~MediaStreamDevicesController() {
99 if (!callback_.is_null()) {
100 callback_.Run(content::MediaStreamDevices(),
101 content::MEDIA_DEVICE_FAILED_DUE_TO_SHUTDOWN,
102 scoped_ptr<content::MediaStreamUI>());
106 // static
107 void MediaStreamDevicesController::RegisterProfilePrefs(
108 user_prefs::PrefRegistrySyncable* prefs) {
109 prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed, true);
110 prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed, true);
111 prefs->RegisterListPref(prefs::kVideoCaptureAllowedUrls);
112 prefs->RegisterListPref(prefs::kAudioCaptureAllowedUrls);
115 bool MediaStreamDevicesController::IsAskingForAudio() const {
116 return old_audio_setting_ == CONTENT_SETTING_ASK;
119 bool MediaStreamDevicesController::IsAskingForVideo() const {
120 return old_video_setting_ == CONTENT_SETTING_ASK;
123 const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const {
124 return request_.security_origin.spec();
127 int MediaStreamDevicesController::GetIconId() const {
128 if (IsAskingForVideo())
129 return IDR_INFOBAR_MEDIA_STREAM_CAMERA;
131 return IDR_INFOBAR_MEDIA_STREAM_MIC;
134 base::string16 MediaStreamDevicesController::GetMessageText() const {
135 int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO;
136 if (!IsAskingForAudio())
137 message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY;
138 else if (!IsAskingForVideo())
139 message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY;
140 return l10n_util::GetStringFUTF16(
141 message_id, base::UTF8ToUTF16(GetSecurityOriginSpec()));
144 base::string16 MediaStreamDevicesController::GetMessageTextFragment() const {
145 int message_id = IDS_MEDIA_CAPTURE_AUDIO_AND_VIDEO_PERMISSION_FRAGMENT;
146 if (!IsAskingForAudio())
147 message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_FRAGMENT;
148 else if (!IsAskingForVideo())
149 message_id = IDS_MEDIA_CAPTURE_AUDIO_ONLY_PERMISSION_FRAGMENT;
150 return l10n_util::GetStringUTF16(message_id);
153 bool MediaStreamDevicesController::HasUserGesture() const {
154 return request_.user_gesture;
157 GURL MediaStreamDevicesController::GetRequestingHostname() const {
158 return request_.security_origin;
161 void MediaStreamDevicesController::PermissionGranted() {
162 GURL origin(GetSecurityOriginSpec());
163 if (content::IsOriginSecure(origin)) {
164 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
165 kAllowHttps, kPermissionActionsMax);
166 } else {
167 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
168 kAllowHttp, kPermissionActionsMax);
170 RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
171 old_audio_setting_, CONTENT_SETTING_ALLOW),
172 GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
173 old_video_setting_, CONTENT_SETTING_ALLOW),
174 content::MEDIA_DEVICE_PERMISSION_DENIED);
177 void MediaStreamDevicesController::PermissionDenied() {
178 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
179 kDeny, kPermissionActionsMax);
180 RunCallback(GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
181 old_audio_setting_, CONTENT_SETTING_BLOCK),
182 GetNewSetting(CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
183 old_video_setting_, CONTENT_SETTING_BLOCK),
184 content::MEDIA_DEVICE_PERMISSION_DENIED);
187 void MediaStreamDevicesController::Cancelled() {
188 UMA_HISTOGRAM_ENUMERATION("Media.DevicePermissionActions",
189 kCancel, kPermissionActionsMax);
190 RunCallback(old_audio_setting_, old_video_setting_,
191 content::MEDIA_DEVICE_PERMISSION_DISMISSED);
194 void MediaStreamDevicesController::RequestFinished() {
195 delete this;
198 content::MediaStreamDevices MediaStreamDevicesController::GetDevices(
199 ContentSetting audio_setting,
200 ContentSetting video_setting) {
201 bool audio_allowed = audio_setting == CONTENT_SETTING_ALLOW;
202 bool video_allowed = video_setting == CONTENT_SETTING_ALLOW;
204 if (!audio_allowed && !video_allowed)
205 return content::MediaStreamDevices();
207 content::MediaStreamDevices devices;
208 switch (request_.request_type) {
209 case content::MEDIA_OPEN_DEVICE: {
210 const content::MediaStreamDevice* device = NULL;
211 // For open device request, when requested device_id is empty, pick
212 // the first available of the given type. If requested device_id is
213 // not empty, return the desired device if it's available. Otherwise,
214 // return no device.
215 if (audio_allowed &&
216 request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
217 DCHECK_EQ(content::MEDIA_NO_SERVICE, request_.video_type);
218 if (!request_.requested_audio_device_id.empty()) {
219 device =
220 MediaCaptureDevicesDispatcher::GetInstance()
221 ->GetRequestedAudioDevice(request_.requested_audio_device_id);
222 } else {
223 device = MediaCaptureDevicesDispatcher::GetInstance()
224 ->GetFirstAvailableAudioDevice();
226 } else if (video_allowed &&
227 request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) {
228 DCHECK_EQ(content::MEDIA_NO_SERVICE, request_.audio_type);
229 // Pepper API opens only one device at a time.
230 if (!request_.requested_video_device_id.empty()) {
231 device =
232 MediaCaptureDevicesDispatcher::GetInstance()
233 ->GetRequestedVideoDevice(request_.requested_video_device_id);
234 } else {
235 device = MediaCaptureDevicesDispatcher::GetInstance()
236 ->GetFirstAvailableVideoDevice();
239 if (device)
240 devices.push_back(*device);
241 break;
243 case content::MEDIA_GENERATE_STREAM: {
244 bool get_default_audio_device = audio_allowed;
245 bool get_default_video_device = video_allowed;
247 // Get the exact audio or video device if an id is specified.
248 if (audio_allowed && !request_.requested_audio_device_id.empty()) {
249 const content::MediaStreamDevice* audio_device =
250 MediaCaptureDevicesDispatcher::GetInstance()
251 ->GetRequestedAudioDevice(request_.requested_audio_device_id);
252 if (audio_device) {
253 devices.push_back(*audio_device);
254 get_default_audio_device = false;
257 if (video_allowed && !request_.requested_video_device_id.empty()) {
258 const content::MediaStreamDevice* video_device =
259 MediaCaptureDevicesDispatcher::GetInstance()
260 ->GetRequestedVideoDevice(request_.requested_video_device_id);
261 if (video_device) {
262 devices.push_back(*video_device);
263 get_default_video_device = false;
267 // If either or both audio and video devices were requested but not
268 // specified by id, get the default devices.
269 if (get_default_audio_device || get_default_video_device) {
270 MediaCaptureDevicesDispatcher::GetInstance()
271 ->GetDefaultDevicesForProfile(profile_, get_default_audio_device,
272 get_default_video_device, &devices);
274 break;
276 case content::MEDIA_DEVICE_ACCESS: {
277 // Get the default devices for the request.
278 MediaCaptureDevicesDispatcher::GetInstance()->GetDefaultDevicesForProfile(
279 profile_, audio_allowed, video_allowed, &devices);
280 break;
282 case content::MEDIA_ENUMERATE_DEVICES: {
283 // Do nothing.
284 NOTREACHED();
285 break;
287 } // switch
289 if (audio_allowed) {
290 profile_->GetHostContentSettingsMap()->UpdateLastUsageByPattern(
291 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin),
292 ContentSettingsPattern::Wildcard(),
293 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC);
295 if (video_allowed) {
296 profile_->GetHostContentSettingsMap()->UpdateLastUsageByPattern(
297 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin),
298 ContentSettingsPattern::Wildcard(),
299 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
302 return devices;
305 void MediaStreamDevicesController::RunCallback(
306 ContentSetting audio_setting,
307 ContentSetting video_setting,
308 content::MediaStreamRequestResult denial_reason) {
309 StorePermission(audio_setting, video_setting);
310 UpdateTabSpecificContentSettings(audio_setting, video_setting);
312 content::MediaStreamDevices devices =
313 GetDevices(audio_setting, video_setting);
315 // If either audio or video are allowed then the callback should report
316 // success, otherwise we report |denial_reason|.
317 content::MediaStreamRequestResult request_result = content::MEDIA_DEVICE_OK;
318 if (audio_setting != CONTENT_SETTING_ALLOW &&
319 video_setting != CONTENT_SETTING_ALLOW) {
320 DCHECK_NE(content::MEDIA_DEVICE_OK, denial_reason);
321 request_result = denial_reason;
322 } else if (devices.empty()) {
323 // Even if one of the content settings was allowed, if there are no devices
324 // at this point we still report a failure.
325 request_result = content::MEDIA_DEVICE_NO_HARDWARE;
328 scoped_ptr<content::MediaStreamUI> ui;
329 if (!devices.empty()) {
330 ui = MediaCaptureDevicesDispatcher::GetInstance()
331 ->GetMediaStreamCaptureIndicator()
332 ->RegisterMediaStream(web_contents_, devices);
334 content::MediaResponseCallback cb = callback_;
335 callback_.Reset();
336 cb.Run(devices, request_result, ui.Pass());
339 void MediaStreamDevicesController::StorePermission(
340 ContentSetting new_audio_setting,
341 ContentSetting new_video_setting) const {
342 DCHECK_CURRENTLY_ON(BrowserThread::UI);
343 ContentSettingsPattern primary_pattern =
344 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin);
346 bool is_pepper_request = request_.request_type == content::MEDIA_OPEN_DEVICE;
348 if (IsAskingForAudio() && new_audio_setting != CONTENT_SETTING_ASK) {
349 if (ShouldPersistContentSetting(new_audio_setting, request_.security_origin,
350 is_pepper_request)) {
351 profile_->GetHostContentSettingsMap()->SetContentSetting(
352 primary_pattern, ContentSettingsPattern::Wildcard(),
353 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, std::string(),
354 new_audio_setting);
357 if (IsAskingForVideo() && new_video_setting != CONTENT_SETTING_ASK) {
358 if (ShouldPersistContentSetting(new_video_setting, request_.security_origin,
359 is_pepper_request)) {
360 profile_->GetHostContentSettingsMap()->SetContentSetting(
361 primary_pattern, ContentSettingsPattern::Wildcard(),
362 CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, std::string(),
363 new_video_setting);
368 void MediaStreamDevicesController::UpdateTabSpecificContentSettings(
369 ContentSetting audio_setting,
370 ContentSetting video_setting) const {
371 if (!content_settings_)
372 return;
374 TabSpecificContentSettings::MicrophoneCameraState microphone_camera_state =
375 TabSpecificContentSettings::MICROPHONE_CAMERA_NOT_ACCESSED;
376 std::string selected_audio_device;
377 std::string selected_video_device;
378 std::string requested_audio_device = request_.requested_audio_device_id;
379 std::string requested_video_device = request_.requested_video_device_id;
381 // TODO(raymes): Why do we use the defaults here for the selected devices?
382 // Shouldn't we just use the devices that were actually selected?
383 PrefService* prefs = Profile::FromBrowserContext(
384 web_contents_->GetBrowserContext())->GetPrefs();
385 if (audio_setting != CONTENT_SETTING_DEFAULT) {
386 selected_audio_device =
387 requested_audio_device.empty()
388 ? prefs->GetString(prefs::kDefaultAudioCaptureDevice)
389 : requested_audio_device;
390 microphone_camera_state |=
391 TabSpecificContentSettings::MICROPHONE_ACCESSED |
392 (audio_setting == CONTENT_SETTING_ALLOW
394 : TabSpecificContentSettings::MICROPHONE_BLOCKED);
397 if (video_setting != CONTENT_SETTING_DEFAULT) {
398 selected_video_device =
399 requested_video_device.empty()
400 ? prefs->GetString(prefs::kDefaultVideoCaptureDevice)
401 : requested_video_device;
402 microphone_camera_state |=
403 TabSpecificContentSettings::CAMERA_ACCESSED |
404 (video_setting == CONTENT_SETTING_ALLOW
406 : TabSpecificContentSettings::CAMERA_BLOCKED);
409 content_settings_->OnMediaStreamPermissionSet(
410 request_.security_origin, microphone_camera_state, selected_audio_device,
411 selected_video_device, requested_audio_device, requested_video_device);
414 ContentSetting MediaStreamDevicesController::GetContentSetting(
415 ContentSettingsType content_type,
416 const content::MediaStreamRequest& request,
417 content::MediaStreamRequestResult* denial_reason) const {
418 DCHECK(content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC ||
419 content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
421 std::string requested_device_id;
422 if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC)
423 requested_device_id = request.requested_audio_device_id;
424 else if (content_type == CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA)
425 requested_device_id = request.requested_video_device_id;
427 if (!IsUserAcceptAllowed(content_type)) {
428 *denial_reason = content::MEDIA_DEVICE_PERMISSION_DENIED;
429 return CONTENT_SETTING_BLOCK;
432 if (ContentTypeIsRequested(content_type, request)) {
433 MediaPermission permission(
434 content_type, request.request_type, request.security_origin,
435 web_contents_->GetLastCommittedURL().GetOrigin(), profile_);
436 return permission.GetPermissionStatusWithDeviceRequired(requested_device_id,
437 denial_reason);
439 // Return the default content setting if the device is not requested.
440 return CONTENT_SETTING_DEFAULT;
443 ContentSetting MediaStreamDevicesController::GetNewSetting(
444 ContentSettingsType content_type,
445 ContentSetting old_setting,
446 ContentSetting user_decision) const {
447 DCHECK(user_decision == CONTENT_SETTING_ALLOW ||
448 user_decision == CONTENT_SETTING_BLOCK);
449 ContentSetting result = old_setting;
450 if (old_setting == CONTENT_SETTING_ASK) {
451 if (user_decision == CONTENT_SETTING_ALLOW &&
452 IsUserAcceptAllowed(content_type)) {
453 result = CONTENT_SETTING_ALLOW;
454 } else if (user_decision == CONTENT_SETTING_BLOCK) {
455 result = CONTENT_SETTING_BLOCK;
458 return result;
461 bool MediaStreamDevicesController::IsUserAcceptAllowed(
462 ContentSettingsType content_type) const {
463 #if defined(OS_ANDROID)
464 content::ContentViewCore* cvc =
465 content::ContentViewCore::FromWebContents(web_contents_);
466 if (!cvc)
467 return false;
469 ui::WindowAndroid* window_android = cvc->GetWindowAndroid();
470 if (!window_android)
471 return false;
473 std::string android_permission =
474 PrefServiceBridge::GetAndroidPermissionForContentSetting(content_type);
475 bool android_permission_blocked = false;
476 if (!android_permission.empty()) {
477 android_permission_blocked =
478 !window_android->HasPermission(android_permission) &&
479 !window_android->CanRequestPermission(android_permission);
481 if (android_permission_blocked)
482 return false;
484 // Don't approve device requests if the tab was hidden.
485 // TODO(qinmin): Add a test for this. http://crbug.com/396869.
486 // TODO(raymes): Shouldn't this apply to all permissions not just audio/video?
487 return web_contents_->GetRenderWidgetHostView()->IsShowing();
488 #endif
489 return true;