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