[MediaStream] 'devicechange' event should not fire in frames that can't access captur...
[WebKit-https.git] / Source / WebKit / WebProcess / MediaStream / UserMediaPermissionRequestManager.cpp
1 /*
2  * Copyright (C) 2014 Igalia S.L.
3  * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "config.h"
21 #include "UserMediaPermissionRequestManager.h"
22
23 #if ENABLE(MEDIA_STREAM)
24
25 #include "Logging.h"
26 #include "WebCoreArgumentCoders.h"
27 #include "WebFrame.h"
28 #include "WebPage.h"
29 #include "WebPageProxyMessages.h"
30 #include <WebCore/CaptureDevice.h>
31 #include <WebCore/Document.h>
32 #include <WebCore/Frame.h>
33 #include <WebCore/FrameLoader.h>
34 #include <WebCore/MediaConstraints.h>
35 #include <WebCore/SecurityOrigin.h>
36 #include <WebCore/SecurityOriginData.h>
37
38 namespace WebKit {
39 using namespace WebCore;
40
41 static constexpr OptionSet<WebCore::ActivityState::Flag> focusedActiveWindow = { WebCore::ActivityState::IsFocused, WebCore::ActivityState::WindowIsActive };
42
43 static uint64_t generateRequestID()
44 {
45     static uint64_t uniqueRequestID = 1;
46     return uniqueRequestID++;
47 }
48
49 UserMediaPermissionRequestManager::UserMediaPermissionRequestManager(WebPage& page)
50     : m_page(page)
51 {
52 }
53
54 UserMediaPermissionRequestManager::~UserMediaPermissionRequestManager()
55 {
56     for (auto& sandboxExtension : m_userMediaDeviceSandboxExtensions)
57         sandboxExtension.value->revoke();
58 }
59
60 void UserMediaPermissionRequestManager::startUserMediaRequest(UserMediaRequest& request)
61 {
62     Document* document = request.document();
63     Frame* frame = document ? document->frame() : nullptr;
64
65     if (!frame || !document->page()) {
66         request.deny(UserMediaRequest::OtherFailure, emptyString());
67         return;
68     }
69
70     if (document->page()->canStartMedia()) {
71         sendUserMediaRequest(request);
72         return;
73     }
74
75     auto& pendingRequests = m_blockedUserMediaRequests.add(document, Vector<RefPtr<UserMediaRequest>>()).iterator->value;
76     if (pendingRequests.isEmpty())
77         document->addMediaCanStartListener(this);
78     pendingRequests.append(&request);
79 }
80
81 void UserMediaPermissionRequestManager::sendUserMediaRequest(UserMediaRequest& userRequest)
82 {
83     auto* frame = userRequest.document() ? userRequest.document()->frame() : nullptr;
84     if (!frame) {
85         userRequest.deny(UserMediaRequest::OtherFailure, emptyString());
86         return;
87     }
88
89     uint64_t requestID = generateRequestID();
90     m_idToUserMediaRequestMap.add(requestID, &userRequest);
91     m_userMediaRequestToIDMap.add(&userRequest, requestID);
92
93     WebFrame* webFrame = WebFrame::fromCoreFrame(*frame);
94     ASSERT(webFrame);
95
96     auto* topLevelDocumentOrigin = userRequest.topLevelDocumentOrigin();
97     m_page.send(Messages::WebPageProxy::RequestUserMediaPermissionForFrame(requestID, webFrame->frameID(), userRequest.userMediaDocumentOrigin()->data(), topLevelDocumentOrigin->data(), userRequest.request()));
98 }
99
100 void UserMediaPermissionRequestManager::cancelUserMediaRequest(UserMediaRequest& request)
101 {
102     uint64_t requestID = m_userMediaRequestToIDMap.take(&request);
103     if (!requestID)
104         return;
105
106     request.deny(UserMediaRequest::OtherFailure, emptyString());
107     m_idToUserMediaRequestMap.remove(requestID);
108     removeMediaRequestFromMaps(request);
109 }
110
111 void UserMediaPermissionRequestManager::mediaCanStart(Document& document)
112 {
113     auto pendingRequests = m_blockedUserMediaRequests.take(&document);
114     while (!pendingRequests.isEmpty()) {
115         if (!document.page()->canStartMedia()) {
116             m_blockedUserMediaRequests.add(&document, pendingRequests);
117             document.addMediaCanStartListener(this);
118             break;
119         }
120
121         sendUserMediaRequest(*pendingRequests.takeLast());
122     }
123 }
124
125 void UserMediaPermissionRequestManager::removeMediaRequestFromMaps(UserMediaRequest& request)
126 {
127     Document* document = request.document();
128     if (!document)
129         return;
130
131     auto pendingRequests = m_blockedUserMediaRequests.take(document);
132     for (auto& pendingRequest : pendingRequests) {
133         if (&request != pendingRequest.get())
134             continue;
135
136         if (pendingRequests.isEmpty())
137             request.document()->removeMediaCanStartListener(this);
138         else
139             m_blockedUserMediaRequests.add(document, pendingRequests);
140         break;
141     }
142
143     m_userMediaRequestToIDMap.remove(&request);
144 }
145
146 void UserMediaPermissionRequestManager::userMediaAccessWasGranted(uint64_t requestID, CaptureDevice&& audioDevice, CaptureDevice&& videoDevice, String&& deviceIdentifierHashSalt)
147 {
148     auto request = m_idToUserMediaRequestMap.take(requestID);
149     if (!request)
150         return;
151     removeMediaRequestFromMaps(*request);
152
153     request->allow(WTFMove(audioDevice), WTFMove(videoDevice), WTFMove(deviceIdentifierHashSalt));
154 }
155
156 void UserMediaPermissionRequestManager::userMediaAccessWasDenied(uint64_t requestID, WebCore::UserMediaRequest::MediaAccessDenialReason reason, String&& invalidConstraint)
157 {
158     auto request = m_idToUserMediaRequestMap.take(requestID);
159     if (!request)
160         return;
161     removeMediaRequestFromMaps(*request);
162
163     request->deny(reason, WTFMove(invalidConstraint));
164 }
165
166 void UserMediaPermissionRequestManager::enumerateMediaDevices(MediaDevicesEnumerationRequest& request)
167 {
168     auto* document = downcast<Document>(request.scriptExecutionContext());
169     auto* frame = document ? document->frame() : nullptr;
170
171     if (!frame) {
172         request.setDeviceInfo(Vector<CaptureDevice>(), emptyString(), false);
173         return;
174     }
175
176     uint64_t requestID = generateRequestID();
177     m_idToMediaDevicesEnumerationRequestMap.add(requestID, &request);
178     m_mediaDevicesEnumerationRequestToIDMap.add(&request, requestID);
179
180     WebFrame* webFrame = WebFrame::fromCoreFrame(*frame);
181     ASSERT(webFrame);
182
183     SecurityOrigin* topLevelDocumentOrigin = request.topLevelDocumentOrigin();
184     ASSERT(topLevelDocumentOrigin);
185     m_page.send(Messages::WebPageProxy::EnumerateMediaDevicesForFrame(requestID, webFrame->frameID(), request.userMediaDocumentOrigin()->data(), topLevelDocumentOrigin->data()));
186 }
187
188 void UserMediaPermissionRequestManager::cancelMediaDevicesEnumeration(WebCore::MediaDevicesEnumerationRequest& request)
189 {
190     uint64_t requestID = m_mediaDevicesEnumerationRequestToIDMap.take(&request);
191     if (!requestID)
192         return;
193     request.setDeviceInfo(Vector<CaptureDevice>(), emptyString(), false);
194     m_idToMediaDevicesEnumerationRequestMap.remove(requestID);
195 }
196
197 void UserMediaPermissionRequestManager::didCompleteMediaDeviceEnumeration(uint64_t requestID, const Vector<CaptureDevice>& deviceList, String&& mediaDeviceIdentifierHashSalt, bool hasPersistentAccess)
198 {
199     RefPtr<MediaDevicesEnumerationRequest> request = m_idToMediaDevicesEnumerationRequestMap.take(requestID);
200     if (!request)
201         return;
202     m_mediaDevicesEnumerationRequestToIDMap.remove(request);
203     
204     request->setDeviceInfo(deviceList, WTFMove(mediaDeviceIdentifierHashSalt), hasPersistentAccess);
205 }
206
207 void UserMediaPermissionRequestManager::grantUserMediaDeviceSandboxExtensions(MediaDeviceSandboxExtensions&& extensions)
208 {
209     for (size_t i = 0; i < extensions.size(); i++) {
210         const auto& extension = extensions[i];
211         extension.second->consume();
212         RELEASE_LOG(WebRTC, "UserMediaPermissionRequestManager::grantUserMediaDeviceSandboxExtensions - granted extension %s", extension.first.utf8().data());
213         m_userMediaDeviceSandboxExtensions.add(extension.first, extension.second.copyRef());
214     }
215 }
216
217 void UserMediaPermissionRequestManager::revokeUserMediaDeviceSandboxExtensions(const Vector<String>& extensionIDs)
218 {
219     for (const auto& extensionID : extensionIDs) {
220         auto extension = m_userMediaDeviceSandboxExtensions.take(extensionID);
221         if (extension) {
222             extension->revoke();
223             RELEASE_LOG(WebRTC, "UserMediaPermissionRequestManager::revokeUserMediaDeviceSandboxExtensions - revoked extension %s", extensionID.utf8().data());
224         }
225     }
226 }
227
228 UserMediaClient::DeviceChangeObserverToken UserMediaPermissionRequestManager::addDeviceChangeObserver(WTF::Function<void()>&& observer)
229 {
230     auto identifier = generateObjectIdentifier<WebCore::UserMediaClient::DeviceChangeObserverTokenType>();
231     m_deviceChangeObserverMap.add(identifier, WTFMove(observer));
232
233     if (!m_monitoringDeviceChange) {
234         m_monitoringDeviceChange = true;
235         m_page.send(Messages::WebPageProxy::BeginMonitoringCaptureDevices());
236     }
237     return identifier;
238 }
239
240 void UserMediaPermissionRequestManager::removeDeviceChangeObserver(UserMediaClient::DeviceChangeObserverToken token)
241 {
242     bool wasRemoved = m_deviceChangeObserverMap.remove(token);
243     ASSERT_UNUSED(wasRemoved, wasRemoved);
244 }
245
246 void UserMediaPermissionRequestManager::captureDevicesChanged()
247 {
248     // When new media input and/or output devices are made available, or any available input and/or
249     // output device becomes unavailable, the User Agent MUST run the following steps in browsing
250     // contexts where at least one of the following criteria are met, but in no other contexts:
251
252     // * The permission state of the "device-info" permission is "granted",
253     // * any of the input devices are attached to an active MediaStream in the browsing context, or
254     // * the active document is fully active and has focus.
255
256     auto identifiers = m_deviceChangeObserverMap.keys();
257     for (auto& identifier : identifiers) {
258         auto iterator = m_deviceChangeObserverMap.find(identifier);
259         if (iterator != m_deviceChangeObserverMap.end())
260             (iterator->value)();
261     }
262 }
263
264 } // namespace WebKit
265
266 #endif // ENABLE(MEDIA_STREAM)