81824b0ffd0d41d723efe43ab123b1924098efa7
[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     if (m_monitoringActivityStateChange)
57         m_page.corePage()->removeActivityStateChangeObserver(*this);
58     for (auto& sandboxExtension : m_userMediaDeviceSandboxExtensions)
59         sandboxExtension.value->revoke();
60 }
61
62 void UserMediaPermissionRequestManager::startUserMediaRequest(UserMediaRequest& request)
63 {
64     Document* document = request.document();
65     Frame* frame = document ? document->frame() : nullptr;
66
67     if (!frame || !document->page()) {
68         request.deny(UserMediaRequest::OtherFailure, emptyString());
69         return;
70     }
71
72     if (document->page()->canStartMedia()) {
73         sendUserMediaRequest(request);
74         return;
75     }
76
77     auto& pendingRequests = m_blockedUserMediaRequests.add(document, Vector<RefPtr<UserMediaRequest>>()).iterator->value;
78     if (pendingRequests.isEmpty())
79         document->addMediaCanStartListener(this);
80     pendingRequests.append(&request);
81 }
82
83 void UserMediaPermissionRequestManager::sendUserMediaRequest(UserMediaRequest& userRequest)
84 {
85     auto* frame = userRequest.document() ? userRequest.document()->frame() : nullptr;
86     if (!frame) {
87         userRequest.deny(UserMediaRequest::OtherFailure, emptyString());
88         return;
89     }
90
91     uint64_t requestID = generateRequestID();
92     m_idToUserMediaRequestMap.add(requestID, &userRequest);
93     m_userMediaRequestToIDMap.add(&userRequest, requestID);
94
95     WebFrame* webFrame = WebFrame::fromCoreFrame(*frame);
96     ASSERT(webFrame);
97
98     auto* topLevelDocumentOrigin = userRequest.topLevelDocumentOrigin();
99     m_page.send(Messages::WebPageProxy::RequestUserMediaPermissionForFrame(requestID, webFrame->frameID(), userRequest.userMediaDocumentOrigin()->data(), topLevelDocumentOrigin->data(), userRequest.request()));
100 }
101
102 void UserMediaPermissionRequestManager::cancelUserMediaRequest(UserMediaRequest& request)
103 {
104     uint64_t requestID = m_userMediaRequestToIDMap.take(&request);
105     if (!requestID)
106         return;
107
108     request.deny(UserMediaRequest::OtherFailure, emptyString());
109     m_idToUserMediaRequestMap.remove(requestID);
110     removeMediaRequestFromMaps(request);
111 }
112
113 void UserMediaPermissionRequestManager::mediaCanStart(Document& document)
114 {
115     auto pendingRequests = m_blockedUserMediaRequests.take(&document);
116     while (!pendingRequests.isEmpty()) {
117         if (!document.page()->canStartMedia()) {
118             m_blockedUserMediaRequests.add(&document, pendingRequests);
119             document.addMediaCanStartListener(this);
120             break;
121         }
122
123         sendUserMediaRequest(*pendingRequests.takeLast());
124     }
125 }
126
127 void UserMediaPermissionRequestManager::removeMediaRequestFromMaps(UserMediaRequest& request)
128 {
129     Document* document = request.document();
130     if (!document)
131         return;
132
133     auto pendingRequests = m_blockedUserMediaRequests.take(document);
134     for (auto& pendingRequest : pendingRequests) {
135         if (&request != pendingRequest.get())
136             continue;
137
138         if (pendingRequests.isEmpty())
139             request.document()->removeMediaCanStartListener(this);
140         else
141             m_blockedUserMediaRequests.add(document, pendingRequests);
142         break;
143     }
144
145     m_userMediaRequestToIDMap.remove(&request);
146 }
147
148 void UserMediaPermissionRequestManager::userMediaAccessWasGranted(uint64_t requestID, CaptureDevice&& audioDevice, CaptureDevice&& videoDevice, String&& deviceIdentifierHashSalt)
149 {
150     auto request = m_idToUserMediaRequestMap.take(requestID);
151     if (!request)
152         return;
153     removeMediaRequestFromMaps(*request);
154
155     request->allow(WTFMove(audioDevice), WTFMove(videoDevice), WTFMove(deviceIdentifierHashSalt));
156 }
157
158 void UserMediaPermissionRequestManager::userMediaAccessWasDenied(uint64_t requestID, WebCore::UserMediaRequest::MediaAccessDenialReason reason, String&& invalidConstraint)
159 {
160     auto request = m_idToUserMediaRequestMap.take(requestID);
161     if (!request)
162         return;
163     removeMediaRequestFromMaps(*request);
164
165     request->deny(reason, WTFMove(invalidConstraint));
166 }
167
168 void UserMediaPermissionRequestManager::enumerateMediaDevices(MediaDevicesEnumerationRequest& request)
169 {
170     auto* document = downcast<Document>(request.scriptExecutionContext());
171     auto* frame = document ? document->frame() : nullptr;
172
173     if (!frame) {
174         request.setDeviceInfo(Vector<CaptureDevice>(), emptyString(), false);
175         return;
176     }
177
178     uint64_t requestID = generateRequestID();
179     m_idToMediaDevicesEnumerationRequestMap.add(requestID, &request);
180     m_mediaDevicesEnumerationRequestToIDMap.add(&request, requestID);
181
182     WebFrame* webFrame = WebFrame::fromCoreFrame(*frame);
183     ASSERT(webFrame);
184
185     SecurityOrigin* topLevelDocumentOrigin = request.topLevelDocumentOrigin();
186     ASSERT(topLevelDocumentOrigin);
187     m_page.send(Messages::WebPageProxy::EnumerateMediaDevicesForFrame(requestID, webFrame->frameID(), request.userMediaDocumentOrigin()->data(), topLevelDocumentOrigin->data()));
188 }
189
190 void UserMediaPermissionRequestManager::cancelMediaDevicesEnumeration(WebCore::MediaDevicesEnumerationRequest& request)
191 {
192     uint64_t requestID = m_mediaDevicesEnumerationRequestToIDMap.take(&request);
193     if (!requestID)
194         return;
195     request.setDeviceInfo(Vector<CaptureDevice>(), emptyString(), false);
196     m_idToMediaDevicesEnumerationRequestMap.remove(requestID);
197 }
198
199 void UserMediaPermissionRequestManager::didCompleteMediaDeviceEnumeration(uint64_t requestID, const Vector<CaptureDevice>& deviceList, String&& mediaDeviceIdentifierHashSalt, bool hasPersistentAccess)
200 {
201     RefPtr<MediaDevicesEnumerationRequest> request = m_idToMediaDevicesEnumerationRequestMap.take(requestID);
202     if (!request)
203         return;
204     m_mediaDevicesEnumerationRequestToIDMap.remove(request);
205     
206     request->setDeviceInfo(deviceList, WTFMove(mediaDeviceIdentifierHashSalt), hasPersistentAccess);
207 }
208
209 void UserMediaPermissionRequestManager::grantUserMediaDeviceSandboxExtensions(MediaDeviceSandboxExtensions&& extensions)
210 {
211     for (size_t i = 0; i < extensions.size(); i++) {
212         const auto& extension = extensions[i];
213         extension.second->consume();
214         RELEASE_LOG(WebRTC, "UserMediaPermissionRequestManager::grantUserMediaDeviceSandboxExtensions - granted extension %s", extension.first.utf8().data());
215         m_userMediaDeviceSandboxExtensions.add(extension.first, extension.second.copyRef());
216     }
217 }
218
219 void UserMediaPermissionRequestManager::revokeUserMediaDeviceSandboxExtensions(const Vector<String>& extensionIDs)
220 {
221     for (const auto& extensionID : extensionIDs) {
222         auto extension = m_userMediaDeviceSandboxExtensions.take(extensionID);
223         if (extension) {
224             extension->revoke();
225             RELEASE_LOG(WebRTC, "UserMediaPermissionRequestManager::revokeUserMediaDeviceSandboxExtensions - revoked extension %s", extensionID.utf8().data());
226         }
227     }
228 }
229
230 UserMediaClient::DeviceChangeObserverToken UserMediaPermissionRequestManager::addDeviceChangeObserver(WTF::Function<void()>&& observer)
231 {
232     auto identifier = generateObjectIdentifier<WebCore::UserMediaClient::DeviceChangeObserverTokenType>();
233     m_deviceChangeObserverMap.add(identifier, WTFMove(observer));
234
235     if (!m_monitoringDeviceChange) {
236         m_monitoringDeviceChange = true;
237         m_page.send(Messages::WebPageProxy::BeginMonitoringCaptureDevices());
238     }
239     return identifier;
240 }
241
242 void UserMediaPermissionRequestManager::removeDeviceChangeObserver(UserMediaClient::DeviceChangeObserverToken token)
243 {
244     bool wasRemoved = m_deviceChangeObserverMap.remove(token);
245     ASSERT_UNUSED(wasRemoved, wasRemoved);
246 }
247
248 void UserMediaPermissionRequestManager::captureDevicesChanged(DeviceAccessState accessState)
249 {
250     // When new media input and/or output devices are made available, or any available input and/or
251     // output device becomes unavailable, the User Agent MUST run the following steps in browsing
252     // contexts where at least one of the following criteria are met, but in no other contexts:
253
254     // * The permission state of the "device-info" permission is "granted",
255     // * any of the input devices are attached to an active MediaStream in the browsing context, or
256     // * the active document is fully active and has focus.
257
258     bool isActive = m_page.corePage()->activityState().containsAll(focusedActiveWindow);
259     if (!isActive && accessState == DeviceAccessState::NoAccess) {
260         if (!isActive) {
261             if (!m_monitoringActivityStateChange) {
262                 m_monitoringActivityStateChange = true;
263                 m_page.corePage()->addActivityStateChangeObserver(*this);
264             }
265             m_pendingDeviceChangeEvent = true;
266             m_accessStateWhenDevicesChanged = accessState;
267         }
268         return;
269     }
270
271     auto identifiers = m_deviceChangeObserverMap.keys();
272     for (auto& identifier : identifiers) {
273         auto iterator = m_deviceChangeObserverMap.find(identifier);
274         if (iterator != m_deviceChangeObserverMap.end())
275             (iterator->value)();
276     }
277 }
278
279 void UserMediaPermissionRequestManager::activityStateDidChange(OptionSet<WebCore::ActivityState::Flag> oldActivityState, OptionSet<WebCore::ActivityState::Flag> newActivityState)
280 {
281     if (!newActivityState.containsAll(focusedActiveWindow))
282         return;
283
284     RunLoop::main().dispatch([this, weakThis = makeWeakPtr(*this)]() mutable {
285         if (!weakThis || !m_monitoringActivityStateChange)
286             return;
287
288         m_monitoringActivityStateChange = false;
289         m_page.corePage()->removeActivityStateChangeObserver(*this);
290     });
291
292     if (!m_pendingDeviceChangeEvent)
293         return;
294
295     m_pendingDeviceChangeEvent = false;
296     auto accessState = m_accessStateWhenDevicesChanged;
297     m_accessStateWhenDevicesChanged = DeviceAccessState::NoAccess;
298     captureDevicesChanged(accessState);
299 }
300
301 } // namespace WebKit
302
303 #endif // ENABLE(MEDIA_STREAM)