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