dee906f8b085dbcfd294c6bac0883c2622dac501
[WebKit-https.git] / Source / WebKit / UIProcess / UserMediaPermissionRequestManagerProxy.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 "UserMediaPermissionRequestManagerProxy.h"
22
23 #include "APISecurityOrigin.h"
24 #include "APIUIClient.h"
25 #include "DeviceIdHashSaltStorage.h"
26 #include "UserMediaPermissionRequestManager.h"
27 #include "UserMediaProcessManager.h"
28 #include "WebAutomationSession.h"
29 #include "WebPageMessages.h"
30 #include "WebPageProxy.h"
31 #include "WebProcess.h"
32 #include "WebProcessPool.h"
33 #include "WebProcessProxy.h"
34 #include "WebsiteDataStore.h"
35 #include <WebCore/MediaConstraints.h>
36 #include <WebCore/MockRealtimeMediaSourceCenter.h>
37 #include <WebCore/RealtimeMediaSource.h>
38 #include <WebCore/SecurityOriginData.h>
39 #include <WebCore/UserMediaRequest.h>
40
41 namespace WebKit {
42 using namespace WebCore;
43
44 #if ENABLE(MEDIA_STREAM)
45 static const MediaProducer::MediaStateFlags activeCaptureMask = MediaProducer::HasActiveAudioCaptureDevice | MediaProducer::HasActiveVideoCaptureDevice;
46
47 static uint64_t generateRequestID()
48 {
49     static uint64_t uniqueRequestID = 1;
50     return uniqueRequestID++;
51 }
52 #endif
53
54 UserMediaPermissionRequestManagerProxy::UserMediaPermissionRequestManagerProxy(WebPageProxy& page)
55     : m_page(page)
56     , m_rejectionTimer(RunLoop::main(), this, &UserMediaPermissionRequestManagerProxy::rejectionTimerFired)
57     , m_watchdogTimer(RunLoop::main(), this, &UserMediaPermissionRequestManagerProxy::watchdogTimerFired)
58 {
59 #if ENABLE(MEDIA_STREAM)
60     UserMediaProcessManager::singleton().addUserMediaPermissionRequestManagerProxy(*this);
61 #endif
62 }
63
64 UserMediaPermissionRequestManagerProxy::~UserMediaPermissionRequestManagerProxy()
65 {
66 #if ENABLE(MEDIA_STREAM)
67     UserMediaProcessManager::singleton().removeUserMediaPermissionRequestManagerProxy(*this);
68 #endif
69     invalidatePendingRequests();
70 }
71
72 void UserMediaPermissionRequestManagerProxy::invalidatePendingRequests()
73 {
74     for (auto& request : m_pendingUserMediaRequests.values())
75         request->invalidate();
76     m_pendingUserMediaRequests.clear();
77
78     for (auto& request : m_pendingDeviceRequests.values())
79         request->invalidate();
80     m_pendingDeviceRequests.clear();
81 }
82
83 void UserMediaPermissionRequestManagerProxy::stopCapture()
84 {
85     invalidatePendingRequests();
86     m_page.stopMediaCapture();
87 }
88
89 void UserMediaPermissionRequestManagerProxy::captureDevicesChanged()
90 {
91 #if ENABLE(MEDIA_STREAM)
92     if (!m_page.isValid() || !m_page.mainFrame())
93         return;
94
95     auto requestID = generateRequestID();
96     auto handler = [this, requestID](bool originHasPersistentAccess) mutable {
97
98         auto pendingRequest = m_pendingDeviceRequests.take(requestID);
99         if (!pendingRequest || !m_page.isValid())
100             return;
101
102         if (m_grantedRequests.isEmpty() && !originHasPersistentAccess)
103             return;
104
105         m_page.process().send(Messages::WebPage::CaptureDevicesChanged(), m_page.pageID());
106     };
107
108     auto origin = WebCore::SecurityOrigin::create(m_page.mainFrame()->url());
109     getUserMediaPermissionInfo(requestID, m_page.mainFrame()->frameID(), WTFMove(handler), origin.get(), WTFMove(origin));
110 #endif
111 }
112
113 void UserMediaPermissionRequestManagerProxy::clearCachedState()
114 {
115     invalidatePendingRequests();
116 }
117
118 Ref<UserMediaPermissionRequestProxy> UserMediaPermissionRequestManagerProxy::createPermissionRequest(uint64_t userMediaID, uint64_t mainFrameID, uint64_t frameID, Ref<SecurityOrigin>&& userMediaDocumentOrigin, Ref<SecurityOrigin>&& topLevelDocumentOrigin, Vector<CaptureDevice>&& audioDevices, Vector<CaptureDevice>&& videoDevices, MediaStreamRequest&& request)
119 {
120     auto permissionRequest = UserMediaPermissionRequestProxy::create(*this, userMediaID, mainFrameID, frameID, WTFMove(userMediaDocumentOrigin), WTFMove(topLevelDocumentOrigin), WTFMove(audioDevices), WTFMove(videoDevices), WTFMove(request));
121     m_pendingUserMediaRequests.add(userMediaID, permissionRequest.ptr());
122     return permissionRequest;
123 }
124
125 #if ENABLE(MEDIA_STREAM)
126 static uint64_t toWebCore(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason reason)
127 {
128     switch (reason) {
129     case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoConstraints:
130         return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::NoConstraints);
131     case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::UserMediaDisabled:
132         return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::UserMediaDisabled);
133     case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoCaptureDevices:
134         return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::NoCaptureDevices);
135     case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::InvalidConstraint:
136         return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::InvalidConstraint);
137     case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::HardwareError:
138         return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::HardwareError);
139     case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied:
140         return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::PermissionDenied);
141     case UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::OtherFailure:
142         return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::OtherFailure);
143     }
144
145     ASSERT_NOT_REACHED();
146     return static_cast<uint64_t>(UserMediaRequest::MediaAccessDenialReason::OtherFailure);
147 }
148 #endif
149
150 void UserMediaPermissionRequestManagerProxy::userMediaAccessWasDenied(uint64_t userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason reason)
151 {
152     if (!m_page.isValid())
153         return;
154
155     auto request = m_pendingUserMediaRequests.take(userMediaID);
156     if (!request)
157         return;
158
159     if (reason == UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied)
160         m_deniedRequests.append(DeniedRequest { request->mainFrameID(), request->userMediaDocumentSecurityOrigin(), request->topLevelDocumentSecurityOrigin(), request->requiresAudioCapture(), request->requiresVideoCapture(), request->requiresDisplayCapture() });
161
162     denyRequest(userMediaID, reason, emptyString());
163 }
164
165 void UserMediaPermissionRequestManagerProxy::denyRequest(uint64_t userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason reason, const String& invalidConstraint)
166 {
167     ASSERT(m_page.isValid());
168
169 #if ENABLE(MEDIA_STREAM)
170     m_page.process().send(Messages::WebPage::UserMediaAccessWasDenied(userMediaID, toWebCore(reason), invalidConstraint), m_page.pageID());
171 #else
172     UNUSED_PARAM(reason);
173     UNUSED_PARAM(invalidConstraint);
174 #endif
175 }
176
177 void UserMediaPermissionRequestManagerProxy::userMediaAccessWasGranted(uint64_t userMediaID, CaptureDevice&& audioDevice, CaptureDevice&& videoDevice)
178 {
179     ASSERT(audioDevice || videoDevice);
180
181     if (!m_page.isValid() || !m_page.websiteDataStore().deviceIdHashSaltStorage())
182         return;
183
184 #if ENABLE(MEDIA_STREAM)
185     auto request = m_pendingUserMediaRequests.take(userMediaID);
186     if (!request)
187         return;
188
189     m_page.websiteDataStore().deviceIdHashSaltStorage()->deviceIdHashSaltForOrigin(request->userMediaDocumentSecurityOrigin(), request->topLevelDocumentSecurityOrigin(), [this, weakThis = makeWeakPtr(*this), userMediaID, audioDevice = WTFMove(audioDevice), videoDevice = WTFMove(videoDevice), localRequest = request.copyRef()] (String&& deviceIDHashSalt) mutable {
190         if (!weakThis)
191             return;
192         if (grantAccess(userMediaID, WTFMove(audioDevice), WTFMove(videoDevice), WTFMove(deviceIDHashSalt))) {
193             m_grantedRequests.append(localRequest.releaseNonNull());
194             if (m_hasFilteredDeviceList)
195                 captureDevicesChanged();
196             m_hasFilteredDeviceList = false;
197         }
198     });
199 #else
200     UNUSED_PARAM(userMediaID);
201     UNUSED_PARAM(audioDevice);
202     UNUSED_PARAM(videoDevice);
203 #endif
204 }
205
206 #if ENABLE(MEDIA_STREAM)
207 void UserMediaPermissionRequestManagerProxy::resetAccess(uint64_t frameID)
208 {
209     m_grantedRequests.removeAllMatching([frameID](const auto& grantedRequest) {
210         return grantedRequest->mainFrameID() == frameID;
211     });
212     m_pregrantedRequests.clear();
213     m_deniedRequests.clear();
214     m_hasFilteredDeviceList = false;
215 }
216
217 const UserMediaPermissionRequestProxy* UserMediaPermissionRequestManagerProxy::searchForGrantedRequest(uint64_t frameID, const SecurityOrigin& userMediaDocumentOrigin, const SecurityOrigin& topLevelDocumentOrigin, bool needsAudio, bool needsVideo) const
218 {
219     if (m_page.isMediaStreamCaptureMuted())
220         return nullptr;
221
222     bool checkForAudio = needsAudio;
223     bool checkForVideo = needsVideo;
224     for (const auto& grantedRequest : m_grantedRequests) {
225         if (grantedRequest->requiresDisplayCapture())
226             continue;
227         if (!grantedRequest->userMediaDocumentSecurityOrigin().isSameSchemeHostPort(userMediaDocumentOrigin))
228             continue;
229         if (!grantedRequest->topLevelDocumentSecurityOrigin().isSameSchemeHostPort(topLevelDocumentOrigin))
230             continue;
231         if (grantedRequest->frameID() != frameID)
232             continue;
233
234         if (grantedRequest->requiresVideoCapture())
235             checkForVideo = false;
236
237         if (grantedRequest->requiresAudioCapture())
238             checkForAudio = false;
239
240         if (checkForVideo || checkForAudio)
241             continue;
242
243         return grantedRequest.ptr();
244     }
245     return nullptr;
246 }
247
248 bool UserMediaPermissionRequestManagerProxy::wasRequestDenied(uint64_t mainFrameID, const SecurityOrigin& userMediaDocumentOrigin, const SecurityOrigin& topLevelDocumentOrigin, bool needsAudio, bool needsVideo, bool needsScreenCapture)
249 {
250     for (const auto& deniedRequest : m_deniedRequests) {
251         if (!deniedRequest.userMediaDocumentOrigin->isSameSchemeHostPort(userMediaDocumentOrigin))
252             continue;
253         if (!deniedRequest.topLevelDocumentOrigin->isSameSchemeHostPort(topLevelDocumentOrigin))
254             continue;
255         if (deniedRequest.mainFrameID != mainFrameID)
256             continue;
257         if (deniedRequest.isAudioDenied && needsAudio)
258             return true;
259         if (deniedRequest.isVideoDenied && needsVideo)
260             return true;
261         if (deniedRequest.isScreenCaptureDenied && needsScreenCapture)
262             return true;
263     }
264     return false;
265 }
266
267 bool UserMediaPermissionRequestManagerProxy::grantAccess(uint64_t userMediaID, const CaptureDevice audioDevice, const CaptureDevice videoDevice, const String& deviceIdentifierHashSalt)
268 {
269     if (!UserMediaProcessManager::singleton().willCreateMediaStream(*this, !!audioDevice, !!videoDevice)) {
270         denyRequest(userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::OtherFailure, "Unable to extend sandbox.");
271         return false;
272     }
273
274     m_page.process().send(Messages::WebPage::UserMediaAccessWasGranted(userMediaID, audioDevice, videoDevice, deviceIdentifierHashSalt), m_page.pageID());
275     return true;
276 }
277 #endif
278
279 void UserMediaPermissionRequestManagerProxy::rejectionTimerFired()
280 {
281     uint64_t userMediaID = m_pendingRejections[0];
282     m_pendingRejections.remove(0);
283
284     denyRequest(userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied, emptyString());
285     if (!m_pendingRejections.isEmpty())
286         scheduleNextRejection();
287 }
288
289 void UserMediaPermissionRequestManagerProxy::scheduleNextRejection()
290 {
291     const double mimimumDelayBeforeReplying = .25;
292     if (!m_rejectionTimer.isActive())
293         m_rejectionTimer.startOneShot(Seconds(mimimumDelayBeforeReplying + randomNumber()));
294 }
295
296 #if ENABLE(MEDIA_STREAM)
297 UserMediaPermissionRequestManagerProxy::RequestAction UserMediaPermissionRequestManagerProxy::getRequestAction(uint64_t frameID, SecurityOrigin& userMediaDocumentOrigin, SecurityOrigin& topLevelDocumentOrigin, const MediaStreamRequest& userRequest, Vector<CaptureDevice>& audioDevices, Vector<CaptureDevice>& videoDevices)
298 {
299     bool requestingScreenCapture = userRequest.type == MediaStreamRequest::Type::DisplayMedia;
300     ASSERT(!(requestingScreenCapture && videoDevices.isEmpty()));
301     ASSERT(!(requestingScreenCapture && !audioDevices.isEmpty()));
302     bool requestingCamera = !requestingScreenCapture && !videoDevices.isEmpty();
303     bool requestingMicrophone = !audioDevices.isEmpty();
304
305     if (wasRequestDenied(frameID, userMediaDocumentOrigin, topLevelDocumentOrigin, requestingMicrophone, requestingCamera, requestingScreenCapture))
306         return RequestAction::Deny;
307
308     if (userRequest.type == MediaStreamRequest::Type::DisplayMedia)
309         return RequestAction::Prompt;
310
311     return searchForGrantedRequest(frameID, userMediaDocumentOrigin, topLevelDocumentOrigin, requestingMicrophone, requestingCamera) ? RequestAction::Grant : RequestAction::Prompt;
312 }
313 #endif
314
315 void UserMediaPermissionRequestManagerProxy::requestUserMediaPermissionForFrame(uint64_t userMediaID, uint64_t frameID, Ref<SecurityOrigin>&& userMediaDocumentOrigin, Ref<SecurityOrigin>&& topLevelDocumentOrigin, const MediaStreamRequest& userRequest)
316 {
317 #if ENABLE(MEDIA_STREAM)
318     if (!UserMediaProcessManager::singleton().captureEnabled()) {
319         m_pendingRejections.append(userMediaID);
320         scheduleNextRejection();
321         return;
322     }
323
324     RealtimeMediaSourceCenter::InvalidConstraintsHandler invalidHandler = [this, userMediaID](const String& invalidConstraint) {
325         if (!m_page.isValid())
326             return;
327
328         denyRequest(userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::InvalidConstraint, invalidConstraint);
329     };
330
331     auto validHandler = [this, userMediaID, frameID, userMediaDocumentOrigin = userMediaDocumentOrigin.copyRef(), topLevelDocumentOrigin = topLevelDocumentOrigin.copyRef(), localUserRequest = userRequest](Vector<CaptureDevice>&& audioDevices, Vector<CaptureDevice>&& videoDevices, String&& deviceIdentifierHashSalt) mutable {
332         if (!m_page.isValid() || !m_page.mainFrame())
333             return;
334
335         if (videoDevices.isEmpty() && audioDevices.isEmpty()) {
336             denyRequest(userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoConstraints, emptyString());
337             return;
338         }
339
340         auto action = getRequestAction(m_page.mainFrame()->frameID(), userMediaDocumentOrigin.get(), topLevelDocumentOrigin.get(), localUserRequest, audioDevices, videoDevices);
341         if (action == RequestAction::Deny) {
342             denyRequest(userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied, emptyString());
343             return;
344         }
345
346         if (action == RequestAction::Grant) {
347             ASSERT(localUserRequest.type != MediaStreamRequest::Type::DisplayMedia);
348
349             if (m_page.isViewVisible()) {
350                 // We select the first available devices, but the current client API allows client to select which device to pick.
351                 // FIXME: Remove the possiblity for the client to do the device selection.
352                 auto audioDevice = !audioDevices.isEmpty() ? audioDevices[0] : CaptureDevice();
353                 auto videoDevice = !videoDevices.isEmpty() ? videoDevices[0] : CaptureDevice();
354                 grantAccess(userMediaID, WTFMove(audioDevice), WTFMove(videoDevice), WTFMove(deviceIdentifierHashSalt));
355             } else
356                 m_pregrantedRequests.append(createPermissionRequest(userMediaID, m_page.mainFrame()->frameID(), frameID, WTFMove(userMediaDocumentOrigin), WTFMove(topLevelDocumentOrigin), WTFMove(audioDevices), WTFMove(videoDevices), WTFMove(localUserRequest)));
357
358             return;
359         }
360
361         auto userMediaOrigin = API::SecurityOrigin::create(userMediaDocumentOrigin.get());
362         auto topLevelOrigin = API::SecurityOrigin::create(topLevelDocumentOrigin.get());
363         auto pendingRequest = createPermissionRequest(userMediaID, m_page.mainFrame()->frameID(), frameID, WTFMove(userMediaDocumentOrigin), WTFMove(topLevelDocumentOrigin), WTFMove(audioDevices), WTFMove(videoDevices), WTFMove(localUserRequest));
364
365         if (m_page.isControlledByAutomation()) {
366             if (WebAutomationSession* automationSession = m_page.process().processPool().automationSession()) {
367                 if (automationSession->shouldAllowGetUserMediaForPage(m_page))
368                     pendingRequest->allow();
369                 else
370                     userMediaAccessWasDenied(userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
371
372                 return;
373             }
374         }
375
376         if (m_page.preferences().mockCaptureDevicesEnabled() && !m_page.preferences().mockCaptureDevicesPromptEnabled()) {
377             pendingRequest->allow();
378             return;
379         }
380
381         if (!m_page.uiClient().decidePolicyForUserMediaPermissionRequest(m_page, *m_page.process().webFrame(frameID), WTFMove(userMediaOrigin), WTFMove(topLevelOrigin), pendingRequest.get()))
382             userMediaAccessWasDenied(userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::UserMediaDisabled);
383     };
384
385     auto requestID = generateRequestID();
386     auto havePermissionInfoHandler = [this, requestID, validHandler = WTFMove(validHandler), invalidHandler = WTFMove(invalidHandler), localUserRequest = userRequest](bool originHasPersistentAccess) mutable {
387
388         auto pendingRequest = m_pendingDeviceRequests.take(requestID);
389         if (!pendingRequest)
390             return;
391
392         if (!m_page.isValid() || !m_page.websiteDataStore().deviceIdHashSaltStorage())
393             return;
394
395         syncWithWebCorePrefs();
396
397         m_page.websiteDataStore().deviceIdHashSaltStorage()->deviceIdHashSaltForOrigin(pendingRequest.value()->userMediaDocumentSecurityOrigin(), pendingRequest.value()->topLevelDocumentSecurityOrigin(), [validHandler = WTFMove(validHandler), invalidHandler = WTFMove(invalidHandler), localUserRequest = localUserRequest] (String&& deviceIDHashSalt) mutable {
398             RealtimeMediaSourceCenter::singleton().validateRequestConstraints(WTFMove(validHandler), WTFMove(invalidHandler), WTFMove(localUserRequest), WTFMove(deviceIDHashSalt));
399         });
400     };
401
402     getUserMediaPermissionInfo(requestID, frameID, WTFMove(havePermissionInfoHandler), WTFMove(userMediaDocumentOrigin), WTFMove(topLevelDocumentOrigin));
403 #else
404     UNUSED_PARAM(userMediaID);
405     UNUSED_PARAM(frameID);
406     UNUSED_PARAM(userMediaDocumentOrigin);
407     UNUSED_PARAM(topLevelDocumentOrigin);
408     UNUSED_PARAM(userRequest);
409 #endif
410 }
411
412 #if ENABLE(MEDIA_STREAM)
413 void UserMediaPermissionRequestManagerProxy::getUserMediaPermissionInfo(uint64_t requestID, uint64_t frameID, UserMediaPermissionCheckProxy::CompletionHandler&& handler, Ref<SecurityOrigin>&& userMediaDocumentOrigin, Ref<SecurityOrigin>&& topLevelDocumentOrigin)
414 {
415     if (!m_page.websiteDataStore().deviceIdHashSaltStorage()) {
416         handler(false);
417         return;
418     }
419
420     auto userMediaOrigin = API::SecurityOrigin::create(userMediaDocumentOrigin.get());
421     auto topLevelOrigin = API::SecurityOrigin::create(topLevelDocumentOrigin.get());
422     auto request = UserMediaPermissionCheckProxy::create(frameID, WTFMove(handler), WTFMove(userMediaDocumentOrigin), WTFMove(topLevelDocumentOrigin));
423
424     m_pendingDeviceRequests.add(requestID, request.copyRef());
425     if (!m_page.uiClient().checkUserMediaPermissionForOrigin(m_page, *m_page.process().webFrame(frameID), userMediaOrigin.get(), topLevelOrigin.get(), request.get())) {
426         m_pendingDeviceRequests.take(requestID);
427         handler(false);
428     }
429 }
430
431 bool UserMediaPermissionRequestManagerProxy::wasGrantedVideoOrAudioAccess(uint64_t frameID, const SecurityOrigin& userMediaDocumentOrigin, const SecurityOrigin& topLevelDocumentOrigin)
432 {
433     for (const auto& grantedRequest : m_grantedRequests) {
434         if (grantedRequest->requiresDisplayCapture())
435             continue;
436         if (!grantedRequest->userMediaDocumentSecurityOrigin().isSameSchemeHostPort(userMediaDocumentOrigin))
437             continue;
438         if (!grantedRequest->topLevelDocumentSecurityOrigin().isSameSchemeHostPort(topLevelDocumentOrigin))
439             continue;
440         if (grantedRequest->frameID() != frameID)
441             continue;
442
443         if (grantedRequest->requiresVideoCapture() || grantedRequest->requiresAudioCapture())
444             return true;
445     }
446
447     return false;
448 }
449 #endif
450
451 void UserMediaPermissionRequestManagerProxy::enumerateMediaDevicesForFrame(uint64_t userMediaID, uint64_t frameID, Ref<SecurityOrigin>&& userMediaDocumentOrigin, Ref<SecurityOrigin>&& topLevelDocumentOrigin)
452 {
453 #if ENABLE(MEDIA_STREAM)
454
455 #if PLATFORM(IOS_FAMILY)
456     static const int defaultMaximumCameraCount = 2;
457 #else
458     static const int defaultMaximumCameraCount = 1;
459 #endif
460     static const int defaultMaximumMicrophoneCount = 1;
461
462     auto requestID = generateRequestID();
463     auto completionHandler = [this, requestID, userMediaID, requestOrigin = userMediaDocumentOrigin.copyRef(), topOrigin = topLevelDocumentOrigin.copyRef()](bool originHasPersistentAccess) {
464         m_page.websiteDataStore().deviceIdHashSaltStorage()->deviceIdHashSaltForOrigin(requestOrigin.get(), topOrigin.get(), [this, weakThis = makeWeakPtr(*this), requestID, userMediaID, &originHasPersistentAccess] (String&& deviceIDHashSalt) {
465             if (!weakThis)
466                 return;
467             auto pendingRequest = m_pendingDeviceRequests.take(requestID);
468             if (!pendingRequest)
469                 return;
470
471             if (!m_page.isValid() || !m_page.websiteDataStore().deviceIdHashSaltStorage())
472                 return;
473
474             syncWithWebCorePrefs();
475
476             auto devices = RealtimeMediaSourceCenter::singleton().getMediaStreamDevices();
477             auto& request = *pendingRequest;
478             bool revealIdsAndLabels = originHasPersistentAccess || wasGrantedVideoOrAudioAccess(request->frameID(), request->userMediaDocumentSecurityOrigin(), request->topLevelDocumentSecurityOrigin());
479             int cameraCount = 0;
480             int microphoneCount = 0;
481
482             Vector<CaptureDevice> filteredDevices;
483             for (const auto& device : devices) {
484                 if (!device.enabled() || (device.type() != WebCore::CaptureDevice::DeviceType::Camera && device.type() != WebCore::CaptureDevice::DeviceType::Microphone))
485                     continue;
486
487                 if (!revealIdsAndLabels) {
488                     if (device.type() == WebCore::CaptureDevice::DeviceType::Camera && ++cameraCount > defaultMaximumCameraCount)
489                         continue;
490                     if (device.type() == WebCore::CaptureDevice::DeviceType::Microphone && ++microphoneCount > defaultMaximumMicrophoneCount)
491                         continue;
492                 }
493
494                 auto label = emptyString();
495                 auto id = emptyString();
496                 auto groupId = emptyString();
497                 if (revealIdsAndLabels) {
498                     label = device.label();
499                     id = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(device.persistentId(), deviceIDHashSalt);
500                     groupId = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(device.groupId(), deviceIDHashSalt);
501                 }
502
503                 filteredDevices.append(CaptureDevice(id, device.type(), label, groupId));
504             }
505
506             m_hasFilteredDeviceList = !revealIdsAndLabels;
507
508             m_page.process().send(Messages::WebPage::DidCompleteMediaDeviceEnumeration(userMediaID, WTFMove(filteredDevices), WTFMove(deviceIDHashSalt), originHasPersistentAccess), m_page.pageID());
509         });
510     };
511
512     getUserMediaPermissionInfo(requestID, frameID, WTFMove(completionHandler), WTFMove(userMediaDocumentOrigin), WTFMove(topLevelDocumentOrigin));
513 #else
514     UNUSED_PARAM(userMediaID);
515     UNUSED_PARAM(frameID);
516     UNUSED_PARAM(userMediaDocumentOrigin);
517     UNUSED_PARAM(topLevelDocumentOrigin);
518 #endif
519 }
520
521 void UserMediaPermissionRequestManagerProxy::syncWithWebCorePrefs() const
522 {
523 #if ENABLE(MEDIA_STREAM)
524     // Enable/disable the mock capture devices for the UI process as per the WebCore preferences. Note that
525     // this is a noop if the preference hasn't changed since the last time this was called.
526     bool mockDevicesEnabled = m_page.preferences().mockCaptureDevicesEnabled();
527     MockRealtimeMediaSourceCenter::setMockRealtimeMediaSourceCenterEnabled(mockDevicesEnabled);
528 #endif
529 }
530
531 void UserMediaPermissionRequestManagerProxy::captureStateChanged(MediaProducer::MediaStateFlags oldState, MediaProducer::MediaStateFlags newState)
532 {
533     if (!m_page.isValid())
534         return;
535
536 #if ENABLE(MEDIA_STREAM)
537     bool wasCapturingAudio = oldState & MediaProducer::AudioCaptureMask;
538     bool wasCapturingVideo = oldState & MediaProducer::VideoCaptureMask;
539     bool isCapturingAudio = newState & MediaProducer::AudioCaptureMask;
540     bool isCapturingVideo = newState & MediaProducer::VideoCaptureMask;
541
542     if ((wasCapturingAudio && !isCapturingAudio) || (wasCapturingVideo && !isCapturingVideo))
543         UserMediaProcessManager::singleton().endedCaptureSession(*this);
544     if ((!wasCapturingAudio && isCapturingAudio) || (!wasCapturingVideo && isCapturingVideo))
545         UserMediaProcessManager::singleton().startedCaptureSession(*this);
546
547     if (m_captureState == (newState & activeCaptureMask))
548         return;
549
550     m_captureState = newState & activeCaptureMask;
551
552     Seconds interval;
553     if (m_captureState & activeCaptureMask)
554         interval = Seconds::fromHours(m_page.preferences().longRunningMediaCaptureStreamRepromptIntervalInHours());
555     else
556         interval = Seconds::fromMinutes(m_page.preferences().inactiveMediaCaptureSteamRepromptIntervalInMinutes());
557
558     if (interval == m_currentWatchdogInterval)
559         return;
560
561     m_currentWatchdogInterval = interval;
562     m_watchdogTimer.startOneShot(m_currentWatchdogInterval);
563 #endif
564 }
565
566 void UserMediaPermissionRequestManagerProxy::viewIsBecomingVisible()
567 {
568     for (auto& request : m_pregrantedRequests)
569         request->allow();
570     m_pregrantedRequests.clear();
571 }
572
573 void UserMediaPermissionRequestManagerProxy::watchdogTimerFired()
574 {
575     m_grantedRequests.clear();
576     m_pregrantedRequests.clear();
577     m_currentWatchdogInterval = 0_s;
578     m_hasFilteredDeviceList = false;
579 }
580
581 } // namespace WebKit