<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[246093] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/246093">246093</a></dd>
<dt>Author</dt> <dd>youenn@apple.com</dd>
<dt>Date</dt> <dd>2019-06-04 18:03:04 -0700 (Tue, 04 Jun 2019)</dd>
</dl>

<h3>Log Message</h3>
<pre>getUserMedia requests should be processed sequentially in UIProcess
https://bugs.webkit.org/show_bug.cgi?id=198430
<rdar://problem/51311420>

Reviewed by Eric Carlson.

Source/WebKit:

Before the patch, we process all incoming gum/gdm requests in parallel.
We now queueu them and process them one at a time.
This allows to take into consideration state changes triggered by one request for the next one.
In particular, if a user grants a request, this might grant the next one as well.

To implement that, we keep a reference of the current request to process.
We queue other requests happening whenever another request comes.
When the request is processed, we look at the next one in the queue.
To ensure we do not stop processing the queue for no good reason, some refactoring is done:
- queue processing happens when sending back IPC response to WebProcess.
- denyRequest/grantRequest are consistently called in the manager proxy.

* UIProcess/UserMediaPermissionRequestManagerProxy.cpp:
(WebKit::UserMediaPermissionRequestManagerProxy::invalidatePendingRequests):
Invalidate pregranted requests as well.
(WebKit::UserMediaPermissionRequestManagerProxy::denyRequest):
Renamed from userMediaAccessWasDenied to denyRequest.
This method is now consistently used whenever the manager proxy wants to deny the request.
It does the IPC to the WebProcess and triggers processing of the next request.
(WebKit::UserMediaPermissionRequestManagerProxy::grantRequest):
Renamed from userMediaAccessWasGranted to grantRequest.
(WebKit::UserMediaPermissionRequestManagerProxy::finishGrantingRequest):
This method is now consistently used whenever the manager proxy wants to deny the request.
It does the IPC to the WebProcess and triggers processing of the next request.
(WebKit::UserMediaPermissionRequestManagerProxy::rejectionTimerFired):
We now keep a queue of request instead of request IDs to make the deny code path more consistent.
(WebKit::UserMediaPermissionRequestManagerProxy::requestUserMediaPermissionForFrame):
(WebKit::UserMediaPermissionRequestManagerProxy::processNextUserMediaRequestIfNeeded):
(WebKit::UserMediaPermissionRequestManagerProxy::startProcessingUserMediaPermissionRequest):
(WebKit::UserMediaPermissionRequestManagerProxy::processUserMediaPermissionRequest):
To make sure we do not process a different request, we keep a pointer to the request and compare it with the current media request.
(WebKit::UserMediaPermissionRequestManagerProxy::processUserMediaPermissionInvalidRequest):
(WebKit::UserMediaPermissionRequestManagerProxy::processUserMediaPermissionValidRequest):
(WebKit::UserMediaPermissionRequestManagerProxy::viewIsBecomingVisible):
* UIProcess/UserMediaPermissionRequestManagerProxy.h:
(WebKit::UserMediaPermissionRequestManagerProxy::denyRequest):
* UIProcess/UserMediaPermissionRequestProxy.cpp:
(WebKit::setDeviceAsFirst):
(WebKit::UserMediaPermissionRequestProxy::allow):
(WebKit::UserMediaPermissionRequestProxy::deny):
* UIProcess/UserMediaPermissionRequestProxy.h:

Tools:

