7f17b2d3d57b8f8e5c56a3666ff2b7bb9ed6e856
[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())
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())
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     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
460         if (!m_page.isValid())
461             return;
462
463         m_page.websiteDataStore().deviceIdHashSaltStorage().deviceIdHashSaltForOrigin(requestOrigin.get(), topOrigin.get(), [this, weakThis = makeWeakPtr(*this), requestID, userMediaID, &originHasPersistentAccess] (String&& deviceIDHashSalt) {
464             if (!weakThis)
465                 return;
466             auto pendingRequest = m_pendingDeviceRequests.take(requestID);
467             if (!pendingRequest)
468                 return;
469
470             if (!m_page.isValid())
471                 return;
472
473             syncWithWebCorePrefs();
474
475             auto devices = RealtimeMediaSourceCenter::singleton().getMediaStreamDevices();
476             auto& request = *pendingRequest;
477             bool revealIdsAndLabels = originHasPersistentAccess || wasGrantedVideoOrAudioAccess(request->frameID(), request->userMediaDocumentSecurityOrigin(), request->topLevelDocumentSecurityOrigin());
478             int cameraCount = 0;
479             int microphoneCount = 0;
480
481             Vector<CaptureDevice> filteredDevices;
482             for (const auto& device : devices) {
483                 if (!device.enabled() || (device.type() != WebCore::CaptureDevice::DeviceType::Camera && device.type() != WebCore::CaptureDevice::DeviceType::Microphone))
484                     continue;
485
486                 if (!revealIdsAndLabels) {
487                     if (device.type() == WebCore::CaptureDevice::DeviceType::Camera && ++cameraCount > defaultMaximumCameraCount)
488                         continue;
489                     if (device.type() == WebCore::CaptureDevice::DeviceType::Microphone && ++microphoneCount > defaultMaximumMicrophoneCount)
490                         continue;
491                 }
492
493                 auto label = emptyString();
494                 auto id = emptyString();
495                 auto groupId = emptyString();
496                 if (revealIdsAndLabels) {
497                     label = device.label();
498                     id = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(device.persistentId(), deviceIDHashSalt);
499                     groupId = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(device.groupId(), deviceIDHashSalt);
500                 }
501
502                 filteredDevices.append(CaptureDevice(id, device.type(), label, groupId));
503             }
504
505             m_hasFilteredDeviceList = !revealIdsAndLabels;
506
507             m_page.process().send(Messages::WebPage::DidCompleteMediaDeviceEnumeration(userMediaID, WTFMove(filteredDevices), WTFMove(deviceIDHashSalt), originHasPersistentAccess), m_page.pageID());
508         });
509     };
510
511     getUserMediaPermissionInfo(requestID, frameID, WTFMove(completionHandler), WTFMove(userMediaDocumentOrigin), WTFMove(topLevelDocumentOrigin));
512 #else
513     UNUSED_PARAM(userMediaID);
514     UNUSED_PARAM(frameID);
515     UNUSED_PARAM(userMediaDocumentOrigin);
516     UNUSED_PARAM(topLevelDocumentOrigin);
517 #endif
518 }
519
520 void UserMediaPermissionRequestManagerProxy::syncWithWebCorePrefs() const
521 {
522 #if ENABLE(MEDIA_STREAM)
523     // Enable/disable the mock capture devices for the UI process as per the WebCore preferences. Note that
524     // this is a noop if the preference hasn't changed since the last time this was called.
525     bool mockDevicesEnabled = m_page.preferences().mockCaptureDevicesEnabled();
526     MockRealtimeMediaSourceCenter::setMockRealtimeMediaSourceCenterEnabled(mockDevicesEnabled);
527 #endif
528 }
529
530 void UserMediaPermissionRequestManagerProxy::captureStateChanged(MediaProducer::MediaStateFlags oldState, MediaProducer::MediaStateFlags newState)
531 {
532     if (!m_page.isValid())
533         return;
534
535 #if ENABLE(MEDIA_STREAM)
536     bool wasCapturingAudio = oldState & MediaProducer::AudioCaptureMask;
537     bool wasCapturingVideo = oldState & MediaProducer::VideoCaptureMask;
538     bool isCapturingAudio = newState & MediaProducer::AudioCaptureMask;
539     bool isCapturingVideo = newState & MediaProducer::VideoCaptureMask;
540
541     if ((wasCapturingAudio && !isCapturingAudio) || (wasCapturingVideo && !isCapturingVideo))
542         UserMediaProcessManager::singleton().endedCaptureSession(*this);
543     if ((!wasCapturingAudio && isCapturingAudio) || (!wasCapturingVideo && isCapturingVideo))
544         UserMediaProcessManager::singleton().startedCaptureSession(*this);
545
546     if (m_captureState == (newState & activeCaptureMask))
547         return;
548
549     m_captureState = newState & activeCaptureMask;
550
551     Seconds interval;
552     if (m_captureState & activeCaptureMask)
553         interval = Seconds::fromHours(m_page.preferences().longRunningMediaCaptureStreamRepromptIntervalInHours());
554     else
555         interval = Seconds::fromMinutes(m_page.preferences().inactiveMediaCaptureSteamRepromptIntervalInMinutes());
556
557     if (interval == m_currentWatchdogInterval)
558         return;
559
560     m_currentWatchdogInterval = interval;
561     m_watchdogTimer.startOneShot(m_currentWatchdogInterval);
562 #endif
563 }
564
565 void UserMediaPermissionRequestManagerProxy::viewIsBecomingVisible()
566 {
567     for (auto& request : m_pregrantedRequests)
568         request->allow();
569     m_pregrantedRequests.clear();
570 }
571
572 void UserMediaPermissionRequestManagerProxy::watchdogTimerFired()
573 {
574     m_grantedRequests.clear();
575     m_pregrantedRequests.clear();
576     m_currentWatchdogInterval = 0_s;
577     m_hasFilteredDeviceList = false;
578 }
579
580 } // namespace WebKit