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