- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / media / media_stream_devices_controller.cc
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.
4
5 #include "chrome/browser/media/media_stream_devices_controller.h"
6
7 #include "base/command_line.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/prefs/scoped_user_pref_update.h"
10 #include "base/values.h"
11 #include "chrome/browser/content_settings/content_settings_provider.h"
12 #include "chrome/browser/content_settings/host_content_settings_map.h"
13 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
14 #include "chrome/browser/media/media_capture_devices_dispatcher.h"
15 #include "chrome/browser/media/media_stream_capture_indicator.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/content_settings.h"
20 #include "chrome/common/content_settings_pattern.h"
21 #include "chrome/common/pref_names.h"
22 #include "components/user_prefs/pref_registry_syncable.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/common/media_stream_request.h"
25 #include "extensions/common/constants.h"
26
27 #if defined(OS_CHROMEOS)
28 #include "chrome/browser/chromeos/login/user_manager.h"
29 #endif
30
31 using content::BrowserThread;
32
33 namespace {
34
35 bool HasAnyAvailableDevice() {
36   const content::MediaStreamDevices& audio_devices =
37       MediaCaptureDevicesDispatcher::GetInstance()->GetAudioCaptureDevices();
38   const content::MediaStreamDevices& video_devices =
39       MediaCaptureDevicesDispatcher::GetInstance()->GetVideoCaptureDevices();
40
41   return !audio_devices.empty() || !video_devices.empty();
42 }
43
44 bool IsInKioskMode() {
45   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode))
46     return true;
47
48 #if defined(OS_CHROMEOS)
49   const chromeos::UserManager* user_manager = chromeos::UserManager::Get();
50   return user_manager && user_manager->IsLoggedInAsKioskApp();
51 #else
52   return false;
53 #endif
54 }
55
56 }  // namespace
57
58 MediaStreamDevicesController::MediaStreamTypeSettings::MediaStreamTypeSettings(
59     Permission permission, const std::string& requested_device_id):
60     permission(permission), requested_device_id(requested_device_id) {}
61
62 MediaStreamDevicesController::MediaStreamTypeSettings::
63     MediaStreamTypeSettings(): permission(MEDIA_NONE) {}
64
65 MediaStreamDevicesController::MediaStreamTypeSettings::
66     ~MediaStreamTypeSettings() {}
67
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);
77
78   // For MEDIA_OPEN_DEVICE requests (Pepper) we always request both webcam
79   // and microphone to avoid popping two infobars.
80   // We start with setting the requested media type to allowed or blocked
81   // depending on the policy. If not blocked by policy it may be blocked later
82   // in the two remaining filtering steps (by user setting or by user when
83   // clicking the infobar).
84   // TODO(grunell): It's not the nicest solution to let the MEDIA_OPEN_DEVICE
85   // case take a ride on the MEDIA_DEVICE_*_CAPTURE permission. Should be fixed.
86   if (request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
87       request.request_type == content::MEDIA_OPEN_DEVICE) {
88     if (GetDevicePolicy(prefs::kAudioCaptureAllowed,
89                         prefs::kAudioCaptureAllowedUrls) == ALWAYS_DENY) {
90       request_permissions_.insert(std::make_pair(
91           content::MEDIA_DEVICE_AUDIO_CAPTURE,
92           MediaStreamTypeSettings(MEDIA_BLOCKED_BY_POLICY,
93                                   request.requested_audio_device_id)));
94     } else {
95       request_permissions_.insert(std::make_pair(
96           content::MEDIA_DEVICE_AUDIO_CAPTURE,
97           MediaStreamTypeSettings(MEDIA_ALLOWED,
98                                   request.requested_audio_device_id)));
99     }
100   }
101   if (request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE ||
102       request.request_type == content::MEDIA_OPEN_DEVICE) {
103     if (GetDevicePolicy(prefs::kVideoCaptureAllowed,
104                         prefs::kVideoCaptureAllowedUrls) == ALWAYS_DENY) {
105       request_permissions_.insert(std::make_pair(
106           content::MEDIA_DEVICE_VIDEO_CAPTURE,
107           MediaStreamTypeSettings(MEDIA_BLOCKED_BY_POLICY,
108                                   request.requested_video_device_id)));
109     } else {
110       request_permissions_.insert(std::make_pair(
111           content::MEDIA_DEVICE_VIDEO_CAPTURE,
112           MediaStreamTypeSettings(MEDIA_ALLOWED,
113                                   request.requested_video_device_id)));
114     }
115   }
116 }
117
118 MediaStreamDevicesController::~MediaStreamDevicesController() {
119   if (!callback_.is_null()) {
120     callback_.Run(content::MediaStreamDevices(),
121                   scoped_ptr<content::MediaStreamUI>());
122   }
123 }
124
125 // static
126 void MediaStreamDevicesController::RegisterProfilePrefs(
127     user_prefs::PrefRegistrySyncable* prefs) {
128   prefs->RegisterBooleanPref(prefs::kVideoCaptureAllowed,
129                              true,
130                              user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
131   prefs->RegisterBooleanPref(prefs::kAudioCaptureAllowed,
132                              true,
133                              user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
134   prefs->RegisterListPref(prefs::kVideoCaptureAllowedUrls,
135                           user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
136   prefs->RegisterListPref(prefs::kAudioCaptureAllowedUrls,
137                           user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
138 }
139
140
141 bool MediaStreamDevicesController::DismissInfoBarAndTakeActionOnSettings() {
142   // Tab capture is allowed for extensions only and infobar is not shown for
143   // extensions.
144   if (request_.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE ||
145       request_.video_type == content::MEDIA_TAB_VIDEO_CAPTURE) {
146     Deny(false);
147     return true;
148   }
149
150   // Deny the request if the security origin is empty, this happens with
151   // file access without |--allow-file-access-from-files| flag.
152   if (request_.security_origin.is_empty()) {
153     Deny(false);
154     return true;
155   }
156
157   // Deny the request if there is no device attached to the OS.
158   if (!HasAnyAvailableDevice()) {
159     Deny(false);
160     return true;
161   }
162
163   // Check if any allow exception has been made for this request.
164   if (IsRequestAllowedByDefault()) {
165     Accept(false);
166     return true;
167   }
168
169   // Filter any parts of the request that have been blocked by default and deny
170   // it if nothing is left to accept.
171   if (FilterBlockedByDefaultDevices() == 0) {
172     Deny(false);
173     return true;
174   }
175
176   // Check if the media default setting is set to block.
177   if (IsDefaultMediaAccessBlocked()) {
178     Deny(false);
179     return true;
180   }
181
182   if (request_.request_type == content::MEDIA_OPEN_DEVICE) {
183     bool no_matched_audio_device =
184         (request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
185          !request_.requested_audio_device_id.empty() &&
186          MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedAudioDevice(
187              request_.requested_audio_device_id) == NULL);
188     bool no_matched_video_device =
189         (request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
190          !request_.requested_video_device_id.empty() &&
191          MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedVideoDevice(
192              request_.requested_video_device_id) == NULL);
193     if (no_matched_audio_device || no_matched_video_device) {
194       Deny(false);
195       return true;
196     }
197   }
198
199   // Show the infobar.
200   return false;
201 }
202
203 bool MediaStreamDevicesController::HasAudio() const {
204   return IsDeviceAudioCaptureRequestedAndAllowed();
205 }
206
207 bool MediaStreamDevicesController::HasVideo() const {
208   return IsDeviceVideoCaptureRequestedAndAllowed();
209 }
210
211 const std::string& MediaStreamDevicesController::GetSecurityOriginSpec() const {
212   return request_.security_origin.spec();
213 }
214
215 void MediaStreamDevicesController::Accept(bool update_content_setting) {
216   NotifyUIRequestAccepted();
217
218   // Get the default devices for the request.
219   content::MediaStreamDevices devices;
220   bool audio_allowed = IsDeviceAudioCaptureRequestedAndAllowed();
221   bool video_allowed = IsDeviceVideoCaptureRequestedAndAllowed();
222   if (audio_allowed || video_allowed) {
223     switch (request_.request_type) {
224       case content::MEDIA_OPEN_DEVICE: {
225         const content::MediaStreamDevice* device = NULL;
226         // For open device request, when requested device_id is empty, pick
227         // the first available of the given type. If requested device_id is
228         // not empty, return the desired device if it's available. Otherwise,
229         // return no device.
230         if (audio_allowed &&
231             request_.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE) {
232           if (!request_.requested_audio_device_id.empty()) {
233             device = MediaCaptureDevicesDispatcher::GetInstance()->
234                 GetRequestedAudioDevice(request_.requested_audio_device_id);
235           } else {
236             device = MediaCaptureDevicesDispatcher::GetInstance()->
237                 GetFirstAvailableAudioDevice();
238           }
239         } else if (video_allowed &&
240             request_.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE) {
241           // Pepper API opens only one device at a time.
242           if (!request_.requested_video_device_id.empty()) {
243             device = MediaCaptureDevicesDispatcher::GetInstance()->
244                 GetRequestedVideoDevice(request_.requested_video_device_id);
245           } else {
246             device = MediaCaptureDevicesDispatcher::GetInstance()->
247                 GetFirstAvailableVideoDevice();
248           }
249         }
250         if (device)
251           devices.push_back(*device);
252         break;
253       }
254       case content::MEDIA_GENERATE_STREAM: {
255         bool get_default_audio_device = audio_allowed;
256         bool get_default_video_device = video_allowed;
257
258         // Get the exact audio or video device if an id is specified.
259         if (audio_allowed && !request_.requested_audio_device_id.empty()) {
260           const content::MediaStreamDevice* audio_device =
261               MediaCaptureDevicesDispatcher::GetInstance()->
262                   GetRequestedAudioDevice(request_.requested_audio_device_id);
263           if (audio_device) {
264             devices.push_back(*audio_device);
265             get_default_audio_device = false;
266           }
267         }
268         if (video_allowed && !request_.requested_video_device_id.empty()) {
269           const content::MediaStreamDevice* video_device =
270               MediaCaptureDevicesDispatcher::GetInstance()->
271                   GetRequestedVideoDevice(request_.requested_video_device_id);
272           if (video_device) {
273             devices.push_back(*video_device);
274             get_default_video_device = false;
275           }
276         }
277
278         // If either or both audio and video devices were requested but not
279         // specified by id, get the default devices.
280         if (get_default_audio_device || get_default_video_device) {
281           MediaCaptureDevicesDispatcher::GetInstance()->
282               GetDefaultDevicesForProfile(profile_,
283                                           get_default_audio_device,
284                                           get_default_video_device,
285                                           &devices);
286         }
287         break;
288       }
289       case content::MEDIA_DEVICE_ACCESS: {
290         // Get the default devices for the request.
291         MediaCaptureDevicesDispatcher::GetInstance()->
292             GetDefaultDevicesForProfile(profile_,
293                                         audio_allowed,
294                                         video_allowed,
295                                         &devices);
296         break;
297       }
298       case content::MEDIA_ENUMERATE_DEVICES: {
299         // Do nothing.
300         NOTREACHED();
301         break;
302       }
303     }  // switch
304
305     // TODO(raymes): We currently set the content permission for non-https
306     // websites for Pepper requests as well. This is temporary and should be
307     // removed.
308     if (update_content_setting) {
309       if ((IsSchemeSecure() && !devices.empty()) ||
310           request_.request_type == content::MEDIA_OPEN_DEVICE) {
311         SetPermission(true);
312       }
313     }
314   }
315
316   scoped_ptr<content::MediaStreamUI> ui;
317   if (!devices.empty()) {
318     ui = MediaCaptureDevicesDispatcher::GetInstance()->
319         GetMediaStreamCaptureIndicator()->RegisterMediaStream(
320             web_contents_, devices);
321   }
322   content::MediaResponseCallback cb = callback_;
323   callback_.Reset();
324   cb.Run(devices, ui.Pass());
325 }
326
327 void MediaStreamDevicesController::Deny(bool update_content_setting) {
328   NotifyUIRequestDenied();
329
330   if (update_content_setting)
331     SetPermission(false);
332
333   content::MediaResponseCallback cb = callback_;
334   callback_.Reset();
335   cb.Run(content::MediaStreamDevices(), scoped_ptr<content::MediaStreamUI>());
336 }
337
338 MediaStreamDevicesController::DevicePolicy
339 MediaStreamDevicesController::GetDevicePolicy(
340     const char* policy_name,
341     const char* whitelist_policy_name) const {
342   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
343
344   // If the security origin policy matches a value in the whitelist, allow it.
345   // Otherwise, check the |policy_name| master switch for the default behavior.
346
347   PrefService* prefs = profile_->GetPrefs();
348
349   // TODO(tommi): Remove the kiosk mode check when the whitelist below
350   // is visible in the media exceptions UI.
351   // See discussion here: https://codereview.chromium.org/15738004/
352   if (IsInKioskMode()) {
353     const base::ListValue* list = prefs->GetList(whitelist_policy_name);
354     std::string value;
355     for (size_t i = 0; i < list->GetSize(); ++i) {
356       if (list->GetString(i, &value)) {
357         ContentSettingsPattern pattern =
358             ContentSettingsPattern::FromString(value);
359         if (pattern == ContentSettingsPattern::Wildcard()) {
360           DLOG(WARNING) << "Ignoring wildcard URL pattern: " << value;
361           continue;
362         }
363         DLOG_IF(ERROR, !pattern.IsValid()) << "Invalid URL pattern: " << value;
364         if (pattern.IsValid() && pattern.Matches(request_.security_origin))
365           return ALWAYS_ALLOW;
366       }
367     }
368   }
369
370   // If a match was not found, check if audio capture is otherwise disallowed
371   // or if the user should be prompted.  Setting the policy value to "true"
372   // is equal to not setting it at all, so from hereon out, we will return
373   // either POLICY_NOT_SET (prompt) or ALWAYS_DENY (no prompt, no access).
374   if (!prefs->GetBoolean(policy_name))
375     return ALWAYS_DENY;
376
377   return POLICY_NOT_SET;
378 }
379
380 bool MediaStreamDevicesController::IsRequestAllowedByDefault() const {
381   // The request from internal objects like chrome://URLs is always allowed.
382   if (ShouldAlwaysAllowOrigin())
383     return true;
384
385   struct {
386     bool has_capability;
387     const char* policy_name;
388     const char* list_policy_name;
389     ContentSettingsType settings_type;
390   } device_checks[] = {
391     { IsDeviceAudioCaptureRequestedAndAllowed(), prefs::kAudioCaptureAllowed,
392       prefs::kAudioCaptureAllowedUrls, CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC },
393     { IsDeviceVideoCaptureRequestedAndAllowed(), prefs::kVideoCaptureAllowed,
394       prefs::kVideoCaptureAllowedUrls,
395       CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA },
396   };
397
398   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(device_checks); ++i) {
399     if (!device_checks[i].has_capability)
400       continue;
401
402     DevicePolicy policy = GetDevicePolicy(device_checks[i].policy_name,
403                                           device_checks[i].list_policy_name);
404
405     if (policy == ALWAYS_DENY)
406       return false;
407
408     if (policy == POLICY_NOT_SET) {
409       // Only load content settings from secure origins unless it is a
410       // content::MEDIA_OPEN_DEVICE (Pepper) request.
411       if (!IsSchemeSecure() &&
412           request_.request_type != content::MEDIA_OPEN_DEVICE) {
413         return false;
414       }
415       if (profile_->GetHostContentSettingsMap()->GetContentSetting(
416               request_.security_origin, request_.security_origin,
417               device_checks[i].settings_type, NO_RESOURCE_IDENTIFIER) !=
418               CONTENT_SETTING_ALLOW) {
419         return false;
420       }
421     }
422     // If we get here, then either policy is set to ALWAYS_ALLOW or the content
423     // settings allow the request by default.
424   }
425
426   return true;
427 }
428
429 int MediaStreamDevicesController::FilterBlockedByDefaultDevices() {
430   int requested_devices = 0;
431
432   if (IsDeviceAudioCaptureRequestedAndAllowed()) {
433     if (profile_->GetHostContentSettingsMap()->GetContentSetting(
434         request_.security_origin,
435         request_.security_origin,
436         CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
437         NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_BLOCK) {
438       request_permissions_[content::MEDIA_DEVICE_AUDIO_CAPTURE].permission =
439           MEDIA_BLOCKED_BY_USER_SETTING;
440     } else {
441       ++requested_devices;
442     }
443   }
444
445   if (IsDeviceVideoCaptureRequestedAndAllowed()) {
446     if (profile_->GetHostContentSettingsMap()->GetContentSetting(
447         request_.security_origin,
448         request_.security_origin,
449         CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
450         NO_RESOURCE_IDENTIFIER) == CONTENT_SETTING_BLOCK) {
451       request_permissions_[content::MEDIA_DEVICE_VIDEO_CAPTURE].permission =
452           MEDIA_BLOCKED_BY_USER_SETTING;
453     } else {
454       ++requested_devices;
455     }
456   }
457
458   return requested_devices;
459 }
460
461 bool MediaStreamDevicesController::IsDefaultMediaAccessBlocked() const {
462   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
463   // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the
464   // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and
465   // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA.
466   ContentSetting current_setting =
467       profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
468           CONTENT_SETTINGS_TYPE_MEDIASTREAM, NULL);
469   return (current_setting == CONTENT_SETTING_BLOCK);
470 }
471
472 bool MediaStreamDevicesController::IsSchemeSecure() const {
473   return request_.security_origin.SchemeIsSecure() ||
474       request_.security_origin.SchemeIs(extensions::kExtensionScheme) ||
475       CommandLine::ForCurrentProcess()->HasSwitch(
476           switches::kDisableUserMediaSecurity);
477 }
478
479 bool MediaStreamDevicesController::ShouldAlwaysAllowOrigin() const {
480   // TODO(markusheintz): Replace CONTENT_SETTINGS_TYPE_MEDIA_STREAM with the
481   // appropriate new CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC and
482   // CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA.
483   return profile_->GetHostContentSettingsMap()->ShouldAllowAllContent(
484       request_.security_origin, request_.security_origin,
485       CONTENT_SETTINGS_TYPE_MEDIASTREAM);
486 }
487
488 void MediaStreamDevicesController::SetPermission(bool allowed) const {
489   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
490 #if defined(OS_ANDROID)
491   // We do not support sticky operations on Android yet.
492   return;
493 #endif
494   ContentSettingsPattern primary_pattern =
495       ContentSettingsPattern::FromURLNoWildcard(request_.security_origin);
496   // Check the pattern is valid or not. When the request is from a file access,
497   // no exception will be made.
498   if (!primary_pattern.IsValid())
499     return;
500
501   ContentSetting content_setting = allowed ?
502       CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK;
503   if (request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE) !=
504       request_permissions_.end()) {
505       profile_->GetHostContentSettingsMap()->SetContentSetting(
506         primary_pattern,
507         ContentSettingsPattern::Wildcard(),
508         CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC,
509         std::string(),
510         content_setting);
511   }
512   if (request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE) !=
513       request_permissions_.end()) {
514     profile_->GetHostContentSettingsMap()->SetContentSetting(
515         primary_pattern,
516         ContentSettingsPattern::Wildcard(),
517         CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA,
518         std::string(),
519         content_setting);
520   }
521 }
522
523 void MediaStreamDevicesController::NotifyUIRequestAccepted() const {
524   if (!content_settings_)
525     return;
526
527   content_settings_->OnMediaStreamPermissionSet(request_.security_origin,
528                                                 request_permissions_);
529 }
530
531 void MediaStreamDevicesController::NotifyUIRequestDenied() {
532   if (!content_settings_)
533     return;
534
535   if (IsDeviceAudioCaptureRequestedAndAllowed()) {
536     request_permissions_[content::MEDIA_DEVICE_AUDIO_CAPTURE].permission =
537         MEDIA_BLOCKED_BY_USER;
538   }
539   if (IsDeviceVideoCaptureRequestedAndAllowed()) {
540     request_permissions_[content::MEDIA_DEVICE_VIDEO_CAPTURE].permission =
541         MEDIA_BLOCKED_BY_USER;
542   }
543
544   content_settings_->OnMediaStreamPermissionSet(request_.security_origin,
545                                                 request_permissions_);
546 }
547
548 bool MediaStreamDevicesController::IsDeviceAudioCaptureRequestedAndAllowed()
549     const {
550   MediaStreamTypeSettingsMap::const_iterator it =
551       request_permissions_.find(content::MEDIA_DEVICE_AUDIO_CAPTURE);
552   return (it != request_permissions_.end() &&
553           it->second.permission == MEDIA_ALLOWED);
554 }
555
556 bool MediaStreamDevicesController::IsDeviceVideoCaptureRequestedAndAllowed()
557     const {
558   MediaStreamTypeSettingsMap::const_iterator it =
559       request_permissions_.find(content::MEDIA_DEVICE_VIDEO_CAPTURE);
560   return (it != request_permissions_.end() &&
561           it->second.permission == MEDIA_ALLOWED);
562 }