* TestWebKitAPI/Tests/WebKit/GetUserMediaReprompt.mm:
(-[GetUserMediaRepromptUIDelegate _webView:requestMediaCaptureAuthorization:decisionHandler:]):
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKit/getUserMedia.html:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKitChangeLog">trunk/Source/WebKit/ChangeLog</a></li>
<li><a href="#trunkSourceWebKitUIProcessUserMediaPermissionRequestManagerProxycpp">trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.cpp</a></li>
<li><a href="#trunkSourceWebKitUIProcessUserMediaPermissionRequestManagerProxyh">trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.h</a></li>
<li><a href="#trunkSourceWebKitUIProcessUserMediaPermissionRequestProxycpp">trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestProxy.cpp</a></li>
<li><a href="#trunkSourceWebKitUIProcessUserMediaPermissionRequestProxyh">trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestProxy.h</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKitGetUserMediaRepromptmm">trunk/Tools/TestWebKitAPI/Tests/WebKit/GetUserMediaReprompt.mm</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKitgetUserMediahtml">trunk/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKitChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/ChangeLog (246092 => 246093)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/ChangeLog    2019-06-05 00:40:35 UTC (rev 246092)
+++ trunk/Source/WebKit/ChangeLog       2019-06-05 01:03:04 UTC (rev 246093)
</span><span class="lines">@@ -1,3 +1,53 @@
</span><ins>+2019-06-04  Youenn Fablet  <youenn@apple.com>
+
+        getUserMedia requests should be processed sequentially in UIProcess
+        https://bugs.webkit.org/show_bug.cgi?id=198430
+        <rdar://problem/51311420>
+
+        Reviewed by Eric Carlson.
+
+        Before the patch, we process all incoming gum/gdm requests in parallel.
+        We now queueu them and process them one at a time.
+        This allows to take into consideration state changes triggered by one request for the next one.
+        In particular, if a user grants a request, this might grant the next one as well.
+
+        To implement that, we keep a reference of the current request to process.
+        We queue other requests happening whenever another request comes.
+        When the request is processed, we look at the next one in the queue.
+        To ensure we do not stop processing the queue for no good reason, some refactoring is done:
+        - queue processing happens when sending back IPC response to WebProcess.
+        - denyRequest/grantRequest are consistently called in the manager proxy.
+
+        * UIProcess/UserMediaPermissionRequestManagerProxy.cpp:
+        (WebKit::UserMediaPermissionRequestManagerProxy::invalidatePendingRequests):
+        Invalidate pregranted requests as well.
+        (WebKit::UserMediaPermissionRequestManagerProxy::denyRequest):
+        Renamed from userMediaAccessWasDenied to denyRequest.
+        This method is now consistently used whenever the manager proxy wants to deny the request.
+        It does the IPC to the WebProcess and triggers processing of the next request.
+        (WebKit::UserMediaPermissionRequestManagerProxy::grantRequest):
+        Renamed from userMediaAccessWasGranted to grantRequest.
+        (WebKit::UserMediaPermissionRequestManagerProxy::finishGrantingRequest):
+        This method is now consistently used whenever the manager proxy wants to deny the request.
+        It does the IPC to the WebProcess and triggers processing of the next request.
+        (WebKit::UserMediaPermissionRequestManagerProxy::rejectionTimerFired):
+        We now keep a queue of request instead of request IDs to make the deny code path more consistent.
+        (WebKit::UserMediaPermissionRequestManagerProxy::requestUserMediaPermissionForFrame):
+        (WebKit::UserMediaPermissionRequestManagerProxy::processNextUserMediaRequestIfNeeded):
+        (WebKit::UserMediaPermissionRequestManagerProxy::startProcessingUserMediaPermissionRequest):
+        (WebKit::UserMediaPermissionRequestManagerProxy::processUserMediaPermissionRequest):
+        To make sure we do not process a different request, we keep a pointer to the request and compare it with the current media request.
+        (WebKit::UserMediaPermissionRequestManagerProxy::processUserMediaPermissionInvalidRequest):
+        (WebKit::UserMediaPermissionRequestManagerProxy::processUserMediaPermissionValidRequest):
+        (WebKit::UserMediaPermissionRequestManagerProxy::viewIsBecomingVisible):
+        * UIProcess/UserMediaPermissionRequestManagerProxy.h:
+        (WebKit::UserMediaPermissionRequestManagerProxy::denyRequest):
+        * UIProcess/UserMediaPermissionRequestProxy.cpp:
+        (WebKit::setDeviceAsFirst):
+        (WebKit::UserMediaPermissionRequestProxy::allow):
+        (WebKit::UserMediaPermissionRequestProxy::deny):
+        * UIProcess/UserMediaPermissionRequestProxy.h:
+
</ins><span class="cx"> 2019-06-04  Commit Queue  <commit-queue@webkit.org>
</span><span class="cx"> 
</span><span class="cx">         Unreviewed, rolling out r246086.
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessUserMediaPermissionRequestManagerProxycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.cpp (246092 => 246093)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.cpp 2019-06-05 00:40:35 UTC (rev 246092)
+++ trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.cpp    2019-06-05 01:03:04 UTC (rev 246093)
</span><span class="lines">@@ -91,10 +91,19 @@
</span><span class="cx"> 
</span><span class="cx"> void UserMediaPermissionRequestManagerProxy::invalidatePendingRequests()
</span><span class="cx"> {
</span><del>-    for (auto& request : m_pendingUserMediaRequests.values())
</del><ins>+    if (m_currentUserMediaRequest) {
+        m_currentUserMediaRequest->invalidate();
+        m_currentUserMediaRequest = nullptr;
+    }
+
+    auto pendingUserMediaRequests = WTFMove(m_pendingUserMediaRequests);
+    for (auto& request : pendingUserMediaRequests)
</ins><span class="cx">         request->invalidate();
</span><del>-    m_pendingUserMediaRequests.clear();
</del><span class="cx"> 
</span><ins>+    auto pregrantedRequests = WTFMove(m_pregrantedRequests);
+    for (auto& request : pregrantedRequests)
+        request->invalidate();
+
</ins><span class="cx">     m_pendingDeviceRequests.clear();
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -158,74 +167,72 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-void UserMediaPermissionRequestManagerProxy::userMediaAccessWasDenied(uint64_t userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason reason)
</del><ins>+void UserMediaPermissionRequestManagerProxy::denyRequest(UserMediaPermissionRequestProxy& request, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason reason, const String& invalidConstraint)
</ins><span class="cx"> {
</span><span class="cx">     if (!m_page.hasRunningProcess())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    ALWAYS_LOG(LOGIDENTIFIER, userMediaID, ", reason: ", reason);
</del><ins>+    ALWAYS_LOG(LOGIDENTIFIER, request.userMediaID(), ", reason: ", reason);
</ins><span class="cx"> 
</span><del>-    auto request = m_pendingUserMediaRequests.take(userMediaID);
-    if (!request)
-        return;
-
</del><span class="cx">     if (reason == UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied)
</span><del>-        m_deniedRequests.append(DeniedRequest { request->mainFrameID(), request->userMediaDocumentSecurityOrigin(), request->topLevelDocumentSecurityOrigin(), request->requiresAudioCapture(), request->requiresVideoCapture(), request->requiresDisplayCapture() });
</del><ins>+        m_deniedRequests.append(DeniedRequest { request.mainFrameID(), request.userMediaDocumentSecurityOrigin(), request.topLevelDocumentSecurityOrigin(), request.requiresAudioCapture(), request.requiresVideoCapture(), request.requiresDisplayCapture() });
</ins><span class="cx"> 
</span><del>-    denyRequest(userMediaID, reason, emptyString());
-}
-
-void UserMediaPermissionRequestManagerProxy::denyRequest(uint64_t userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason reason, const String& invalidConstraint)
-{
-    ASSERT(m_page.hasRunningProcess());
-
-    ALWAYS_LOG(LOGIDENTIFIER, userMediaID, ", reason: ", reason);
-
</del><span class="cx"> #if ENABLE(MEDIA_STREAM)
</span><del>-    m_page.process().send(Messages::WebPage::UserMediaAccessWasDenied(userMediaID, toWebCore(reason), invalidConstraint), m_page.pageID());
</del><ins>+    m_page.process().send(Messages::WebPage::UserMediaAccessWasDenied(request.userMediaID(), toWebCore(reason), invalidConstraint), m_page.pageID());
</ins><span class="cx"> #else
</span><span class="cx">     UNUSED_PARAM(reason);
</span><span class="cx">     UNUSED_PARAM(invalidConstraint);
</span><span class="cx"> #endif
</span><ins>+
+    processNextUserMediaRequestIfNeeded();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void UserMediaPermissionRequestManagerProxy::userMediaAccessWasGranted(uint64_t userMediaID, CaptureDevice&& audioDevice, CaptureDevice&& videoDevice)
</del><ins>+void UserMediaPermissionRequestManagerProxy::grantRequest(UserMediaPermissionRequestProxy& request)
</ins><span class="cx"> {
</span><del>-    ASSERT(audioDevice || videoDevice);
-
</del><span class="cx">     if (!m_page.hasRunningProcess())
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(MEDIA_STREAM)
</span><del>-    auto logSiteIdentifier = LOGIDENTIFIER;
-    ALWAYS_LOG(logSiteIdentifier, userMediaID, ", video: ", videoDevice ? videoDevice.label() : "", ", audio: ", audioDevice ? audioDevice.label() : " ");
</del><ins>+    ALWAYS_LOG(LOGIDENTIFIER, request.userMediaID(), ", video: ", request.videoDevice().label(), ", audio: ", request.audioDevice().label());
</ins><span class="cx"> 
</span><del>-    auto request = m_pendingUserMediaRequests.take(userMediaID);
-    if (!request)
-        return;
-
-    auto& userMediaDocumentSecurityOrigin = request->userMediaDocumentSecurityOrigin();
-    auto& topLevelDocumentSecurityOrigin = request->topLevelDocumentSecurityOrigin();
-    m_page.websiteDataStore().deviceIdHashSaltStorage().deviceIdHashSaltForOrigin(userMediaDocumentSecurityOrigin, topLevelDocumentSecurityOrigin, [this, weakThis = makeWeakPtr(*this), request = request.releaseNonNull(), logSiteIdentifier] (String&& deviceIDHashSalt) mutable {
</del><ins>+    auto& userMediaDocumentSecurityOrigin = request.userMediaDocumentSecurityOrigin();
+    auto& topLevelDocumentSecurityOrigin = request.topLevelDocumentSecurityOrigin();
+    m_page.websiteDataStore().deviceIdHashSaltStorage().deviceIdHashSaltForOrigin(userMediaDocumentSecurityOrigin, topLevelDocumentSecurityOrigin, [this, weakThis = makeWeakPtr(*this), request = makeRef(request)](String&&) mutable {
</ins><span class="cx">         if (!weakThis)
</span><span class="cx">             return;
</span><del>-        if (!grantAccess(request))
-            return;
-
-        ALWAYS_LOG(logSiteIdentifier, deviceIDHashSalt);
-        m_grantedRequests.append(WTFMove(request));
-        if (m_hasFilteredDeviceList)
-            captureDevicesChanged();
-        m_hasFilteredDeviceList = false;
</del><ins>+        finishGrantingRequest(request);
</ins><span class="cx">     });
</span><span class="cx"> #else
</span><del>-    UNUSED_PARAM(userMediaID);
-    UNUSED_PARAM(audioDevice);
-    UNUSED_PARAM(videoDevice);
</del><ins>+    UNUSED_PARAM(request);
</ins><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(MEDIA_STREAM)
</span><ins>+void UserMediaPermissionRequestManagerProxy::finishGrantingRequest(UserMediaPermissionRequestProxy& request)
+{
+    ALWAYS_LOG(LOGIDENTIFIER, request.userMediaID());
+    if (!UserMediaProcessManager::singleton().willCreateMediaStream(*this, request.hasAudioDevice(), request.hasVideoDevice())) {
+        denyRequest(request, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::OtherFailure, "Unable to extend sandbox.");
+        return;
+    }
+
+    if (request.requestType() == MediaStreamRequest::Type::UserMedia)
+        m_grantedRequests.append(makeRef(request));
+
+    if (m_hasFilteredDeviceList)
+        captureDevicesChanged();
+    m_hasFilteredDeviceList = false;
+
+    ++m_hasPendingCapture;
+    m_page.process().connection()->sendWithAsyncReply(Messages::WebPage::UserMediaAccessWasGranted { request.userMediaID(), request.audioDevice(), request.videoDevice(), request.deviceIdentifierHashSalt() }, [this, weakThis = makeWeakPtr(this)] {
+        if (!weakThis)
+            return;
+        --m_hasPendingCapture;
+    }, m_page.pageID());
+
+    processNextUserMediaRequestIfNeeded();
+}
+
</ins><span class="cx"> void UserMediaPermissionRequestManagerProxy::resetAccess(uint64_t frameID)
</span><span class="cx"> {
</span><span class="cx">     ALWAYS_LOG(LOGIDENTIFIER, frameID);
</span><span class="lines">@@ -287,30 +294,11 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool UserMediaPermissionRequestManagerProxy::grantAccess(const UserMediaPermissionRequestProxy& request)
-{
-    ALWAYS_LOG(LOGIDENTIFIER, request.userMediaID());
-    if (!UserMediaProcessManager::singleton().willCreateMediaStream(*this, request.hasAudioDevice(), request.hasVideoDevice())) {
-        denyRequest(request.userMediaID(), UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::OtherFailure, "Unable to extend sandbox.");
-        return false;
-    }
-
-    ++m_hasPendingCapture;
-    m_page.process().connection()->sendWithAsyncReply(Messages::WebPage::UserMediaAccessWasGranted { request.userMediaID(), request.audioDevice(), request.videoDevice(), request.deviceIdentifierHashSalt() }, [this, weakThis = makeWeakPtr(this)] {
-        if (!weakThis)
-            return;
-        --m_hasPendingCapture;
-    }, m_page.pageID());
-    return true;
-}
</del><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> void UserMediaPermissionRequestManagerProxy::rejectionTimerFired()
</span><span class="cx"> {
</span><del>-    uint64_t userMediaID = m_pendingRejections[0];
-    m_pendingRejections.remove(0);
-
-    denyRequest(userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied, emptyString());
</del><ins>+    denyRequest(m_pendingRejections.takeFirst(), UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied, emptyString());
</ins><span class="cx">     if (!m_pendingRejections.isEmpty())
</span><span class="cx">         scheduleNextRejection();
</span><span class="cx"> }
</span><span class="lines">@@ -347,50 +335,80 @@
</span><span class="cx"> #if ENABLE(MEDIA_STREAM)
</span><span class="cx">     auto logSiteIdentifier = LOGIDENTIFIER;
</span><span class="cx"> 
</span><ins>+    if (!m_page.hasRunningProcess())
+        return;
+
+    ALWAYS_LOG(logSiteIdentifier, userMediaID);
+
+    auto request = UserMediaPermissionRequestProxy::create(*this, userMediaID, m_page.mainFrame()->frameID(), frameID, WTFMove(userMediaDocumentOrigin), WTFMove(topLevelDocumentOrigin), { }, { }, WTFMove(userRequest));
+    if (m_currentUserMediaRequest) {
+        m_pendingUserMediaRequests.append(WTFMove(request));
+        return;
+    }
+
</ins><span class="cx">     if (!UserMediaProcessManager::singleton().captureEnabled()) {
</span><span class="cx">         ALWAYS_LOG(logSiteIdentifier, "capture disabled");
</span><del>-        m_pendingRejections.append(userMediaID);
</del><ins>+        m_pendingRejections.append(WTFMove(request));
</ins><span class="cx">         scheduleNextRejection();
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (!m_page.hasRunningProcess())
</del><ins>+    startProcessingUserMediaPermissionRequest(WTFMove(request));
+#else
+    UNUSED_PARAM(userMediaID);
+    UNUSED_PARAM(frameID);
+    UNUSED_PARAM(userMediaDocumentOrigin);
+    UNUSED_PARAM(topLevelDocumentOrigin);
+    UNUSED_PARAM(userRequest);
+#endif
+}
+
+void UserMediaPermissionRequestManagerProxy::processNextUserMediaRequestIfNeeded()
+{
+#if ENABLE(MEDIA_STREAM)
+    if (m_pendingUserMediaRequests.isEmpty()) {
+        m_currentUserMediaRequest = nullptr;
</ins><span class="cx">         return;
</span><ins>+    }
+    startProcessingUserMediaPermissionRequest(m_pendingUserMediaRequests.takeFirst());
+#endif
+}
</ins><span class="cx"> 
</span><del>-    ALWAYS_LOG(logSiteIdentifier, userMediaID);
</del><ins>+#if ENABLE(MEDIA_STREAM)
+void UserMediaPermissionRequestManagerProxy::startProcessingUserMediaPermissionRequest(Ref<UserMediaPermissionRequestProxy>&& request)
+{
+    m_currentUserMediaRequest = WTFMove(request);
</ins><span class="cx"> 
</span><del>-    auto request = m_pendingUserMediaRequests.add(userMediaID, UserMediaPermissionRequestProxy::create(*this, userMediaID, m_page.mainFrame()->frameID(), frameID, WTFMove(userMediaDocumentOrigin), WTFMove(topLevelDocumentOrigin), { }, { }, WTFMove(userRequest))).iterator->value.copyRef();
-
-    auto& userMediaOrigin = request->userMediaDocumentSecurityOrigin();
-    auto& topLevelOrigin = request->topLevelDocumentSecurityOrigin();
-    getUserMediaPermissionInfo(frameID, userMediaOrigin, topLevelOrigin, [this, request = request.releaseNonNull(), logSiteIdentifier](Optional<bool> hasPersistentAccess) mutable {
</del><ins>+    auto& userMediaDocumentSecurityOrigin = m_currentUserMediaRequest->userMediaDocumentSecurityOrigin();
+    auto& topLevelDocumentSecurityOrigin = m_currentUserMediaRequest->topLevelDocumentSecurityOrigin();
+    getUserMediaPermissionInfo(m_currentUserMediaRequest->frameID(), userMediaDocumentSecurityOrigin, topLevelDocumentSecurityOrigin, [this, request = m_currentUserMediaRequest](Optional<bool> hasPersistentAccess) mutable {
</ins><span class="cx">         if (!request->isPending())
</span><span class="cx">             return;
</span><span class="cx"> 
</span><span class="cx">         if (!hasPersistentAccess) {
</span><del>-            request->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::OtherFailure);
</del><ins>+            denyRequest(*m_currentUserMediaRequest, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::OtherFailure);
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        ALWAYS_LOG(logSiteIdentifier, request->userMediaID(), ", persistent access: ", *hasPersistentAccess);
-        processUserMediaPermissionRequest(WTFMove(request), *hasPersistentAccess);
</del><ins>+        ALWAYS_LOG(LOGIDENTIFIER, m_currentUserMediaRequest->userMediaID(), ", persistent access: ", *hasPersistentAccess);
+        processUserMediaPermissionRequest(*hasPersistentAccess);
</ins><span class="cx">     });
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void UserMediaPermissionRequestManagerProxy::processUserMediaPermissionRequest(Ref<UserMediaPermissionRequestProxy>&& request, bool hasPersistentAccess)
</del><ins>+void UserMediaPermissionRequestManagerProxy::processUserMediaPermissionRequest(bool hasPersistentAccess)
</ins><span class="cx"> {
</span><del>-    ALWAYS_LOG(LOGIDENTIFIER, request->userMediaID());
</del><ins>+    ALWAYS_LOG(LOGIDENTIFIER, m_currentUserMediaRequest->userMediaID());
</ins><span class="cx"> 
</span><span class="cx">     if (hasPersistentAccess)
</span><del>-        request->setHasPersistentAccess();
</del><ins>+        m_currentUserMediaRequest->setHasPersistentAccess();
</ins><span class="cx"> 
</span><del>-    auto& userMediaDocumentSecurityOrigin = request->userMediaDocumentSecurityOrigin();
-    auto& topLevelDocumentSecurityOrigin = request->topLevelDocumentSecurityOrigin();
-    m_page.websiteDataStore().deviceIdHashSaltStorage().deviceIdHashSaltForOrigin(userMediaDocumentSecurityOrigin, topLevelDocumentSecurityOrigin, [this, request = WTFMove(request)] (String&& deviceIDHashSalt) mutable {
</del><ins>+    auto& userMediaDocumentSecurityOrigin = m_currentUserMediaRequest->userMediaDocumentSecurityOrigin();
+    auto& topLevelDocumentSecurityOrigin = m_currentUserMediaRequest->topLevelDocumentSecurityOrigin();
+    m_page.websiteDataStore().deviceIdHashSaltStorage().deviceIdHashSaltForOrigin(userMediaDocumentSecurityOrigin, topLevelDocumentSecurityOrigin, [this, request = m_currentUserMediaRequest] (String&& deviceIDHashSalt) mutable {
</ins><span class="cx">         if (!request->isPending())
</span><span class="cx">             return;
</span><span class="cx"> 
</span><del>-        RealtimeMediaSourceCenter::InvalidConstraintsHandler invalidHandler = [this, request = request.copyRef()](const String& invalidConstraint) {
</del><ins>+        RealtimeMediaSourceCenter::InvalidConstraintsHandler invalidHandler = [this, request](const String& invalidConstraint) {
</ins><span class="cx">             if (!request->isPending())
</span><span class="cx">                 return;
</span><span class="cx"> 
</span><span class="lines">@@ -397,10 +415,10 @@
</span><span class="cx">             if (!m_page.hasRunningProcess())
</span><span class="cx">                 return;
</span><span class="cx"> 
</span><del>-            processUserMediaPermissionInvalidRequest(request.get(), invalidConstraint);
</del><ins>+            processUserMediaPermissionInvalidRequest(invalidConstraint);
</ins><span class="cx">         };
</span><span class="cx"> 
</span><del>-        auto validHandler = [this, request = request.copyRef()](Vector<CaptureDevice>&& audioDevices, Vector<CaptureDevice>&& videoDevices, String&& deviceIdentifierHashSalt) mutable {
</del><ins>+        auto validHandler = [this, request](Vector<CaptureDevice>&& audioDevices, Vector<CaptureDevice>&& videoDevices, String&& deviceIdentifierHashSalt) mutable {
</ins><span class="cx">             if (!request->isPending())
</span><span class="cx">                 return;
</span><span class="cx"> 
</span><span class="lines">@@ -407,58 +425,52 @@
</span><span class="cx">             if (!m_page.hasRunningProcess() || !m_page.mainFrame())
</span><span class="cx">                 return;
</span><span class="cx"> 
</span><del>-            processUserMediaPermissionValidRequest(WTFMove(request), WTFMove(audioDevices), WTFMove(videoDevices), WTFMove(deviceIdentifierHashSalt));
</del><ins>+            processUserMediaPermissionValidRequest(WTFMove(audioDevices), WTFMove(videoDevices), WTFMove(deviceIdentifierHashSalt));
</ins><span class="cx">         };
</span><span class="cx"> 
</span><span class="cx">         syncWithWebCorePrefs();
</span><span class="cx"> 
</span><del>-        RealtimeMediaSourceCenter::singleton().validateRequestConstraints(WTFMove(validHandler), WTFMove(invalidHandler), request->userRequest(), WTFMove(deviceIDHashSalt));
</del><ins>+        RealtimeMediaSourceCenter::singleton().validateRequestConstraints(WTFMove(validHandler), WTFMove(invalidHandler), m_currentUserMediaRequest->userRequest(), WTFMove(deviceIDHashSalt));
</ins><span class="cx">     });
</span><del>-#else
-    UNUSED_PARAM(userMediaID);
-    UNUSED_PARAM(frameID);
-    UNUSED_PARAM(userMediaDocumentOrigin);
-    UNUSED_PARAM(topLevelDocumentOrigin);
-    UNUSED_PARAM(userRequest);
</del><ins>+}
</ins><span class="cx"> #endif
</span><del>-}
</del><span class="cx"> 
</span><span class="cx"> #if ENABLE(MEDIA_STREAM)
</span><del>-void UserMediaPermissionRequestManagerProxy::processUserMediaPermissionInvalidRequest(const UserMediaPermissionRequestProxy& request, const String& invalidConstraint)
</del><ins>+void UserMediaPermissionRequestManagerProxy::processUserMediaPermissionInvalidRequest(const String& invalidConstraint)
</ins><span class="cx"> {
</span><del>-    ALWAYS_LOG(LOGIDENTIFIER, request.userMediaID());
-    bool filterConstraint = !request.hasPersistentAccess() && !wasGrantedVideoOrAudioAccess(request.frameID(), request.userMediaDocumentSecurityOrigin(), request.topLevelDocumentSecurityOrigin());
</del><ins>+    ALWAYS_LOG(LOGIDENTIFIER, m_currentUserMediaRequest->userMediaID());
+    bool filterConstraint = !m_currentUserMediaRequest->hasPersistentAccess() && !wasGrantedVideoOrAudioAccess(m_currentUserMediaRequest->frameID(), m_currentUserMediaRequest->userMediaDocumentSecurityOrigin(), m_currentUserMediaRequest->topLevelDocumentSecurityOrigin());
</ins><span class="cx"> 
</span><del>-    denyRequest(request.userMediaID(), UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::InvalidConstraint, filterConstraint ? String { } : invalidConstraint);
</del><ins>+    denyRequest(*m_currentUserMediaRequest, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::InvalidConstraint, filterConstraint ? String { } : invalidConstraint);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void UserMediaPermissionRequestManagerProxy::processUserMediaPermissionValidRequest(Ref<UserMediaPermissionRequestProxy>&& request, Vector<CaptureDevice>&& audioDevices, Vector<CaptureDevice>&& videoDevices, String&& deviceIdentifierHashSalt)
</del><ins>+void UserMediaPermissionRequestManagerProxy::processUserMediaPermissionValidRequest(Vector<CaptureDevice>&& audioDevices, Vector<CaptureDevice>&& videoDevices, String&& deviceIdentifierHashSalt)
</ins><span class="cx"> {
</span><del>-    ALWAYS_LOG(LOGIDENTIFIER, request->userMediaID(), ", video: ", videoDevices.size(), " audio: ", audioDevices.size());
</del><ins>+    ALWAYS_LOG(LOGIDENTIFIER, m_currentUserMediaRequest->userMediaID(), ", video: ", videoDevices.size(), " audio: ", audioDevices.size());
</ins><span class="cx">     if (videoDevices.isEmpty() && audioDevices.isEmpty()) {
</span><del>-        denyRequest(request->userMediaID(), UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoConstraints, emptyString());
</del><ins>+        denyRequest(*m_currentUserMediaRequest, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoConstraints, emptyString());
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    request->setDeviceIdentifierHashSalt(WTFMove(deviceIdentifierHashSalt));
-    request->setEligibleVideoDeviceUIDs(WTFMove(videoDevices));
-    request->setEligibleAudioDeviceUIDs(WTFMove(audioDevices));
</del><ins>+    m_currentUserMediaRequest->setDeviceIdentifierHashSalt(WTFMove(deviceIdentifierHashSalt));
+    m_currentUserMediaRequest->setEligibleVideoDeviceUIDs(WTFMove(videoDevices));
+    m_currentUserMediaRequest->setEligibleAudioDeviceUIDs(WTFMove(audioDevices));
</ins><span class="cx"> 
</span><del>-    auto action = getRequestAction(request);
-    ALWAYS_LOG(LOGIDENTIFIER, request->userMediaID(), ", action: ", action);
</del><ins>+    auto action = getRequestAction(*m_currentUserMediaRequest);
+    ALWAYS_LOG(LOGIDENTIFIER, m_currentUserMediaRequest->userMediaID(), ", action: ", action);
</ins><span class="cx"> 
</span><span class="cx">     if (action == RequestAction::Deny) {
</span><del>-        denyRequest(request->userMediaID(), UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied, emptyString());
</del><ins>+        denyRequest(*m_currentUserMediaRequest, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied, emptyString());
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (action == RequestAction::Grant) {
</span><del>-        ASSERT(request->requestType() != MediaStreamRequest::Type::DisplayMedia);
</del><ins>+        ASSERT(m_currentUserMediaRequest->requestType() != MediaStreamRequest::Type::DisplayMedia);
</ins><span class="cx"> 
</span><span class="cx">         if (m_page.isViewVisible())
</span><del>-            grantAccess(request);
</del><ins>+            grantRequest(*m_currentUserMediaRequest);
</ins><span class="cx">         else
</span><del>-            m_pregrantedRequests.append(WTFMove(request));
</del><ins>+            m_pregrantedRequests.append(m_currentUserMediaRequest.releaseNonNull());
</ins><span class="cx"> 
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="lines">@@ -465,34 +477,33 @@
</span><span class="cx"> 
</span><span class="cx">     if (m_page.isControlledByAutomation()) {
</span><span class="cx">         if (WebAutomationSession* automationSession = m_page.process().processPool().automationSession()) {
</span><del>-            ALWAYS_LOG(LOGIDENTIFIER, request->userMediaID(), ", page controlled by automation");
</del><ins>+            ALWAYS_LOG(LOGIDENTIFIER, m_currentUserMediaRequest->userMediaID(), ", page controlled by automation");
</ins><span class="cx">             if (automationSession->shouldAllowGetUserMediaForPage(m_page))
</span><del>-                request->allow();
</del><ins>+                grantRequest(*m_currentUserMediaRequest);
</ins><span class="cx">             else
</span><del>-                userMediaAccessWasDenied(request->userMediaID(), UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
-
</del><ins>+                denyRequest(*m_currentUserMediaRequest, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (m_page.preferences().mockCaptureDevicesEnabled() && !m_page.preferences().mockCaptureDevicesPromptEnabled()) {
</span><del>-        ALWAYS_LOG(LOGIDENTIFIER, request->userMediaID(), ", mock devices don't require prompt");
-        request->allow();
</del><ins>+        ALWAYS_LOG(LOGIDENTIFIER, m_currentUserMediaRequest->userMediaID(), ", mock devices don't require prompt");
+        grantRequest(*m_currentUserMediaRequest);
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // If page navigated, there is no need to call the page client for authorization.
</span><del>-    auto* webFrame = m_page.process().webFrame(request->frameID());
</del><ins>+    auto* webFrame = m_page.process().webFrame(m_currentUserMediaRequest->frameID());
</ins><span class="cx"> 
</span><del>-    if (!webFrame || !SecurityOrigin::createFromString(m_page.pageLoadState().activeURL())->isSameSchemeHostPort(request->topLevelDocumentSecurityOrigin())) {
-        denyRequest(request->userMediaID(), UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoConstraints, emptyString());
</del><ins>+    if (!webFrame || !SecurityOrigin::createFromString(m_page.pageLoadState().activeURL())->isSameSchemeHostPort(m_currentUserMediaRequest->topLevelDocumentSecurityOrigin())) {
+        denyRequest(*m_currentUserMediaRequest, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoConstraints, emptyString());
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // FIXME: Remove webFrame, userMediaOrigin and topLevelOrigin from this uiClient API call.
</span><del>-    auto userMediaOrigin = API::SecurityOrigin::create(request->userMediaDocumentSecurityOrigin());
-    auto topLevelOrigin = API::SecurityOrigin::create(request->topLevelDocumentSecurityOrigin());
-    m_page.uiClient().decidePolicyForUserMediaPermissionRequest(m_page, *webFrame, WTFMove(userMediaOrigin), WTFMove(topLevelOrigin), request);
</del><ins>+    auto userMediaOrigin = API::SecurityOrigin::create(m_currentUserMediaRequest->userMediaDocumentSecurityOrigin());
+    auto topLevelOrigin = API::SecurityOrigin::create(m_currentUserMediaRequest->topLevelDocumentSecurityOrigin());
+    m_page.uiClient().decidePolicyForUserMediaPermissionRequest(m_page, *webFrame, WTFMove(userMediaOrigin), WTFMove(topLevelOrigin), *m_currentUserMediaRequest);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void UserMediaPermissionRequestManagerProxy::getUserMediaPermissionInfo(uint64_t frameID, Ref<SecurityOrigin>&& userMediaDocumentOrigin, Ref<SecurityOrigin>&& topLevelDocumentOrigin, CompletionHandler<void(Optional<bool>)>&& handler)
</span><span class="lines">@@ -663,9 +674,9 @@
</span><span class="cx"> 
</span><span class="cx"> void UserMediaPermissionRequestManagerProxy::viewIsBecomingVisible()
</span><span class="cx"> {
</span><del>-    for (auto& request : m_pregrantedRequests)
-        request->allow();
-    m_pregrantedRequests.clear();
</del><ins>+    auto pregrantedRequests = WTFMove(m_pregrantedRequests);
+    for (auto& request : pregrantedRequests)
+        grantRequest(request);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void UserMediaPermissionRequestManagerProxy::watchdogTimerFired()
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessUserMediaPermissionRequestManagerProxyh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.h (246092 => 246093)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.h   2019-06-05 00:40:35 UTC (rev 246092)
+++ trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.h      2019-06-05 01:03:04 UTC (rev 246093)
</span><span class="lines">@@ -24,6 +24,7 @@
</span><span class="cx"> #include <WebCore/MediaProducer.h>
</span><span class="cx"> #include <WebCore/SecurityOrigin.h>
</span><span class="cx"> #include <wtf/CompletionHandler.h>
</span><ins>+#include <wtf/Deque.h>
</ins><span class="cx"> #include <wtf/HashMap.h>
</span><span class="cx"> #include <wtf/LoggerHelper.h>
</span><span class="cx"> #include <wtf/RunLoop.h>
</span><span class="lines">@@ -64,8 +65,8 @@
</span><span class="cx">     void resetAccess(uint64_t mainFrameID);
</span><span class="cx">     void viewIsBecomingVisible();
</span><span class="cx"> 
</span><del>-    void userMediaAccessWasGranted(uint64_t, WebCore::CaptureDevice&& audioDevice, WebCore::CaptureDevice&& videoDevice);
-    void userMediaAccessWasDenied(uint64_t, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason);
</del><ins>+    void grantRequest(UserMediaPermissionRequestProxy&);
+    void denyRequest(UserMediaPermissionRequestProxy&, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason, const String& invalidConstraint = { });
</ins><span class="cx"> 
</span><span class="cx">     void enumerateMediaDevicesForFrame(uint64_t userMediaID, uint64_t frameID, Ref<WebCore::SecurityOrigin>&& userMediaDocumentOrigin, Ref<WebCore::SecurityOrigin>&& topLevelDocumentOrigin);
</span><span class="cx"> 
</span><span class="lines">@@ -93,9 +94,8 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     Ref<UserMediaPermissionRequestProxy> createPermissionRequest(uint64_t userMediaID, uint64_t mainFrameID, uint64_t frameID, Ref<WebCore::SecurityOrigin>&& userMediaDocumentOrigin, Ref<WebCore::SecurityOrigin>&& topLevelDocumentOrigin, Vector<WebCore::CaptureDevice>&& audioDevices, Vector<WebCore::CaptureDevice>&& videoDevices, WebCore::MediaStreamRequest&&);
</span><del>-    void denyRequest(uint64_t userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason, const String& invalidConstraint);
</del><span class="cx"> #if ENABLE(MEDIA_STREAM)
</span><del>-    bool grantAccess(const UserMediaPermissionRequestProxy&);
</del><ins>+    void finishGrantingRequest(UserMediaPermissionRequestProxy&);
</ins><span class="cx"> 
</span><span class="cx">     const UserMediaPermissionRequestProxy* searchForGrantedRequest(uint64_t frameID, const WebCore::SecurityOrigin& userMediaDocumentOrigin, const WebCore::SecurityOrigin& topLevelDocumentOrigin, bool needsAudio, bool needsVideo) const;
</span><span class="cx">     bool wasRequestDenied(uint64_t mainFrameID, const WebCore::SecurityOrigin& userMediaDocumentOrigin, const WebCore::SecurityOrigin& topLevelDocumentOrigin, bool needsAudio, bool needsVideo, bool needsScreenCapture);
</span><span class="lines">@@ -108,20 +108,24 @@
</span><span class="cx"> 
</span><span class="cx">     Vector<WebCore::CaptureDevice> computeFilteredDeviceList(bool revealIdsAndLabels, const String& deviceIDHashSalt);
</span><span class="cx"> 
</span><del>-    void processUserMediaPermissionRequest(Ref<UserMediaPermissionRequestProxy>&&, bool hasPersistentAccess);
-    void processUserMediaPermissionInvalidRequest(const UserMediaPermissionRequestProxy&, const String& invalidConstraint);
-    void processUserMediaPermissionValidRequest(Ref<UserMediaPermissionRequestProxy>&&, Vector<WebCore::CaptureDevice>&& audioDevices, Vector<WebCore::CaptureDevice>&& videoDevices, String&& deviceIdentifierHashSalt);
</del><ins>+    void processUserMediaPermissionRequest(bool hasPersistentAccess);
+    void processUserMediaPermissionInvalidRequest(const String& invalidConstraint);
+    void processUserMediaPermissionValidRequest(Vector<WebCore::CaptureDevice>&& audioDevices, Vector<WebCore::CaptureDevice>&& videoDevices, String&& deviceIdentifierHashSalt);
+    void startProcessingUserMediaPermissionRequest(Ref<UserMediaPermissionRequestProxy>&&);
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     void watchdogTimerFired();
</span><span class="cx"> 
</span><del>-    HashMap<uint64_t, RefPtr<UserMediaPermissionRequestProxy>> m_pendingUserMediaRequests;
</del><ins>+    void processNextUserMediaRequestIfNeeded();
+
+    RefPtr<UserMediaPermissionRequestProxy> m_currentUserMediaRequest;
+    Deque<Ref<UserMediaPermissionRequestProxy>> m_pendingUserMediaRequests;
</ins><span class="cx">     HashSet<uint64_t> m_pendingDeviceRequests;
</span><span class="cx"> 
</span><span class="cx">     WebPageProxy& m_page;
</span><span class="cx"> 
</span><span class="cx">     RunLoop::Timer<UserMediaPermissionRequestManagerProxy> m_rejectionTimer;
</span><del>-    Vector<uint64_t> m_pendingRejections;
</del><ins>+    Deque<Ref<UserMediaPermissionRequestProxy>> m_pendingRejections;
</ins><span class="cx"> 
</span><span class="cx">     Vector<Ref<UserMediaPermissionRequestProxy>> m_pregrantedRequests;
</span><span class="cx">     Vector<Ref<UserMediaPermissionRequestProxy>> m_grantedRequests;
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessUserMediaPermissionRequestProxycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestProxy.cpp (246092 => 246093)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestProxy.cpp        2019-06-05 00:40:35 UTC (rev 246092)
+++ trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestProxy.cpp   2019-06-05 01:03:04 UTC (rev 246093)
</span><span class="lines">@@ -43,58 +43,39 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void UserMediaPermissionRequestProxy::allow(const String& audioDeviceUID, const String& videoDeviceUID)
</del><ins>+#if ENABLE(MEDIA_STREAM)
+static inline void setDeviceAsFirst(Vector<CaptureDevice>& devices, const String& deviceID)
</ins><span class="cx"> {
</span><del>-    ASSERT(m_manager);
-    if (!m_manager)
-        return;
</del><ins>+    size_t index = devices.findMatching([&deviceID](const auto& device) {
+        return device.persistentId() == deviceID;
+    });
+    ASSERT(index != notFound);
</ins><span class="cx"> 
</span><del>-#if ENABLE(MEDIA_STREAM)
-    CaptureDevice audioDevice;
-    if (!audioDeviceUID.isEmpty()) {
-        size_t index = m_eligibleAudioDevices.findMatching([&](const auto& device) {
-            return device.persistentId() == audioDeviceUID;
-        });
-        ASSERT(index != notFound);
</del><ins>+    if (index) {
+        auto device = devices[index];
+        ASSERT(device.enabled());
</ins><span class="cx"> 
</span><del>-        if (index != notFound)
-            audioDevice = m_eligibleAudioDevices[index];
-
-        ASSERT(audioDevice.enabled());
</del><ins>+        devices.remove(index);
+        devices.insert(0, WTFMove(device));
</ins><span class="cx">     }
</span><ins>+}
+#endif
</ins><span class="cx"> 
</span><del>-    CaptureDevice videoDevice;
-    if (!videoDeviceUID.isEmpty()) {
-        size_t index = m_eligibleVideoDevices.findMatching([&](const auto& device) {
-            return device.persistentId() == videoDeviceUID;
-        });
-        ASSERT(index != notFound);
-
-        if (index != notFound)
-            videoDevice = m_eligibleVideoDevices[index];
-
-        ASSERT(videoDevice.enabled());
-    }
-
-    m_manager->userMediaAccessWasGranted(m_userMediaID, WTFMove(audioDevice), WTFMove(videoDevice));
</del><ins>+void UserMediaPermissionRequestProxy::allow(const String& audioDeviceUID, const String& videoDeviceUID)
+{
+#if ENABLE(MEDIA_STREAM)
+    if (!audioDeviceUID.isEmpty())
+        setDeviceAsFirst(m_eligibleAudioDevices, audioDeviceUID);
+    if (!videoDeviceUID.isEmpty())
+        setDeviceAsFirst(m_eligibleVideoDevices, videoDeviceUID);
</ins><span class="cx"> #else
</span><span class="cx">     UNUSED_PARAM(audioDeviceUID);
</span><span class="cx">     UNUSED_PARAM(videoDeviceUID);
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-    invalidate();
</del><ins>+    allow();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void UserMediaPermissionRequestProxy::allow(WebCore::CaptureDevice&& audioDevice, WebCore::CaptureDevice&& videoDevice)
-{
-    ASSERT(m_manager);
-    if (!m_manager)
-        return;
-
-    m_manager->userMediaAccessWasGranted(m_userMediaID, WTFMove(audioDevice), WTFMove(videoDevice));
-    invalidate();
-}
-
</del><span class="cx"> void UserMediaPermissionRequestProxy::allow()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_manager);
</span><span class="lines">@@ -101,10 +82,7 @@
</span><span class="cx">     if (!m_manager)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    auto audioDevice = !m_eligibleAudioDevices.isEmpty() ? m_eligibleAudioDevices[0] : CaptureDevice();
-    auto videoDevice = !m_eligibleVideoDevices.isEmpty() ? m_eligibleVideoDevices[0] : CaptureDevice();
-
-    m_manager->userMediaAccessWasGranted(m_userMediaID, WTFMove(audioDevice), WTFMove(videoDevice));
</del><ins>+    m_manager->grantRequest(*this);
</ins><span class="cx">     invalidate();
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -113,7 +91,7 @@
</span><span class="cx">     if (!m_manager)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    m_manager->userMediaAccessWasDenied(m_userMediaID, reason);
</del><ins>+    m_manager->denyRequest(*this, reason);
</ins><span class="cx">     invalidate();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessUserMediaPermissionRequestProxyh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestProxy.h (246092 => 246093)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestProxy.h  2019-06-05 00:40:35 UTC (rev 246092)
+++ trunk/Source/WebKit/UIProcess/UserMediaPermissionRequestProxy.h     2019-06-05 01:03:04 UTC (rev 246093)
</span><span class="lines">@@ -41,7 +41,6 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void allow(const String& audioDeviceUID, const String& videoDeviceUID);
</span><del>-    void allow(WebCore::CaptureDevice&& audioDevice, WebCore::CaptureDevice&& videoDevice);
</del><span class="cx">     void allow();
</span><span class="cx"> 
</span><span class="cx">     enum class UserMediaAccessDenialReason { NoConstraints, UserMediaDisabled, NoCaptureDevices, InvalidConstraint, HardwareError, PermissionDenied, OtherFailure };
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (246092 => 246093)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog    2019-06-05 00:40:35 UTC (rev 246092)
+++ trunk/Tools/ChangeLog       2019-06-05 01:03:04 UTC (rev 246093)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2019-06-04  Youenn Fablet  <youenn@apple.com>
+
+        getUserMedia requests should be processed sequentially in UIProcess
+        https://bugs.webkit.org/show_bug.cgi?id=198430
+        <rdar://problem/51311420>
+
+        Reviewed by Eric Carlson.
+
+        * TestWebKitAPI/Tests/WebKit/GetUserMediaReprompt.mm:
+        (-[GetUserMediaRepromptUIDelegate _webView:requestMediaCaptureAuthorization:decisionHandler:]):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebKit/getUserMedia.html:
+
</ins><span class="cx"> 2019-06-04  David Kilzer  <ddkilzer@apple.com>
</span><span class="cx"> 
</span><span class="cx">         REGRESSION (r244557): Leak of WKNSString in WTR::runOpenPanel() while running WebKit layout tests
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKitGetUserMediaRepromptmm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit/GetUserMediaReprompt.mm (246092 => 246093)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit/GetUserMediaReprompt.mm   2019-06-05 00:40:35 UTC (rev 246092)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit/GetUserMediaReprompt.mm      2019-06-05 01:03:04 UTC (rev 246093)
</span><span class="lines">@@ -37,6 +37,7 @@
</span><span class="cx"> #import <WebKit/_WKProcessPoolConfiguration.h>
</span><span class="cx"> 
</span><span class="cx"> static bool wasPrompted = false;
</span><ins>+static int numberOfPrompts = 0;
</ins><span class="cx"> 
</span><span class="cx"> @interface GetUserMediaRepromptUIDelegate : NSObject<WKUIDelegate>
</span><span class="cx"> - (void)_webView:(WKWebView *)webView requestMediaCaptureAuthorization: (_WKCaptureDevices)devices decisionHandler:(void (^)(BOOL))decisionHandler;
</span><span class="lines">@@ -46,6 +47,7 @@
</span><span class="cx"> @implementation GetUserMediaRepromptUIDelegate
</span><span class="cx"> - (void)_webView:(WKWebView *)webView requestMediaCaptureAuthorization: (_WKCaptureDevices)devices decisionHandler:(void (^)(BOOL))decisionHandler
</span><span class="cx"> {
</span><ins>+    numberOfPrompts++;
</ins><span class="cx">     wasPrompted = true;
</span><span class="cx">     decisionHandler(YES);
</span><span class="cx"> }
</span><span class="lines">@@ -117,6 +119,31 @@
</span><span class="cx">     EXPECT_TRUE([webView haveStream:YES]);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+TEST(WebKit2, MultipleGetUserMediaSynchronously)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto processPoolConfig = adoptNS([[_WKProcessPoolConfiguration alloc] init]);
+    auto preferences = [configuration preferences];
+    preferences._mediaCaptureRequiresSecureConnection = NO;
+    preferences._mediaDevicesEnabled = YES;
+    preferences._mockCaptureDevicesEnabled = YES;
+    auto webView = [[GetUserMediaRepromptTestView alloc] initWithFrame:CGRectMake(0, 0, 320, 500) configuration:configuration.get() processPoolConfiguration:processPoolConfig.get()];
+    auto delegate = adoptNS([[GetUserMediaRepromptUIDelegate alloc] init]);
+    webView.UIDelegate = delegate.get();
+
+    wasPrompted = false;
+    numberOfPrompts = 0;
+    [webView loadTestPageNamed:@"getUserMedia"];
+    TestWebKitAPI::Util::run(&wasPrompted);
+    EXPECT_EQ(numberOfPrompts, 1);
+
+    wasPrompted = false;
+    numberOfPrompts = 0;
+    [webView stringByEvaluatingJavaScript:@"doMultipleGetUserMediaSynchronously()"];
+    TestWebKitAPI::Util::run(&wasPrompted);
+    EXPECT_EQ(numberOfPrompts, 1);
+}
+
</ins><span class="cx"> } // namespace TestWebKitAPI
</span><span class="cx"> 
</span><span class="cx"> #endif // ENABLE(MEDIA_STREAM)
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKitgetUserMediahtml"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html (246092 => 246093)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html 2019-06-05 00:40:35 UTC (rev 246092)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit/getUserMedia.html    2019-06-05 01:03:04 UTC (rev 246093)
</span><span class="lines">@@ -36,6 +36,20 @@
</span><span class="cx">             {
</span><span class="cx">                 return stream !== null;
</span><span class="cx">             }
</span><ins>+
+            function doMultipleGetUserMediaSynchronously()
+            {
+                navigator.mediaDevices.getUserMedia({video: true});
+                navigator.mediaDevices.getUserMedia({video: true});
+                navigator.mediaDevices.getUserMedia({video: true});
+
+                // This one should prompt.
+                navigator.mediaDevices.getUserMedia({audio: true});
+
+                navigator.mediaDevices.getUserMedia({audio: true});
+                navigator.mediaDevices.getUserMedia({audio: true});
+                navigator.mediaDevices.getUserMedia({audio: true, video: true});
+            }
</ins><span class="cx">         </script>
</span><span class="cx">     <head>
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>