Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(209)

Side by Side Diff: chrome/browser/media/media_stream_devices_controller.cc

Issue 262763004: Per navigation sticky media permissions. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/media/media_stream_devices_controller.h" 5 #include "chrome/browser/media/media_stream_devices_controller.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/metrics/histogram.h" 8 #include "base/metrics/histogram.h"
9 #include "base/prefs/pref_service.h" 9 #include "base/prefs/pref_service.h"
10 #include "base/prefs/scoped_user_pref_update.h" 10 #include "base/prefs/scoped_user_pref_update.h"
11 #include "base/strings/utf_string_conversions.h" 11 #include "base/strings/utf_string_conversions.h"
12 #include "base/values.h" 12 #include "base/values.h"
13 #include "chrome/browser/content_settings/content_settings_provider.h" 13 #include "chrome/browser/content_settings/content_settings_provider.h"
14 #include "chrome/browser/content_settings/host_content_settings_map.h" 14 #include "chrome/browser/content_settings/host_content_settings_map.h"
15 #include "chrome/browser/content_settings/tab_specific_content_settings.h" 15 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
16 #include "chrome/browser/media/media_capture_devices_dispatcher.h" 16 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
17 #include "chrome/browser/media/media_stream_capture_indicator.h" 17 #include "chrome/browser/media/media_stream_capture_indicator.h"
18 #include "chrome/browser/profiles/profile.h" 18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser.h" 19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/common/chrome_switches.h" 20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/content_settings.h" 21 #include "chrome/common/content_settings.h"
22 #include "chrome/common/content_settings_pattern.h" 22 #include "chrome/common/content_settings_pattern.h"
23 #include "chrome/common/pref_names.h" 23 #include "chrome/common/pref_names.h"
24 #include "components/user_prefs/pref_registry_syncable.h" 24 #include "components/user_prefs/pref_registry_syncable.h"
25 #include "content/public/browser/browser_thread.h" 25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/navigation_entry.h"
26 #include "content/public/common/media_stream_request.h" 27 #include "content/public/common/media_stream_request.h"
27 #include "extensions/common/constants.h" 28 #include "extensions/common/constants.h"
28 #include "grit/generated_resources.h" 29 #include "grit/generated_resources.h"
29 #include "grit/theme_resources.h" 30 #include "grit/theme_resources.h"
30 #include "ui/base/l10n/l10n_util.h" 31 #include "ui/base/l10n/l10n_util.h"
31 32
32 #if defined(OS_CHROMEOS) 33 #if defined(OS_CHROMEOS)
33 #include "chrome/browser/chromeos/login/user_manager.h" 34 #include "chrome/browser/chromeos/login/user_manager.h"
34 #endif 35 #endif
35 36
36 using content::BrowserThread; 37 using content::BrowserThread;
37 38
38 namespace { 39 namespace {
39 40
41 // This prefix is combined with request security origins to store media access
42 // permissions that the user has granted a specific page navigation instance.
43 // The string value stored with the navigation instance will contain one or more
44 // kMediaPermissionXxx constants that indicates the permission(s) that the user
45 // has granted the page.
46 const char kMediaPermissionKeyPrefix[] = "media_permissions#";
47 const base::char16 kMediaPermissionAudio = static_cast<base::char16>('a');
48 const base::char16 kMediaPermissionVideo = static_cast<base::char16>('v');
49
40 bool HasAvailableDevicesForRequest(const content::MediaStreamRequest& request) { 50 bool HasAvailableDevicesForRequest(const content::MediaStreamRequest& request) {
41 bool has_audio_device = 51 const content::MediaStreamDevices* audio_devices =
42 request.audio_type == content::MEDIA_NO_SERVICE || 52 request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE ?
43 !MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices() 53 &MediaCaptureDevicesDispatcher::GetInstance()
44 .empty(); 54 ->GetAudioCaptureDevices() :
45 bool has_video_device = 55 NULL;
46 request.video_type == content::MEDIA_NO_SERVICE ||
47 !MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices()
48 .empty();
49 56
50 return has_audio_device && has_video_device; 57 const content::MediaStreamDevices* video_devices =
58 request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE ?
59 &MediaCaptureDevicesDispatcher::GetInstance()
60 ->GetVideoCaptureDevices() :
61 NULL;
62
63 // Check if we're being asked for audio and/or video and that either of those
64 // lists is empty. If they are, we do not have devices available for the
65 // request.
66 // TODO(tommi): It's kind of strange to have this here since if we fail this
67 // test, there'll be a UI shown that indicates to the user that access to
68 // non-existing audio/video devices has been denied. The user won't have
69 // any way to change that but there will be a UI shown which indicates that
70 // access is blocked.
71 if ((audio_devices != NULL && audio_devices->empty()) ||
72 (video_devices != NULL && video_devices->empty())) {
73 return false;
74 }
75
76 // Note: we check requested_[audio|video]_device_id before dereferencing
77 // [audio|video]_devices. If the requested device id is non-empty, then
78 // the corresponding device list must not be NULL.
79
80 if (!request.requested_audio_device_id.empty() &&
81 !audio_devices->FindById(request.requested_audio_device_id)) {
82 return false;
83 }
84
85 if (!request.requested_video_device_id.empty() &&
86 !video_devices->FindById(request.requested_video_device_id)) {
87 return false;
88 }
89
90 return true;
51 } 91 }
52 92
93 base::string16 GetMediaPermissionsFromNavigationEntry(
94 content::NavigationEntry* navigation_entry,
95 const content::MediaStreamRequest& request) {
96 const std::string key(kMediaPermissionKeyPrefix +
97 request.security_origin.spec());
98
99 base::string16 permissions;
100 if (!navigation_entry->GetExtraData(key, &permissions)) {
101 DCHECK(permissions.empty());
102 }
103
104 return permissions;
105 }
106
107 void SetMediaPermissionsForNavigationEntry(
108 content::NavigationEntry* navigation_entry,
109 const content::MediaStreamRequest& request,
110 const base::string16& permissions) {
111 const std::string key(kMediaPermissionKeyPrefix +
112 request.security_origin.spec());
113 permissions.empty() ?
114 navigation_entry->ClearExtraData(key) :
115 navigation_entry->SetExtraData(key, permissions);
no longer working on chromium 2014/05/05 08:14:36 what happen if it sets the same entry to the navig
tommi (sloooow) - chröme 2014/05/05 08:28:53 Not sure if I understand the question, but if you
no longer working on chromium 2014/05/05 08:43:03 Thanks, I was just trying to understand the behavi
tommi (sloooow) - chröme 2014/05/05 08:50:31 Ah, ok, yeah. I wasn't sure what "entry to the na
116 }
117
118 void SetMediaPermissionsForNavigationEntry(
119 content::NavigationEntry* navigation_entry,
120 const content::MediaStreamRequest& request,
121 bool allow_audio,
122 bool allow_video) {
123 base::string16 permissions;
124 if (allow_audio)
125 permissions += kMediaPermissionAudio;
126 if (allow_video)
127 permissions += kMediaPermissionVideo;
128 SetMediaPermissionsForNavigationEntry(navigation_entry, request, permissions);
129 }
130
131 bool IsRequestAllowedByNavigationEntry(
132 content::NavigationEntry* navigation_entry,
133 const content::MediaStreamRequest& request) {
134 using content::MEDIA_NO_SERVICE;
135 using content::MEDIA_DEVICE_AUDIO_CAPTURE;
136 using content::MEDIA_DEVICE_VIDEO_CAPTURE;
137
138 // If we aren't being asked for at least one of these two, fail right away.
139 if (!navigation_entry ||
140 (request.audio_type != MEDIA_DEVICE_AUDIO_CAPTURE &&
141 request.video_type != MEDIA_DEVICE_VIDEO_CAPTURE)) {
142 return false;
143 }
144
145 base::string16 permissions =
146 GetMediaPermissionsFromNavigationEntry(navigation_entry, request);
147
148 bool audio_requested_and_granted =
149 request.audio_type == MEDIA_DEVICE_AUDIO_CAPTURE &&
150 permissions.find(kMediaPermissionAudio) != base::string16::npos;
151
152 bool video_requested_and_granted =
153 request.video_type == MEDIA_DEVICE_VIDEO_CAPTURE &&
154 permissions.find(kMediaPermissionVideo) != base::string16::npos;
155
156 return
157 (audio_requested_and_granted || request.audio_type == MEDIA_NO_SERVICE) &&
158 (video_requested_and_granted || request.video_type == MEDIA_NO_SERVICE);
159 }
160
161
53 bool IsInKioskMode() { 162 bool IsInKioskMode() {
54 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode)) 163 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
55 return true; 164 return true;
56 165
57 #if defined(OS_CHROMEOS) 166 #if defined(OS_CHROMEOS)
58 const chromeos::UserManager* user_manager = chromeos::UserManager::Get(); 167 const chromeos::UserManager* user_manager = chromeos::UserManager::Get();
59 return user_manager && user_manager->IsLoggedInAsKioskApp(); 168 return user_manager && user_manager->IsLoggedInAsKioskApp();
60 #else 169 #else
61 return false; 170 return false;
62 #endif 171 #endif
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after
180 Deny(false, content::MEDIA_DEVICE_NO_HARDWARE); 289 Deny(false, content::MEDIA_DEVICE_NO_HARDWARE);
181 return true; 290 return true;
182 } 291 }
183 292
184 // Check if any allow exception has been made for this request. 293 // Check if any allow exception has been made for this request.
185 if (IsRequestAllowedByDefault()) { 294 if (IsRequestAllowedByDefault()) {
186 Accept(false); 295 Accept(false);
187 return true; 296 return true;
188 } 297 }
189 298
299 // Check if the navigation entry has previously been granted access.
300 content::NavigationEntry* navigation_entry =
301 web_contents_->GetController().GetVisibleEntry();
302 if (IsRequestAllowedByNavigationEntry(navigation_entry, request_)) {
no longer working on chromium 2014/05/05 08:14:36 Does this navigation entry has higher priority tha
tommi (sloooow) - chröme 2014/05/05 08:28:53 Permissions are stored with the navigation entry _
no longer working on chromium 2014/05/05 08:43:03 Then shouldn't these code be moved after IsDefault
tommi (sloooow) - chröme 2014/05/05 08:50:31 No, it should be here. The permissions that are c
no longer working on chromium 2014/05/05 09:27:02 I think I got your point. But My question is about
tommi (sloooow) - chröme 2014/05/05 10:00:13 Ah, I see. Yes you're right. Thanks for catching
303 Accept(false);
304 return true;
305 }
306
190 // Filter any parts of the request that have been blocked by default and deny 307 // Filter any parts of the request that have been blocked by default and deny
191 // it if nothing is left to accept. 308 // it if nothing is left to accept.
192 if (FilterBlockedByDefaultDevices() == 0) { 309 if (FilterBlockedByDefaultDevices() == 0) {
193 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED); 310 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED);
194 return true; 311 return true;
195 } 312 }
196 313
197 // Check if the media default setting is set to block. 314 // Check if the media default setting is set to block.
198 if (IsDefaultMediaAccessBlocked()) { 315 if (IsDefaultMediaAccessBlocked()) {
199 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED); 316 Deny(false, content::MEDIA_DEVICE_PERMISSION_DENIED);
200 return true; 317 return true;
201 } 318 }
202 319
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. 320 // Show the infobar.
221 return false; 321 return false;
222 } 322 }
223 323
224 bool MediaStreamDevicesController::HasAudio() const { 324 bool MediaStreamDevicesController::HasAudio() const {
225 return IsDeviceAudioCaptureRequestedAndAllowed(); 325 return IsDeviceAudioCaptureRequestedAndAllowed();
226 } 326 }
227 327
228 bool MediaStreamDevicesController::HasVideo() const { 328 bool MediaStreamDevicesController::HasVideo() const {
229 return IsDeviceVideoCaptureRequestedAndAllowed(); 329 return IsDeviceVideoCaptureRequestedAndAllowed();
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
298 398
299 // If either or both audio and video devices were requested but not 399 // If either or both audio and video devices were requested but not
300 // specified by id, get the default devices. 400 // specified by id, get the default devices.
301 if (get_default_audio_device || get_default_video_device) { 401 if (get_default_audio_device || get_default_video_device) {
302 MediaCaptureDevicesDispatcher::GetInstance()-> 402 MediaCaptureDevicesDispatcher::GetInstance()->
303 GetDefaultDevicesForProfile(profile_, 403 GetDefaultDevicesForProfile(profile_,
304 get_default_audio_device, 404 get_default_audio_device,
305 get_default_video_device, 405 get_default_video_device,
306 &devices); 406 &devices);
307 } 407 }
408
409 // Tag this navigation entry with the granted permissions.
410 // This avoids repeated prompts for requests accessed via http.
411 content::NavigationEntry* navigation_entry =
412 web_contents_->GetController().GetVisibleEntry();
413 if (navigation_entry) {
414 SetMediaPermissionsForNavigationEntry(
415 navigation_entry, request_, audio_allowed, video_allowed);
416 }
308 break; 417 break;
309 } 418 }
310 case content::MEDIA_DEVICE_ACCESS: { 419 case content::MEDIA_DEVICE_ACCESS: {
311 // Get the default devices for the request. 420 // Get the default devices for the request.
312 MediaCaptureDevicesDispatcher::GetInstance()-> 421 MediaCaptureDevicesDispatcher::GetInstance()->
313 GetDefaultDevicesForProfile(profile_, 422 GetDefaultDevicesForProfile(profile_,
314 audio_allowed, 423 audio_allowed,
315 video_allowed, 424 video_allowed,
316 &devices); 425 &devices);
317 break; 426 break;
(...skipping 29 matching lines...) Expand all
347 content::MEDIA_DEVICE_NO_HARDWARE : content::MEDIA_DEVICE_OK, 456 content::MEDIA_DEVICE_NO_HARDWARE : content::MEDIA_DEVICE_OK,
348 ui.Pass()); 457 ui.Pass());
349 } 458 }
350 459
351 void MediaStreamDevicesController::Deny( 460 void MediaStreamDevicesController::Deny(
352 bool update_content_setting, 461 bool update_content_setting,
353 content::MediaStreamRequestResult result) { 462 content::MediaStreamRequestResult result) {
354 DLOG(WARNING) << "MediaStreamDevicesController::Deny: " << result; 463 DLOG(WARNING) << "MediaStreamDevicesController::Deny: " << result;
355 NotifyUIRequestDenied(); 464 NotifyUIRequestDenied();
356 465
466 // Clear previously allowed permissions from the navigation entry if any.
467 content::NavigationEntry* navigation_entry =
no longer working on chromium 2014/05/05 08:14:36 Deny(false) can be triggered by clicking "X" butto
tommi (sloooow) - chröme 2014/05/05 08:28:53 Yes, intentional.
no longer working on chromium 2014/05/05 08:43:03 Good.
468 web_contents_->GetController().GetVisibleEntry();
469 if (navigation_entry) {
470 SetMediaPermissionsForNavigationEntry(
471 navigation_entry, request_, false, false);
472 }
473
357 if (update_content_setting) { 474 if (update_content_setting) {
358 CHECK_EQ(content::MEDIA_DEVICE_PERMISSION_DENIED, result); 475 CHECK_EQ(content::MEDIA_DEVICE_PERMISSION_DENIED, result);
359 SetPermission(false); 476 SetPermission(false);
360 } 477 }
361 478
362 content::MediaResponseCallback cb = callback_; 479 content::MediaResponseCallback cb = callback_;
363 callback_.Reset(); 480 callback_.Reset();
364 cb.Run(content::MediaStreamDevices(), 481 cb.Run(content::MediaStreamDevices(),
365 result, 482 result,
366 scoped_ptr<content::MediaStreamUI>()); 483 scoped_ptr<content::MediaStreamUI>());
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
584 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin); 701 ContentSettingsPattern::FromURLNoWildcard(request_.security_origin);
585 // Check the pattern is valid or not. When the request is from a file access, 702 // Check the pattern is valid or not. When the request is from a file access,
586 // no exception will be made. 703 // no exception will be made.
587 if (!primary_pattern.IsValid()) 704 if (!primary_pattern.IsValid())
588 return; 705 return;
589 706
590 ContentSetting content_setting = allowed ? 707 ContentSetting content_setting = allowed ?
591 CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; 708 CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
592 if (request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE) != 709 if (request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE) !=
593 request_permissions_.end()) { 710 request_permissions_.end()) {
594 profile_->GetHostContentSettingsMap()->SetContentSetting( 711 profile_->GetHostContentSettingsMap()->SetContentSetting(
595 primary_pattern, 712 primary_pattern,
596 ContentSettingsPattern::Wildcard(), 713 ContentSettingsPattern::Wildcard(),
597 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, 714 CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
598 std::string(), 715 std::string(),
599 content_setting); 716 content_setting);
600 } 717 }
601 if (request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE) != 718 if (request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE) !=
602 request_permissions_.end()) { 719 request_permissions_.end()) {
603 profile_->GetHostContentSettingsMap()->SetContentSetting( 720 profile_->GetHostContentSettingsMap()->SetContentSetting(
604 primary_pattern, 721 primary_pattern,
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
642 it->second.permission == MEDIA_ALLOWED); 759 it->second.permission == MEDIA_ALLOWED);
643 } 760 }
644 761
645 bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed() 762 bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed()
646 const { 763 const {
647 MediaStreamTypeSettingsMap::const_iterator it = 764 MediaStreamTypeSettingsMap::const_iterator it =
648 request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE); 765 request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE);
649 return (it != request_permissions_.end() && 766 return (it != request_permissions_.end() &&
650 it->second.permission == MEDIA_ALLOWED); 767 it->second.permission == MEDIA_ALLOWED);
651 } 768 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698