1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/media/extension_media_access_handler.h"
#include "chrome/browser/media/media_capture_devices_dispatcher.h"
#include "chrome/browser/media/media_stream_capture_indicator.h"
#include "chrome/browser/media/media_stream_device_permissions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/extension.h"
#include "extensions/common/permissions/permissions_data.h"
namespace {
// This is a short-term solution to grant camera and/or microphone access to
// extensions:
// 1. Virtual keyboard extension.
// 2. Flutter gesture recognition extension.
// 3. TODO(smus): Airbender experiment 1.
// 4. TODO(smus): Airbender experiment 2.
// 5. Hotwording component extension.
// 6. XKB input method component extension.
// 7. M17n/T13n/CJK input method component extension.
// Once http://crbug.com/292856 is fixed, remove this whitelist.
bool IsMediaRequestWhitelistedForExtension(
const extensions::Extension* extension) {
return extension->id() == "mppnpdlheglhdfmldimlhpnegondlapf" ||
extension->id() == "jokbpnebhdcladagohdnfgjcpejggllo" ||
extension->id() == "clffjmdilanldobdnedchkdbofoimcgb" ||
extension->id() == "nnckehldicaciogcbchegobnafnjkcne" ||
extension->id() == "nbpagnldghgfoolbancepceaanlmhfmd" ||
extension->id() == "jkghodnilhceideoidjikpgommlajknk" ||
extension->id() == "gjaehgfemfahhmlgpdfknkhdnemmolop";
}
} // namespace
ExtensionMediaAccessHandler::ExtensionMediaAccessHandler() {
}
ExtensionMediaAccessHandler::~ExtensionMediaAccessHandler() {
}
bool ExtensionMediaAccessHandler::SupportsStreamType(
const content::MediaStreamType type,
const extensions::Extension* extension) {
return extension && (extension->is_platform_app() ||
IsMediaRequestWhitelistedForExtension(extension)) &&
(type == content::MEDIA_DEVICE_AUDIO_CAPTURE ||
type == content::MEDIA_DEVICE_VIDEO_CAPTURE);
}
bool ExtensionMediaAccessHandler::CheckMediaAccessPermission(
content::WebContents* web_contents,
const GURL& security_origin,
content::MediaStreamType type,
const extensions::Extension* extension) {
return extension->permissions_data()->HasAPIPermission(
type == content::MEDIA_DEVICE_AUDIO_CAPTURE
? extensions::APIPermission::kAudioCapture
: extensions::APIPermission::kVideoCapture);
}
void ExtensionMediaAccessHandler::HandleRequest(
content::WebContents* web_contents,
const content::MediaStreamRequest& request,
const content::MediaResponseCallback& callback,
const extensions::Extension* extension) {
// TODO(vrk): This code is largely duplicated in
// MediaStreamDevicesController::Accept(). Move this code into a shared method
// between the two classes.
Profile* profile =
Profile::FromBrowserContext(web_contents->GetBrowserContext());
bool audio_allowed =
request.audio_type == content::MEDIA_DEVICE_AUDIO_CAPTURE &&
extension->permissions_data()->HasAPIPermission(
extensions::APIPermission::kAudioCapture) &&
GetDevicePolicy(profile, extension->url(), prefs::kAudioCaptureAllowed,
prefs::kAudioCaptureAllowedUrls) != ALWAYS_DENY;
bool video_allowed =
request.video_type == content::MEDIA_DEVICE_VIDEO_CAPTURE &&
extension->permissions_data()->HasAPIPermission(
extensions::APIPermission::kVideoCapture) &&
GetDevicePolicy(profile, extension->url(), prefs::kVideoCaptureAllowed,
prefs::kVideoCaptureAllowedUrls) != ALWAYS_DENY;
bool get_default_audio_device = audio_allowed;
bool get_default_video_device = video_allowed;
content::MediaStreamDevices devices;
// Set an initial error result. If neither audio or video is allowed, we'll
// never try to get any device below but will just create |ui| and return an
// empty list with "invalid state" result. If at least one is allowed, we'll
// try to get device(s), and if failure, we want to return "no hardware"
// result.
// TODO(grunell): The invalid state result should be changed to a new denied
// result + a dcheck to ensure at least one of audio or video types is
// capture.
content::MediaStreamRequestResult result =
(audio_allowed || video_allowed) ? content::MEDIA_DEVICE_NO_HARDWARE
: content::MEDIA_DEVICE_INVALID_STATE;
// Get the exact audio or video device if an id is specified.
// We only set any error result here and before running the callback change
// it to OK if we have any device.
if (audio_allowed && !request.requested_audio_device_id.empty()) {
const content::MediaStreamDevice* audio_device =
MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedAudioDevice(
request.requested_audio_device_id);
if (audio_device) {
devices.push_back(*audio_device);
get_default_audio_device = false;
}
}
if (video_allowed && !request.requested_video_device_id.empty()) {
const content::MediaStreamDevice* video_device =
MediaCaptureDevicesDispatcher::GetInstance()->GetRequestedVideoDevice(
request.requested_video_device_id);
if (video_device) {
devices.push_back(*video_device);
get_default_video_device = false;
}
}
// If either or both audio and video devices were requested but not
// specified by id, get the default devices.
if (get_default_audio_device || get_default_video_device) {
MediaCaptureDevicesDispatcher::GetInstance()->GetDefaultDevicesForProfile(
profile, get_default_audio_device, get_default_video_device, &devices);
}
scoped_ptr<content::MediaStreamUI> ui;
if (!devices.empty()) {
result = content::MEDIA_DEVICE_OK;
ui = MediaCaptureDevicesDispatcher::GetInstance()
->GetMediaStreamCaptureIndicator()
->RegisterMediaStream(web_contents, devices);
}
callback.Run(devices, result, ui.Pass());
}
|