Title: [216836] trunk
Revision
216836
Author
[email protected]
Date
2017-05-13 22:27:18 -0700 (Sat, 13 May 2017)

Log Message

[MediaStream] deviceId constraint doesn't work with getUserMedia
https://bugs.webkit.org/show_bug.cgi?id=171877
<rdar://problem/31899730>

Reviewed by Jer Noble.

Source/WebCore:

Test: fast/mediastream/get-user-media-device-id.html

* Modules/mediastream/MediaConstraintsImpl.h:
(WebCore::MediaConstraintsData::MediaConstraintsData): Add a constructor that
takes a const MediaConstraints&.

* Modules/mediastream/MediaDevices.cpp:
(WebCore::MediaDevices::~MediaDevices): m_deviceChangedToken is a std::optional<>.
* Modules/mediastream/MediaDevices.h:

* Modules/mediastream/MediaDevicesEnumerationRequest.cpp:
(WebCore::MediaDevicesEnumerationRequest::topLevelDocumentOrigin): Don't return
NULL for the main frame so the origin matches that returned for a UserMediaRequest.

* Modules/mediastream/UserMediaController.h:
(WebCore::UserMediaController::setDeviceIDHashSalt): Deleted, not used.
(WebCore::UserMediaController::deviceIDHashSalt): Deleted, not used.

* Modules/mediastream/UserMediaRequest.cpp:
(WebCore::UserMediaRequest::allow): Add device ID hash salt parameter, set it on
constraints.
* Modules/mediastream/UserMediaRequest.h:

* platform/mediastream/MediaConstraints.h:
* platform/mediastream/RealtimeMediaSource.cpp:
(WebCore::RealtimeMediaSource::fitnessDistance): ASSERT if called for DeviceId.
(WebCore::RealtimeMediaSource::selectSettings): Special case DeviceId because it
we have to hash the device ID before comparing, and because the DeviceId can't be
changed so it should never be added to the flattened constraints.
(WebCore::RealtimeMediaSource::supportsConstraints):
(WebCore::RealtimeMediaSource::applyConstraints):
* platform/mediastream/RealtimeMediaSource.h:

* platform/mediastream/RealtimeMediaSourceCenter.cpp:
(WebCore::RealtimeMediaSourceCenter::validateRequestConstraints): Implement.
* platform/mediastream/RealtimeMediaSourceCenter.h:

* platform/mediastream/RealtimeMediaSourceSupportedConstraints.cpp:
(WebCore::RealtimeMediaSourceSupportedConstraints::nameForConstraint): Deleted, unused.
(WebCore::RealtimeMediaSourceSupportedConstraints::constraintFromName): Deleted, unused.
* platform/mediastream/RealtimeMediaSourceSupportedConstraints.h:

* platform/mediastream/mac/AVVideoCaptureSource.mm:
* platform/mediastream/mac/RealtimeMediaSourceCenterMac.cpp:
(WebCore::RealtimeMediaSourceCenterMac::bestSourcesForTypeAndConstraints): Pass device
id, not empty string.
(WebCore::RealtimeMediaSourceCenterMac::validateRequestConstraints): Deleted.
* platform/mediastream/mac/RealtimeMediaSourceCenterMac.h:

* platform/mock/MockRealtimeMediaSourceCenter.cpp:
(WebCore::MockRealtimeMediaSourceCenter::validateRequestConstraints): Deleted.
* platform/mock/MockRealtimeMediaSourceCenter.h:

Source/WebKit2:

* Shared/WebCoreArgumentCoders.cpp:
(IPC::ArgumentCoder<MediaConstraintsData>::encode): Encode deviceIDHashSalt.
(IPC::ArgumentCoder<MediaConstraintsData>::decode): Decode deviceIDHashSalt.

* UIProcess/UserMediaPermissionCheckProxy.cpp:
(WebKit::UserMediaPermissionCheckProxy::UserMediaPermissionCheckProxy): Initialize
completion handler, frame ID, and security origins.
(WebKit::UserMediaPermissionCheckProxy::setUserMediaAccessInfo): Complete by calling
completion handler because we now sometimes request access info before calling gUM.
(WebKit::UserMediaPermissionCheckProxy::invalidate): Clear completion handler.
* UIProcess/UserMediaPermissionCheckProxy.h:

* UIProcess/UserMediaPermissionRequestManagerProxy.cpp:
(WebKit::FrameAuthorizationState::FrameAuthorizationState): Take security origins, not
UserMediaPermissionRequestProxy, so it can be constructed with a UserMediaPermissionCheckProxy.
(WebKit::FrameAuthorizationState::ensureSecurityOriginsAreEqual): Ditto. Clear has salt
when origins don't match.
(WebKit::UserMediaPermissionRequestManagerProxy::stateForRequest): Templatize.
(WebKit::UserMediaPermissionRequestManagerProxy::userMediaAccessWasDenied): Fix typo.
(WebKit::UserMediaPermissionRequestManagerProxy::userMediaAccessWasGranted): Ditto.
Don't set state for empty UIDs. Pass hash salt to web process.
(WebKit::UserMediaPermissionRequestManagerProxy::requestUserMediaPermissionForFrame):
The device ID hash salt is now required to validate constraints, so get it first.
(WebKit::UserMediaPermissionRequestManagerProxy::getUserMediaPermissionInfo): Helper
method used to get the device ID hash salt.
(WebKit::UserMediaPermissionRequestManagerProxy::enumerateMediaDevicesForFrame): Restructure
to use getUserMediaPermissionInfo.
(WebKit::UserMediaPermissionRequestManagerProxy::didCompleteUserMediaPermissionCheck): Deleted.
* UIProcess/UserMediaPermissionRequestManagerProxy.h:

* WebProcess/MediaStream/UserMediaPermissionRequestManager.cpp:
(WebKit::UserMediaPermissionRequestManager::userMediaAccessWasGranted): Add device ID
hash salt parameter.
* WebProcess/MediaStream/UserMediaPermissionRequestManager.h:

* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::userMediaAccessWasGranted): Ditto.
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

* WebProcess/cocoa/UserMediaCaptureManager.cpp:
(WebKit::UserMediaCaptureManager::createCaptureSource): Use new MediaConstraintsData
constructor.

Tools:

The device ID hash salt is now required for getUserMedia to check deviceId constraint, so
implement the "checkUserMediaPermission" callback.
* TestWebKitAPI/Tests/WebKit2/UserMedia.cpp:
(TestWebKitAPI::decidePolicyForUserMediaPermissionRequestCallBack):
(TestWebKitAPI::checkUserMediaPermissionCallback):
(TestWebKitAPI::TEST):

* TestWebKitAPI/Tests/WebKit2Cocoa/UserMediaDisabled.mm:
(-[UserMediaUIDelegate _webView:checkUserMediaPermissionForURL:mainFrameURL:frameIdentifier:decisionHandler:]):

LayoutTests:

* fast/mediastream/get-user-media-device-id-expected.txt: Added.
* fast/mediastream/get-user-media-device-id.html: Added.
* fast/mediastream/apply-constraints-video-expected.txt: Updated.
* fast/mediastream/apply-constraints-video.html: Ditto.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (216835 => 216836)


--- trunk/LayoutTests/ChangeLog	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/LayoutTests/ChangeLog	2017-05-14 05:27:18 UTC (rev 216836)
@@ -1,3 +1,16 @@
+2017-05-13  Eric Carlson  <[email protected]>
+
+        [MediaStream] deviceId constraint doesn't work with getUserMedia
+        https://bugs.webkit.org/show_bug.cgi?id=171877
+        <rdar://problem/31899730>
+
+        Reviewed by Jer Noble.
+
+        * fast/mediastream/get-user-media-device-id-expected.txt: Added.
+        * fast/mediastream/get-user-media-device-id.html: Added.
+        * fast/mediastream/apply-constraints-video-expected.txt: Updated.
+        * fast/mediastream/apply-constraints-video.html: Ditto.
+
 2017-05-13  David Kilzer  <[email protected]>
 
         TestExpectations: Remove last refereneces to Legacy Notifications

Modified: trunk/LayoutTests/fast/mediastream/apply-constraints-video-expected.txt (216835 => 216836)


--- trunk/LayoutTests/fast/mediastream/apply-constraints-video-expected.txt	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/LayoutTests/fast/mediastream/apply-constraints-video-expected.txt	2017-05-14 05:27:18 UTC (rev 216836)
@@ -87,10 +87,6 @@
 PASS settings['width'] is 1024
 PASS settings['height'] is 576
 
-** Constraint: {"deviceId":{"exact":"20983-20o198-109283-098-09812"}} - the 'exact' deviceID doesn't match, promise should reject.
-PASS Promise was rejected
-PASS error.constraint is "deviceId"
-
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/fast/mediastream/apply-constraints-video.html (216835 => 216836)


--- trunk/LayoutTests/fast/mediastream/apply-constraints-video.html	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/LayoutTests/fast/mediastream/apply-constraints-video.html	2017-05-14 05:27:18 UTC (rev 216836)
@@ -94,12 +94,6 @@
                     constraint: { height: 576 }, 
                     expected: { width: 1024, height: 576},
                 },
-                {
-                    message: "the 'exact' deviceID doesn't match, promise should reject.",
-                    constraint: { deviceId: {exact: "20983-20o198-109283-098-09812"}, }, 
-                    expected: { },
-                    error: "deviceId",
-                },
             ];
 
             let tester = new ConstraintsTest({ video: true }, tests, "Tests applyConstraints on a video stream track.")

Added: trunk/LayoutTests/fast/mediastream/get-user-media-device-id-expected.txt (0 => 216836)


--- trunk/LayoutTests/fast/mediastream/get-user-media-device-id-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/mediastream/get-user-media-device-id-expected.txt	2017-05-14 05:27:18 UTC (rev 216836)
@@ -0,0 +1,5 @@
+
+PASS Collect device IDs 
+PASS Pass device IDs as exact constraints 
+PASS Pass device IDs as optional constraints 
+

Added: trunk/LayoutTests/fast/mediastream/get-user-media-device-id.html (0 => 216836)


--- trunk/LayoutTests/fast/mediastream/get-user-media-device-id.html	                        (rev 0)
+++ trunk/LayoutTests/fast/mediastream/get-user-media-device-id.html	2017-05-14 05:27:18 UTC (rev 216836)
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>Test passing capture device IDs to getUserMedia</title>
+    <script src=""
+    <script src=""
+    <script>
+    let deviceIds = [];
+
+    if (window.testRunner)
+        testRunner.setUserMediaPermission(true);
+
+    promise_test((test) => {
+        return navigator.mediaDevices.enumerateDevices()
+            .then((devices) => {
+                devices.forEach((device) => {
+                    let kind = device.kind == "audioinput" ? "audio" : "video";
+                    deviceIds.push({ type: kind, id : device.deviceId});
+                });
+            });
+    }, "Collect device IDs");
+    
+    let constraints = { };
+
+    promise_test((test) => {
+        deviceIds.forEach((info) => {
+            constraints[info.type] = { deviceId: { exact: info.id } };
+        });
+    
+        return navigator.mediaDevices.getUserMedia(constraints)
+            .then((stream) => {
+                assert_equals(stream.getAudioTracks().length, 1, "correct number of audio tracks");
+                assert_equals(stream.getVideoTracks().length, 1, "correct number of audio tracks");
+            })
+
+    }, "Pass device IDs as exact constraints");
+    
+    promise_test((test) => {
+        deviceIds.forEach((info) => {
+            constraints[info.type] = { deviceId: info.id };
+        });
+    
+        return navigator.mediaDevices.getUserMedia(constraints)
+            .then((stream) => {
+                assert_equals(stream.getAudioTracks().length, 1, "correct number of audio tracks");
+                assert_equals(stream.getVideoTracks().length, 1, "correct number of audio tracks");
+            })
+
+    }, "Pass device IDs as optional constraints");
+
+    </script>
+</head>
+<body>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (216835 => 216836)


--- trunk/Source/WebCore/ChangeLog	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/ChangeLog	2017-05-14 05:27:18 UTC (rev 216836)
@@ -1,3 +1,64 @@
+2017-05-13  Eric Carlson  <[email protected]>
+
+        [MediaStream] deviceId constraint doesn't work with getUserMedia
+        https://bugs.webkit.org/show_bug.cgi?id=171877
+        <rdar://problem/31899730>
+
+        Reviewed by Jer Noble.
+
+        Test: fast/mediastream/get-user-media-device-id.html
+
+        * Modules/mediastream/MediaConstraintsImpl.h:
+        (WebCore::MediaConstraintsData::MediaConstraintsData): Add a constructor that 
+        takes a const MediaConstraints&.
+
+        * Modules/mediastream/MediaDevices.cpp:
+        (WebCore::MediaDevices::~MediaDevices): m_deviceChangedToken is a std::optional<>.
+        * Modules/mediastream/MediaDevices.h:
+
+        * Modules/mediastream/MediaDevicesEnumerationRequest.cpp:
+        (WebCore::MediaDevicesEnumerationRequest::topLevelDocumentOrigin): Don't return
+        NULL for the main frame so the origin matches that returned for a UserMediaRequest.
+
+        * Modules/mediastream/UserMediaController.h:
+        (WebCore::UserMediaController::setDeviceIDHashSalt): Deleted, not used.
+        (WebCore::UserMediaController::deviceIDHashSalt): Deleted, not used.
+
+        * Modules/mediastream/UserMediaRequest.cpp:
+        (WebCore::UserMediaRequest::allow): Add device ID hash salt parameter, set it on
+        constraints.
+        * Modules/mediastream/UserMediaRequest.h:
+
+        * platform/mediastream/MediaConstraints.h:
+        * platform/mediastream/RealtimeMediaSource.cpp:
+        (WebCore::RealtimeMediaSource::fitnessDistance): ASSERT if called for DeviceId.
+        (WebCore::RealtimeMediaSource::selectSettings): Special case DeviceId because it
+        we have to hash the device ID before comparing, and because the DeviceId can't be
+        changed so it should never be added to the flattened constraints.
+        (WebCore::RealtimeMediaSource::supportsConstraints):
+        (WebCore::RealtimeMediaSource::applyConstraints):
+        * platform/mediastream/RealtimeMediaSource.h:
+
+        * platform/mediastream/RealtimeMediaSourceCenter.cpp:
+        (WebCore::RealtimeMediaSourceCenter::validateRequestConstraints): Implement.
+        * platform/mediastream/RealtimeMediaSourceCenter.h:
+
+        * platform/mediastream/RealtimeMediaSourceSupportedConstraints.cpp:
+        (WebCore::RealtimeMediaSourceSupportedConstraints::nameForConstraint): Deleted, unused.
+        (WebCore::RealtimeMediaSourceSupportedConstraints::constraintFromName): Deleted, unused.
+        * platform/mediastream/RealtimeMediaSourceSupportedConstraints.h:
+
+        * platform/mediastream/mac/AVVideoCaptureSource.mm:
+        * platform/mediastream/mac/RealtimeMediaSourceCenterMac.cpp:
+        (WebCore::RealtimeMediaSourceCenterMac::bestSourcesForTypeAndConstraints): Pass device
+        id, not empty string.
+        (WebCore::RealtimeMediaSourceCenterMac::validateRequestConstraints): Deleted.
+        * platform/mediastream/mac/RealtimeMediaSourceCenterMac.h:
+
+        * platform/mock/MockRealtimeMediaSourceCenter.cpp:
+        (WebCore::MockRealtimeMediaSourceCenter::validateRequestConstraints): Deleted.
+        * platform/mock/MockRealtimeMediaSourceCenter.h:
+
 2017-05-13  Chris Dumez  <[email protected]>
 
         Stop using RefPtr::release()

Modified: trunk/Source/WebCore/Modules/mediastream/MediaConstraintsImpl.h (216835 => 216836)


--- trunk/Source/WebCore/Modules/mediastream/MediaConstraintsImpl.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/Modules/mediastream/MediaConstraintsImpl.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -46,6 +46,20 @@
         , isValid(isValid)
     {
     }
+    MediaConstraintsData(const MediaConstraintsData& constraints, const String& hashSalt)
+        : mandatoryConstraints(constraints.mandatoryConstraints)
+        , advancedConstraints(constraints.advancedConstraints)
+        , deviceIDHashSalt(hashSalt)
+        , isValid(constraints.isValid)
+    {
+    }
+    MediaConstraintsData(const MediaConstraints& constraints)
+        : mandatoryConstraints(constraints.mandatoryConstraints())
+        , advancedConstraints(constraints.advancedConstraints())
+        , deviceIDHashSalt(constraints.deviceIDHashSalt())
+        , isValid(constraints.isValid())
+    {
+    }
 
     void setDefaultVideoConstraints();
     bool isConstraintSet(std::function<bool(const MediaTrackConstraintSetMap&)>&&);
@@ -52,6 +66,7 @@
 
     MediaTrackConstraintSetMap mandatoryConstraints;
     Vector<MediaTrackConstraintSetMap> advancedConstraints;
+    String deviceIDHashSalt;
     bool isValid { false };
 };
 
@@ -66,8 +81,11 @@
     const MediaTrackConstraintSetMap& mandatoryConstraints() const final { return m_data.mandatoryConstraints; }
     const Vector<MediaTrackConstraintSetMap>& advancedConstraints() const final { return m_data.advancedConstraints; }
     bool isValid() const final { return m_data.isValid; }
+
+    const String& deviceIDHashSalt() const final { return m_data.deviceIDHashSalt; }
+    void setDeviceIDHashSalt(const String& salt) final { m_data.deviceIDHashSalt = salt; }
+
     const MediaConstraintsData& data() const { return m_data; }
-
     void setDefaultVideoConstraints() { m_data.setDefaultVideoConstraints(); }
 
 private:

Modified: trunk/Source/WebCore/Modules/mediastream/MediaDevices.cpp (216835 => 216836)


--- trunk/Source/WebCore/Modules/mediastream/MediaDevices.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/Modules/mediastream/MediaDevices.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -61,7 +61,7 @@
 MediaDevices::~MediaDevices()
 {
     if (m_deviceChangedToken)
-        RealtimeMediaSourceCenter::singleton().removeDevicesChangedObserver(m_deviceChangedToken);
+        RealtimeMediaSourceCenter::singleton().removeDevicesChangedObserver(m_deviceChangedToken.value());
 }
 
 Ref<MediaDevices> MediaDevices::create(Document& document)

Modified: trunk/Source/WebCore/Modules/mediastream/MediaDevices.h (216835 => 216836)


--- trunk/Source/WebCore/Modules/mediastream/MediaDevices.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/Modules/mediastream/MediaDevices.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -82,7 +82,7 @@
     void derefEventTarget() override { deref(); }
 
     Timer m_scheduledEventTimer;
-    RealtimeMediaSourceCenter::DevicesChangedObserverToken m_deviceChangedToken { 0 };
+    std::optional<RealtimeMediaSourceCenter::DevicesChangedObserverToken> m_deviceChangedToken;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.cpp (216835 => 216836)


--- trunk/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/Modules/mediastream/MediaDevicesEnumerationRequest.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -65,11 +65,6 @@
     if (!scriptExecutionContext())
         return nullptr;
 
-    if (Frame* frame = downcast<Document>(*scriptExecutionContext()).frame()) {
-        if (frame->isMainFrame())
-            return nullptr;
-    }
-
     return &scriptExecutionContext()->topOrigin();
 }
 

Modified: trunk/Source/WebCore/Modules/mediastream/UserMediaController.h (216835 => 216836)


--- trunk/Source/WebCore/Modules/mediastream/UserMediaController.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/Modules/mediastream/UserMediaController.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -46,15 +46,11 @@
     void enumerateMediaDevices(MediaDevicesEnumerationRequest&);
     void cancelMediaDevicesEnumerationRequest(MediaDevicesEnumerationRequest&);
 
-    void setDeviceIDHashSalt(const String& salt) { m_idHashSalt = salt; }
-    String deviceIDHashSalt() const { return m_idHashSalt; }
-
     WEBCORE_EXPORT static const char* supplementName();
     static UserMediaController* from(Page* page) { return static_cast<UserMediaController*>(Supplement<Page>::from(page, supplementName())); }
 
 private:
     UserMediaClient* m_client;
-    String m_idHashSalt;
 };
 
 inline void UserMediaController::requestUserMediaAccess(UserMediaRequest& request)

Modified: trunk/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp (216835 => 216836)


--- trunk/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/Modules/mediastream/UserMediaRequest.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -153,10 +153,10 @@
     m_controller->requestUserMediaAccess(*this);
 }
 
-void UserMediaRequest::allow(const String& audioDeviceUID, const String& videoDeviceUID)
+void UserMediaRequest::allow(String&& audioDeviceUID, String&& videoDeviceUID, String&& deviceIdentifierHashSalt)
 {
-    m_allowedAudioDeviceUID = audioDeviceUID;
-    m_allowedVideoDeviceUID = videoDeviceUID;
+    m_allowedAudioDeviceUID = WTFMove(audioDeviceUID);
+    m_allowedVideoDeviceUID = WTFMove(videoDeviceUID);
 
     RefPtr<UserMediaRequest> protectedThis = this;
     RealtimeMediaSourceCenter::NewMediaStreamHandler callback = [this, protectedThis = WTFMove(protectedThis)](RefPtr<MediaStreamPrivate>&& privateStream) mutable {
@@ -180,6 +180,9 @@
         m_promise.resolve(stream);
     };
 
+    m_audioConstraints->setDeviceIDHashSalt(String(deviceIdentifierHashSalt));
+    m_videoConstraints->setDeviceIDHashSalt(WTFMove(deviceIdentifierHashSalt));
+
     RealtimeMediaSourceCenter::singleton().createMediaStream(WTFMove(callback), m_allowedAudioDeviceUID, m_allowedVideoDeviceUID, &m_audioConstraints.get(), &m_videoConstraints.get());
 }
 

Modified: trunk/Source/WebCore/Modules/mediastream/UserMediaRequest.h (216835 => 216836)


--- trunk/Source/WebCore/Modules/mediastream/UserMediaRequest.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/Modules/mediastream/UserMediaRequest.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -53,7 +53,7 @@
     void start();
 
     WEBCORE_EXPORT void setAllowedMediaDeviceUIDs(const String& audioDeviceUID, const String& videoDeviceUID);
-    WEBCORE_EXPORT void allow(const String& audioDeviceUID, const String& videoDeviceUID);
+    WEBCORE_EXPORT void allow(String&& audioDeviceUID, String&& videoDeviceUID, String&& deviceIdentifierHashSalt);
 
     enum MediaAccessDenialReason { NoConstraints, UserMediaDisabled, NoCaptureDevices, InvalidConstraint, HardwareError, PermissionDenied, OtherFailure };
     WEBCORE_EXPORT void deny(MediaAccessDenialReason, const String& invalidConstraint);

Modified: trunk/Source/WebCore/platform/mediastream/MediaConstraints.h (216835 => 216836)


--- trunk/Source/WebCore/platform/mediastream/MediaConstraints.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mediastream/MediaConstraints.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -811,6 +811,9 @@
     virtual const Vector<MediaTrackConstraintSetMap>& advancedConstraints() const = 0;
     virtual bool isValid() const = 0;
 
+    virtual const String& deviceIDHashSalt() const = 0;
+    virtual void setDeviceIDHashSalt(const String&) = 0;
+
 protected:
     MediaConstraints() { }
 };

Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp (216835 => 216836)


--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -39,6 +39,7 @@
 #include "MediaConstraints.h"
 #include "NotImplemented.h"
 #include "RealtimeMediaSourceCapabilities.h"
+#include "RealtimeMediaSourceCenter.h"
 #include <wtf/MainThread.h>
 #include <wtf/UUID.h>
 #include <wtf/text/StringHash.h>
@@ -341,11 +342,7 @@
     }
 
     case MediaConstraintType::DeviceId: {
-        ASSERT(constraint.isString());
-        if (!capabilities.supportsDeviceId())
-            return 0;
-
-        return downcast<StringConstraint>(constraint).fitnessDistance(m_id);
+        ASSERT_NOT_REACHED();
         break;
     }
 
@@ -502,7 +499,7 @@
     }
 }
 
-bool RealtimeMediaSource::selectSettings(const MediaConstraints& constraints, FlattenedConstraint& candidates, String& failedConstraint)
+bool RealtimeMediaSource::selectSettings(const MediaConstraints& constraints, FlattenedConstraint& candidates, String& failedConstraint, SelectType type)
 {
     m_fitnessScore = std::numeric_limits<double>::infinity();
 
@@ -541,6 +538,26 @@
             return false;
         }
 
+        // The deviceId can't be changed, and the constraint value is the hashed device ID, so verify that the
+        // device's unique ID hashes to the constraint value but don't include the constraint in the flattened
+        // constraint set.
+        if (constraint.constraintType() == MediaConstraintType::DeviceId) {
+            if (type == SelectType::ForApplyConstraints)
+                return false;
+
+            ASSERT(constraint.isString());
+            ASSERT(!constraints.deviceIDHashSalt().isEmpty());
+
+            auto hashedID = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(m_persistentID, constraints.deviceIDHashSalt());
+            double constraintDistance = downcast<StringConstraint>(constraint).fitnessDistance(hashedID);
+            if (std::isinf(constraintDistance)) {
+                failedConstraint = constraint.name();
+                return true;
+            }
+
+            return false;
+        }
+
         double constraintDistance = fitnessDistance(constraint);
         if (std::isinf(constraintDistance)) {
             failedConstraint = constraint.name();
@@ -681,7 +698,7 @@
     ASSERT(constraints.isValid());
 
     FlattenedConstraint candidates;
-    if (!selectSettings(constraints, candidates, invalidConstraint))
+    if (!selectSettings(constraints, candidates, invalidConstraint, SelectType::ForSupportsConstraints))
         return false;
     
     return true;
@@ -744,7 +761,7 @@
 
     FlattenedConstraint candidates;
     String failedConstraint;
-    if (!selectSettings(constraints, candidates, failedConstraint))
+    if (!selectSettings(constraints, candidates, failedConstraint, SelectType::ForApplyConstraints))
         return { { failedConstraint, ASCIILiteral("Constraint not supported") } };
 
     applyConstraints(candidates);

Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h (216835 => 216836)


--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSource.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -222,7 +222,8 @@
     virtual void beginConfiguration() { }
     virtual void commitConfiguration() { }
 
-    virtual bool selectSettings(const MediaConstraints&, FlattenedConstraint&, String&);
+    enum class SelectType { ForApplyConstraints, ForSupportsConstraints };
+    bool selectSettings(const MediaConstraints&, FlattenedConstraint&, String&, SelectType);
     virtual double fitnessDistance(const MediaConstraint&);
     virtual bool supportsSizeAndFrameRate(std::optional<IntConstraint> width, std::optional<IntConstraint> height, std::optional<DoubleConstraint>, String&, double& fitnessDistance);
     virtual bool supportsSizeAndFrameRate(std::optional<int> width, std::optional<int> height, std::optional<double>);

Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.cpp (216835 => 216836)


--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -198,6 +198,68 @@
         it.value();
 }
 
+void RealtimeMediaSourceCenter::validateRequestConstraints(ValidConstraintsHandler&& validHandler, InvalidConstraintsHandler&& invalidHandler, const MediaConstraints& audioConstraints, const MediaConstraints& videoConstraints)
+{
+    struct DeviceInfo {
+        unsigned fitnessScore;
+        String id;
+    };
+
+    struct {
+        bool operator()(const DeviceInfo& a, const DeviceInfo& b)
+        {
+            return a.fitnessScore < b.fitnessScore;
+        }
+    } sortBasedOnFitnessScore;
+
+    Vector<DeviceInfo> audioDeviceInfo;
+    Vector<DeviceInfo> videoDeviceInfo;
+
+    String firstInvalidConstraint;
+    for (auto& device : getMediaStreamDevices()) {
+        if (!device.enabled())
+            continue;
+
+        String invalidConstraint;
+        CaptureSourceOrError sourceOrError;
+        if (device.type() == CaptureDevice::DeviceType::Video && videoConstraints.isValid()) {
+            auto sourceOrError = videoFactory().createVideoCaptureSource(device.persistentId(), nullptr);
+            if (sourceOrError && sourceOrError.captureSource->supportsConstraints(videoConstraints, invalidConstraint))
+                videoDeviceInfo.append({sourceOrError.captureSource->fitnessScore(), device.persistentId()});
+        } else if (device.type() == CaptureDevice::DeviceType::Audio && audioConstraints.isValid()) {
+            auto sourceOrError = audioFactory().createAudioCaptureSource(device.persistentId(), nullptr);
+            if (sourceOrError && sourceOrError.captureSource->supportsConstraints(audioConstraints, invalidConstraint))
+                audioDeviceInfo.append({sourceOrError.captureSource->fitnessScore(), device.persistentId()});
+        }
+
+        if (!invalidConstraint.isEmpty() && firstInvalidConstraint.isEmpty())
+            firstInvalidConstraint = invalidConstraint;
+    }
+
+    if ((audioConstraints.isValid() && audioDeviceInfo.isEmpty()) || (videoConstraints.isValid() && videoDeviceInfo.isEmpty())) {
+        invalidHandler(firstInvalidConstraint);
+        return;
+    }
+
+    Vector<String> audioSourceIds;
+    if (!audioDeviceInfo.isEmpty()) {
+        audioSourceIds.reserveInitialCapacity(audioDeviceInfo.size());
+        std::sort(audioDeviceInfo.begin(), audioDeviceInfo.end(), sortBasedOnFitnessScore);
+        for (auto& info : audioDeviceInfo)
+            audioSourceIds.uncheckedAppend(WTFMove(info.id));
+    }
+
+    Vector<String> videoSourceIds;
+    if (!videoDeviceInfo.isEmpty()) {
+        videoSourceIds.reserveInitialCapacity(videoDeviceInfo.size());
+        std::sort(videoDeviceInfo.begin(), videoDeviceInfo.end(), sortBasedOnFitnessScore);
+        for (auto& info : videoDeviceInfo)
+            videoSourceIds.uncheckedAppend(WTFMove(info.id));
+    }
+
+    validHandler(WTFMove(audioSourceIds), WTFMove(videoSourceIds));
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(MEDIA_STREAM)

Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.h (216835 => 216836)


--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -57,7 +57,7 @@
 
     using ValidConstraintsHandler = std::function<void(const Vector<String>&& audioDeviceUIDs, const Vector<String>&& videoDeviceUIDs)>;
     using InvalidConstraintsHandler = std::function<void(const String& invalidConstraint)>;
-    virtual void validateRequestConstraints(ValidConstraintsHandler&&, InvalidConstraintsHandler&&, const MediaConstraints& audioConstraints, const MediaConstraints& videoConstraints) = 0;
+    virtual void validateRequestConstraints(ValidConstraintsHandler&&, InvalidConstraintsHandler&&, const MediaConstraints& audioConstraints, const MediaConstraints& videoConstraints);
 
     using NewMediaStreamHandler = std::function<void(RefPtr<MediaStreamPrivate>&&)>;
     virtual void createMediaStream(NewMediaStreamHandler&&, const String& audioDeviceID, const String& videoDeviceID, const MediaConstraints* audioConstraints, const MediaConstraints* videoConstraints) = 0;

Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceSupportedConstraints.cpp (216835 => 216836)


--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceSupportedConstraints.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceSupportedConstraints.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -35,72 +35,6 @@
 
 namespace WebCore {
 
-const AtomicString& RealtimeMediaSourceSupportedConstraints::nameForConstraint(MediaConstraintType constraint)
-{
-    static NeverDestroyed<AtomicString> unknownConstraintName(emptyString());
-    static NeverDestroyed<AtomicString> widthConstraintName("width");
-    static NeverDestroyed<AtomicString> heightConstraintName("height");
-    static NeverDestroyed<AtomicString> aspectRatioConstraintName("aspectRatio");
-    static NeverDestroyed<AtomicString> frameRateConstraintName("frameRate");
-    static NeverDestroyed<AtomicString> facingModeConstraintName("facingMode");
-    static NeverDestroyed<AtomicString> volumeConstraintName("volume");
-    static NeverDestroyed<AtomicString> sampleRateConstraintName("sampleRate");
-    static NeverDestroyed<AtomicString> sampleSizeConstraintName("sampleSize");
-    static NeverDestroyed<AtomicString> echoCancellationConstraintName("echoCancellation");
-    static NeverDestroyed<AtomicString> deviceIdConstraintName("deviceId");
-    static NeverDestroyed<AtomicString> groupIdConstraintName("groupId");
-    switch (constraint) {
-    case MediaConstraintType::Unknown:
-        return unknownConstraintName;
-    case MediaConstraintType::Width:
-        return widthConstraintName;
-    case MediaConstraintType::Height:
-        return heightConstraintName;
-    case MediaConstraintType::AspectRatio:
-        return aspectRatioConstraintName;
-    case MediaConstraintType::FrameRate:
-        return frameRateConstraintName;
-    case MediaConstraintType::FacingMode:
-        return facingModeConstraintName;
-    case MediaConstraintType::Volume:
-        return volumeConstraintName;
-    case MediaConstraintType::SampleRate:
-        return sampleRateConstraintName;
-    case MediaConstraintType::SampleSize:
-        return sampleSizeConstraintName;
-    case MediaConstraintType::EchoCancellation:
-        return echoCancellationConstraintName;
-    case MediaConstraintType::DeviceId:
-        return deviceIdConstraintName;
-    case MediaConstraintType::GroupId:
-        return groupIdConstraintName;
-    }
-
-    ASSERT_NOT_REACHED();
-    return emptyAtom;
-}
-
-MediaConstraintType RealtimeMediaSourceSupportedConstraints::constraintFromName(const String& constraintName)
-{
-    static NeverDestroyed<HashMap<AtomicString, MediaConstraintType>> nameToConstraintMap;
-    HashMap<AtomicString, MediaConstraintType>& nameToConstraintMapValue = nameToConstraintMap.get();
-    if (!nameToConstraintMapValue.size()) {
-        nameToConstraintMapValue.add("width", MediaConstraintType::Width);
-        nameToConstraintMapValue.add("height", MediaConstraintType::Height);
-        nameToConstraintMapValue.add("aspectRatio", MediaConstraintType::AspectRatio);
-        nameToConstraintMapValue.add("frameRate", MediaConstraintType::FrameRate);
-        nameToConstraintMapValue.add("facingMode", MediaConstraintType::FacingMode);
-        nameToConstraintMapValue.add("volume", MediaConstraintType::Volume);
-        nameToConstraintMapValue.add("sampleRate", MediaConstraintType::SampleRate);
-        nameToConstraintMapValue.add("sampleSize", MediaConstraintType::SampleSize);
-        nameToConstraintMapValue.add("echoCancellation", MediaConstraintType::EchoCancellation);
-        nameToConstraintMapValue.add("deviceId", MediaConstraintType::DeviceId);
-        nameToConstraintMapValue.add("groupId", MediaConstraintType::GroupId);
-    }
-    auto iter = nameToConstraintMapValue.find(constraintName);
-    return iter == nameToConstraintMapValue.end() ? MediaConstraintType::Unknown : iter->value;
-}
-
 bool RealtimeMediaSourceSupportedConstraints::supportsConstraint(MediaConstraintType constraint) const
 {
     switch (constraint) {

Modified: trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceSupportedConstraints.h (216835 => 216836)


--- trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceSupportedConstraints.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mediastream/RealtimeMediaSourceSupportedConstraints.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -93,9 +93,6 @@
 
     bool supportsConstraint(MediaConstraintType) const;
 
-    static const AtomicString& nameForConstraint(MediaConstraintType);
-    static MediaConstraintType constraintFromName(const String&);
-
     template<class Encoder> void encode(Encoder&) const;
     template<class Decoder> static bool decode(Decoder&, RealtimeMediaSourceSupportedConstraints&);
 

Modified: trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm (216835 => 216836)


--- trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mediastream/mac/AVVideoCaptureSource.mm	2017-05-14 05:27:18 UTC (rev 216836)
@@ -120,7 +120,7 @@
         AVCaptureDeviceTypedef *device = [getAVCaptureDeviceClass() deviceWithUniqueID:deviceID];
         if (!device)
             return { };
-        return AVVideoCaptureSource::create(device, emptyString(), constraints);
+        return AVVideoCaptureSource::create(device, deviceID, constraints);
     }
 };
 

Modified: trunk/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.cpp (216835 => 216836)


--- trunk/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -81,31 +81,6 @@
 {
 }
 
-void RealtimeMediaSourceCenterMac::validateRequestConstraints(ValidConstraintsHandler&& validHandler, InvalidConstraintsHandler&& invalidHandler, const MediaConstraints& audioConstraints, const MediaConstraints& videoConstraints)
-{
-    Vector<String> audioSourceUIDs;
-    Vector<String> videoSourceUIDs;
-    String invalidConstraint;
-
-    if (audioConstraints.isValid()) {
-        audioSourceUIDs = bestSourcesForTypeAndConstraints(RealtimeMediaSource::Type::Audio, audioConstraints, invalidConstraint);
-        if (!invalidConstraint.isEmpty()) {
-            invalidHandler(invalidConstraint);
-            return;
-        }
-    }
-
-    if (videoConstraints.isValid()) {
-        videoSourceUIDs = bestSourcesForTypeAndConstraints(RealtimeMediaSource::Type::Video, videoConstraints, invalidConstraint);
-        if (!invalidConstraint.isEmpty()) {
-            invalidHandler(invalidConstraint);
-            return;
-        }
-    }
-
-    validHandler(WTFMove(audioSourceUIDs), WTFMove(videoSourceUIDs));
-}
-
 void RealtimeMediaSourceCenterMac::createMediaStream(NewMediaStreamHandler&& completionHandler, const String& audioDeviceID, const String& videoDeviceID, const MediaConstraints* audioConstraints, const MediaConstraints* videoConstraints)
 {
     Vector<Ref<RealtimeMediaSource>> audioSources;
@@ -152,48 +127,6 @@
     return result;
 }
 
-Vector<String> RealtimeMediaSourceCenterMac::bestSourcesForTypeAndConstraints(RealtimeMediaSource::Type type, const MediaConstraints& constraints, String& invalidConstraint)
-{
-    Vector<RefPtr<RealtimeMediaSource>> bestSources;
-
-    struct {
-        bool operator()(RefPtr<RealtimeMediaSource> a, RefPtr<RealtimeMediaSource> b)
-        {
-            return a->fitnessScore() < b->fitnessScore();
-        }
-    } sortBasedOnFitnessScore;
-
-    CaptureDevice::DeviceType deviceType = type == RealtimeMediaSource::Type::Video ? CaptureDevice::DeviceType::Video : CaptureDevice::DeviceType::Audio;
-    for (auto& captureDevice : getMediaStreamDevices()) {
-        if (!captureDevice.enabled() || captureDevice.type() != deviceType)
-            continue;
-
-        CaptureSourceOrError sourceOrError;
-        if (type == RealtimeMediaSource::Type::Video)
-            sourceOrError = videoFactory().createVideoCaptureSource(captureDevice.persistentId(), &constraints);
-        else if (type == RealtimeMediaSource::Type::Audio)
-            sourceOrError = audioFactory().createAudioCaptureSource(captureDevice.persistentId(), &constraints);
-
-        if (!sourceOrError) {
-            // FIXME: Handle the case of invalid constraints on more than one device.
-            invalidConstraint = WTFMove(sourceOrError.errorMessage);
-            continue;
-        }
-        bestSources.append(sourceOrError.source());
-    }
-
-    Vector<String> sourceUIDs;
-    if (bestSources.isEmpty())
-        return sourceUIDs;
-
-    sourceUIDs.reserveInitialCapacity(bestSources.size());
-    std::sort(bestSources.begin(), bestSources.end(), sortBasedOnFitnessScore);
-    for (auto& device : bestSources)
-        sourceUIDs.uncheckedAppend(device->persistentID());
-
-    return sourceUIDs;
-}
-
 RealtimeMediaSource::AudioCaptureFactory& RealtimeMediaSourceCenterMac::defaultAudioFactory()
 {
     return m_useAVFoundationAudioCapture ? AVAudioCaptureSource::factory() : CoreAudioCaptureSource::factory();

Modified: trunk/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.h (216835 => 216836)


--- trunk/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mediastream/mac/RealtimeMediaSourceCenterMac.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -50,12 +50,9 @@
     RealtimeMediaSourceCenterMac();
     ~RealtimeMediaSourceCenterMac();
 
-    void validateRequestConstraints(ValidConstraintsHandler&& validHandler, InvalidConstraintsHandler&& invalidHandler, const MediaConstraints& audioConstraints, const MediaConstraints& videoConstraints) final;
     void createMediaStream(NewMediaStreamHandler&&, const String& audioDeviceID, const String& videoDeviceID, const MediaConstraints* audioConstraints, const MediaConstraints* videoConstraints) final;
     Vector<CaptureDevice> getMediaStreamDevices() final;
 
-    Vector<String> bestSourcesForTypeAndConstraints(RealtimeMediaSource::Type, const MediaConstraints&, String& invalidConstraint);
-
     RealtimeMediaSource::AudioCaptureFactory& defaultAudioFactory() final;
     RealtimeMediaSource::VideoCaptureFactory& defaultVideoFactory() final;
 

Modified: trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp (216835 => 216836)


--- trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -65,67 +65,6 @@
     m_supportedConstraints.setSupportsDeviceId(true);
 }
 
-void MockRealtimeMediaSourceCenter::validateRequestConstraints(ValidConstraintsHandler&& validHandler, InvalidConstraintsHandler&& invalidHandler, const MediaConstraints& audioConstraints, const MediaConstraints& videoConstraints)
-{
-    Vector<String> audioSourceIds;
-    Vector<String> videoSourceIds;
-    String invalidConstraint;
-
-    struct DeviceInfo {
-        unsigned fitnessScore;
-        String id;
-    };
-
-    struct {
-        bool operator()(const DeviceInfo& a, const DeviceInfo& b)
-        {
-            return a.fitnessScore < b.fitnessScore;
-        }
-    } sortBasedOnFitnessScore;
-
-    if (audioConstraints.isValid()) {
-        Vector<DeviceInfo> deviceInfo;
-        for (const auto& device : MockRealtimeMediaSource::audioDevices()) {
-            auto sourceOrError = MockRealtimeAudioSource::create(device.label(), nullptr);
-            if (sourceOrError && sourceOrError.captureSource->supportsConstraints(audioConstraints, invalidConstraint))
-                deviceInfo.append({sourceOrError.captureSource->fitnessScore(), device.persistentId()});
-        }
-
-        if (deviceInfo.isEmpty()) {
-            if (invalidHandler)
-                invalidHandler(invalidConstraint);
-            return;
-        }
-
-        audioSourceIds.reserveInitialCapacity(deviceInfo.size());
-        std::sort(deviceInfo.begin(), deviceInfo.end(), sortBasedOnFitnessScore);
-        for (const auto& info : deviceInfo)
-            audioSourceIds.uncheckedAppend(info.id);
-    }
-
-    if (videoConstraints.isValid()) {
-        Vector<DeviceInfo> deviceInfo;
-        for (const auto& device : MockRealtimeMediaSource::videoDevices()) {
-            auto sourceOrError = MockRealtimeVideoSource::create(device.label(), nullptr);
-            if (sourceOrError && sourceOrError.captureSource->supportsConstraints(videoConstraints, invalidConstraint))
-                deviceInfo.append({sourceOrError.captureSource->fitnessScore(), device.persistentId()});
-        }
-
-        if (deviceInfo.isEmpty()) {
-            if (invalidHandler)
-                invalidHandler(invalidConstraint);
-            return;
-        }
-
-        videoSourceIds.reserveInitialCapacity(deviceInfo.size());
-        std::sort(deviceInfo.begin(), deviceInfo.end(), sortBasedOnFitnessScore);
-        for (const auto& info : deviceInfo)
-            videoSourceIds.uncheckedAppend(info.id);
-    }
-
-    validHandler(WTFMove(audioSourceIds), WTFMove(videoSourceIds));
-}
-
 void MockRealtimeMediaSourceCenter::createMediaStream(NewMediaStreamHandler&& completionHandler, const String& audioDeviceID, const String& videoDeviceID, const MediaConstraints* audioConstraints, const MediaConstraints* videoConstraints)
 {
     Vector<Ref<RealtimeMediaSource>> audioSources;

Modified: trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h (216835 => 216836)


--- trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebCore/platform/mock/MockRealtimeMediaSourceCenter.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -41,7 +41,6 @@
     friend NeverDestroyed<MockRealtimeMediaSourceCenter>;
     MockRealtimeMediaSourceCenter();
 
-    void validateRequestConstraints(ValidConstraintsHandler&& validHandler, InvalidConstraintsHandler&& invalidHandler, const MediaConstraints& audioConstraints, const MediaConstraints& videoConstraints) final;
     Vector<CaptureDevice> getMediaStreamDevices() final;
     void createMediaStream(NewMediaStreamHandler&&, const String& audioDeviceID, const String& videoDeviceID, const MediaConstraints* audioConstraints, const MediaConstraints* videoConstraints) final;
 

Modified: trunk/Source/WebKit2/ChangeLog (216835 => 216836)


--- trunk/Source/WebKit2/ChangeLog	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/ChangeLog	2017-05-14 05:27:18 UTC (rev 216836)
@@ -1,3 +1,55 @@
+2017-05-13  Eric Carlson  <[email protected]>
+
+        [MediaStream] deviceId constraint doesn't work with getUserMedia
+        https://bugs.webkit.org/show_bug.cgi?id=171877
+        <rdar://problem/31899730>
+
+        Reviewed by Jer Noble.
+
+        * Shared/WebCoreArgumentCoders.cpp:
+        (IPC::ArgumentCoder<MediaConstraintsData>::encode): Encode deviceIDHashSalt.
+        (IPC::ArgumentCoder<MediaConstraintsData>::decode): Decode deviceIDHashSalt.
+
+        * UIProcess/UserMediaPermissionCheckProxy.cpp:
+        (WebKit::UserMediaPermissionCheckProxy::UserMediaPermissionCheckProxy): Initialize
+        completion handler, frame ID, and security origins.
+        (WebKit::UserMediaPermissionCheckProxy::setUserMediaAccessInfo): Complete by calling
+        completion handler because we now sometimes request access info before calling gUM.
+        (WebKit::UserMediaPermissionCheckProxy::invalidate): Clear completion handler.
+        * UIProcess/UserMediaPermissionCheckProxy.h:
+
+        * UIProcess/UserMediaPermissionRequestManagerProxy.cpp:
+        (WebKit::FrameAuthorizationState::FrameAuthorizationState): Take security origins, not
+        UserMediaPermissionRequestProxy, so it can be constructed with a UserMediaPermissionCheckProxy.
+        (WebKit::FrameAuthorizationState::ensureSecurityOriginsAreEqual): Ditto. Clear has salt
+        when origins don't match.
+        (WebKit::UserMediaPermissionRequestManagerProxy::stateForRequest): Templatize.
+        (WebKit::UserMediaPermissionRequestManagerProxy::userMediaAccessWasDenied): Fix typo.
+        (WebKit::UserMediaPermissionRequestManagerProxy::userMediaAccessWasGranted): Ditto.
+        Don't set state for empty UIDs. Pass hash salt to web process.
+        (WebKit::UserMediaPermissionRequestManagerProxy::requestUserMediaPermissionForFrame):
+        The device ID hash salt is now required to validate constraints, so get it first.
+        (WebKit::UserMediaPermissionRequestManagerProxy::getUserMediaPermissionInfo): Helper
+        method used to get the device ID hash salt.
+        (WebKit::UserMediaPermissionRequestManagerProxy::enumerateMediaDevicesForFrame): Restructure
+        to use getUserMediaPermissionInfo.
+        (WebKit::UserMediaPermissionRequestManagerProxy::didCompleteUserMediaPermissionCheck): Deleted.
+        * UIProcess/UserMediaPermissionRequestManagerProxy.h:
+
+        * WebProcess/MediaStream/UserMediaPermissionRequestManager.cpp:
+        (WebKit::UserMediaPermissionRequestManager::userMediaAccessWasGranted): Add device ID
+        hash salt parameter.
+        * WebProcess/MediaStream/UserMediaPermissionRequestManager.h:
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::userMediaAccessWasGranted): Ditto.
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
+        * WebProcess/cocoa/UserMediaCaptureManager.cpp:
+        (WebKit::UserMediaCaptureManager::createCaptureSource): Use new MediaConstraintsData 
+        constructor.
+
 2017-05-13  Chris Dumez  <[email protected]>
 
         Stop using RefPtr::release()

Modified: trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.cpp (216835 => 216836)


--- trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/Shared/WebCoreArgumentCoders.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -2393,6 +2393,7 @@
 {
     encoder << constraint.mandatoryConstraints
         << constraint.advancedConstraints
+        << constraint.deviceIDHashSalt
         << constraint.isValid;
 }
 
@@ -2400,6 +2401,7 @@
 {
     return decoder.decode(constraints.mandatoryConstraints)
         && decoder.decode(constraints.advancedConstraints)
+        && decoder.decode(constraints.deviceIDHashSalt)
         && decoder.decode(constraints.isValid);
 }
 

Modified: trunk/Source/WebKit2/UIProcess/UserMediaPermissionCheckProxy.cpp (216835 => 216836)


--- trunk/Source/WebKit2/UIProcess/UserMediaPermissionCheckProxy.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/UIProcess/UserMediaPermissionCheckProxy.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -27,28 +27,34 @@
 #include "UserMediaPermissionCheckProxy.h"
 
 #include "UserMediaPermissionRequestManagerProxy.h"
+#include <WebCore/SecurityOrigin.h>
+#include <WebCore/SecurityOriginData.h>
 
+using namespace WebCore;
+
 namespace WebKit {
 
-UserMediaPermissionCheckProxy::UserMediaPermissionCheckProxy(UserMediaPermissionRequestManagerProxy& manager, uint64_t userMediaID)
-    : m_manager(&manager)
-    , m_userMediaID(userMediaID)
+UserMediaPermissionCheckProxy::UserMediaPermissionCheckProxy(uint64_t userMediaID, uint64_t frameID, CompletionHandler&& handler, String&& userMediaDocumentOriginIdentifier, String&& topLevelDocumentOriginIdentifier)
+    : m_userMediaID(userMediaID)
+    , m_frameID(frameID)
+    , m_completionHandler(WTFMove(handler))
+    , m_userMediaDocumentSecurityOrigin((SecurityOriginData::fromDatabaseIdentifier(WTFMove(userMediaDocumentOriginIdentifier))->securityOrigin()))
+    , m_topLevelDocumentSecurityOrigin(SecurityOriginData::fromDatabaseIdentifier(WTFMove(topLevelDocumentOriginIdentifier))->securityOrigin())
 {
 }
 
-void UserMediaPermissionCheckProxy::setUserMediaAccessInfo(const String& mediaDeviceIdentifierHashSalt, bool allowed)
+void UserMediaPermissionCheckProxy::setUserMediaAccessInfo(String&& mediaDeviceIdentifierHashSalt, bool allowed)
 {
-    ASSERT(m_manager);
-    if (!m_manager)
+    if (!m_completionHandler)
         return;
 
-    m_manager->didCompleteUserMediaPermissionCheck(m_userMediaID, mediaDeviceIdentifierHashSalt, allowed);
-    m_manager = nullptr;
+    m_completionHandler(m_userMediaID, WTFMove(mediaDeviceIdentifierHashSalt), allowed);
+    m_completionHandler = nullptr;
 }
 
 void UserMediaPermissionCheckProxy::invalidate()
 {
-    m_manager = nullptr;
+    m_completionHandler = nullptr;
 }
 
 } // namespace WebKit

Modified: trunk/Source/WebKit2/UIProcess/UserMediaPermissionCheckProxy.h (216835 => 216836)


--- trunk/Source/WebKit2/UIProcess/UserMediaPermissionCheckProxy.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/UIProcess/UserMediaPermissionCheckProxy.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -29,6 +29,10 @@
 #include "APIObject.h"
 #include <wtf/text/WTFString.h>
 
+namespace WebCore {
+class SecurityOrigin;
+}
+
 namespace WebKit {
 
 class UserMediaPermissionRequestManagerProxy;
@@ -35,19 +39,29 @@
 
 class UserMediaPermissionCheckProxy : public API::ObjectImpl<API::Object::Type::UserMediaPermissionCheck> {
 public:
-    static Ref<UserMediaPermissionCheckProxy> create(UserMediaPermissionRequestManagerProxy& manager, uint64_t userMediaID)
+
+    using CompletionHandler = std::function<void(uint64_t, String&&, bool allowed)>;
+
+    static Ref<UserMediaPermissionCheckProxy> create(uint64_t userMediaID, uint64_t frameID, CompletionHandler&& handler, String&& userMediaDocumentOriginIdentifier, String&& topLevelDocumentOriginIdentifier)
     {
-        return adoptRef(*new UserMediaPermissionCheckProxy(manager, userMediaID));
+        return adoptRef(*new UserMediaPermissionCheckProxy(userMediaID, frameID, WTFMove(handler), WTFMove(userMediaDocumentOriginIdentifier), WTFMove(topLevelDocumentOriginIdentifier)));
     }
 
-    void setUserMediaAccessInfo(const String&, bool allowed);
+    void setUserMediaAccessInfo(String&&, bool allowed);
     void invalidate();
 
+    uint64_t frameID() const { return m_frameID; }
+    WebCore::SecurityOrigin* userMediaDocumentSecurityOrigin() { return &m_userMediaDocumentSecurityOrigin.get(); }
+    WebCore::SecurityOrigin* topLevelDocumentSecurityOrigin() { return &m_topLevelDocumentSecurityOrigin.get(); }
+
 private:
-    UserMediaPermissionCheckProxy(UserMediaPermissionRequestManagerProxy&, uint64_t);
+    UserMediaPermissionCheckProxy(uint64_t userMediaID, uint64_t frameID, CompletionHandler&&, String&& userMediaDocumentOriginIdentifier, String&& topLevelDocumentOriginIdentifier);
 
-    UserMediaPermissionRequestManagerProxy* m_manager;
     uint64_t m_userMediaID;
+    uint64_t m_frameID;
+    CompletionHandler m_completionHandler;
+    Ref<WebCore::SecurityOrigin> m_userMediaDocumentSecurityOrigin;
+    Ref<WebCore::SecurityOrigin> m_topLevelDocumentSecurityOrigin;
 };
 
 } // namespace WebKit

Modified: trunk/Source/WebKit2/UIProcess/UserMediaPermissionRequestManagerProxy.cpp (216835 => 216836)


--- trunk/Source/WebKit2/UIProcess/UserMediaPermissionRequestManagerProxy.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/UIProcess/UserMediaPermissionRequestManagerProxy.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -39,9 +39,9 @@
 
 namespace WebKit {
 
-FrameAuthorizationState::FrameAuthorizationState(UserMediaPermissionRequestProxy& request)
-    : m_userMediaDocumentSecurityOrigin(request.userMediaDocumentSecurityOrigin())
-    , m_topLevelDocumentSecurityOrigin(request.topLevelDocumentSecurityOrigin())
+FrameAuthorizationState::FrameAuthorizationState(SecurityOrigin* userMediaDocumentSecurityOrigin, SecurityOrigin* topLevelDocumentSecurityOrigin)
+    : m_userMediaDocumentSecurityOrigin(userMediaDocumentSecurityOrigin)
+    , m_topLevelDocumentSecurityOrigin(topLevelDocumentSecurityOrigin)
 {
 }
 
@@ -65,32 +65,34 @@
         m_authorizedDeviceUIDs.remove(index);
 }
 
-void FrameAuthorizationState::ensureSecurityOriginsAreEqual(UserMediaPermissionRequestProxy& request)
+void FrameAuthorizationState::ensureSecurityOriginsAreEqual(WebCore::SecurityOrigin* userMediaDocumentSecurityOrigin, WebCore::SecurityOrigin* topLevelDocumentSecurityOrigin)
 {
     do {
-        if (!m_userMediaDocumentSecurityOrigin || !m_userMediaDocumentSecurityOrigin->equal(request.userMediaDocumentSecurityOrigin()))
+        if (!m_userMediaDocumentSecurityOrigin || !m_userMediaDocumentSecurityOrigin->equal(userMediaDocumentSecurityOrigin))
             break;
 
-        if (!m_topLevelDocumentSecurityOrigin || !m_topLevelDocumentSecurityOrigin->equal(request.topLevelDocumentSecurityOrigin()))
+        if (!m_topLevelDocumentSecurityOrigin || !m_topLevelDocumentSecurityOrigin->equal(topLevelDocumentSecurityOrigin))
             break;
 
         return;
     } while (0);
 
-    m_userMediaDocumentSecurityOrigin = request.userMediaDocumentSecurityOrigin();
-    m_topLevelDocumentSecurityOrigin = request.topLevelDocumentSecurityOrigin();
+    m_userMediaDocumentSecurityOrigin = userMediaDocumentSecurityOrigin;
+    m_topLevelDocumentSecurityOrigin = topLevelDocumentSecurityOrigin;
     m_authorizedDeviceUIDs.clear();
+    m_deviceIdentifierHashSalt = emptyString();
 }
 
-FrameAuthorizationState& UserMediaPermissionRequestManagerProxy::stateForRequest(UserMediaPermissionRequestProxy& request)
+template <typename RequestType>
+FrameAuthorizationState& UserMediaPermissionRequestManagerProxy::stateForRequest(RequestType& request)
 {
     auto& state = m_frameStates.add(request.frameID(), nullptr).iterator->value;
     if (state) {
-        state->ensureSecurityOriginsAreEqual(request);
+        state->ensureSecurityOriginsAreEqual(request.userMediaDocumentSecurityOrigin(), request.topLevelDocumentSecurityOrigin());
         return *state;
     }
 
-    state = std::make_unique<FrameAuthorizationState>(request);
+    state = std::make_unique<FrameAuthorizationState>(request.userMediaDocumentSecurityOrigin(), request.topLevelDocumentSecurityOrigin());
     return *state;
 }
 
@@ -183,11 +185,11 @@
     if (!request)
         return;
 
-    auto fameState = stateForRequest(*request);
+    auto& frameState = stateForRequest(*request);
     for (const auto& deviceUID : request->videoDeviceUIDs())
-        fameState.setHasPermissionToUseCaptureDevice(deviceUID, false);
+        frameState.setHasPermissionToUseCaptureDevice(deviceUID, false);
     for (const auto& deviceUID : request->audioDeviceUIDs())
-        fameState.setHasPermissionToUseCaptureDevice(deviceUID, false);
+        frameState.setHasPermissionToUseCaptureDevice(deviceUID, false);
 
     denyRequest(userMediaID, reason, emptyString());
 }
@@ -216,13 +218,15 @@
     if (!request)
         return;
 
-    auto& fameState = stateForRequest(*request);
-    fameState.setHasPermissionToUseCaptureDevice(audioDeviceUID, true);
-    fameState.setHasPermissionToUseCaptureDevice(videoDeviceUID, true);
+    auto& frameState = stateForRequest(*request);
+    if (!audioDeviceUID.isEmpty())
+        frameState.setHasPermissionToUseCaptureDevice(audioDeviceUID, true);
+    if (!videoDeviceUID.isEmpty())
+        frameState.setHasPermissionToUseCaptureDevice(videoDeviceUID, true);
 
     UserMediaProcessManager::singleton().willCreateMediaStream(*this, !audioDeviceUID.isEmpty(), !videoDeviceUID.isEmpty());
 
-    m_page.process().send(Messages::WebPage::UserMediaAccessWasGranted(userMediaID, audioDeviceUID, videoDeviceUID), m_page.pageID());
+    m_page.process().send(Messages::WebPage::UserMediaAccessWasGranted(userMediaID, audioDeviceUID, videoDeviceUID, frameState.deviceIdentifierHashSalt()), m_page.pageID());
 #else
     UNUSED_PARAM(userMediaID);
     UNUSED_PARAM(audioDeviceUID);
@@ -266,21 +270,21 @@
             return;
         }
 
-        auto userMediaOrigin = API::SecurityOrigin::create(SecurityOriginData::fromDatabaseIdentifier(userMediaDocumentOriginIdentifier)->securityOrigin());
-        auto topLevelOrigin = API::SecurityOrigin::create(SecurityOriginData::fromDatabaseIdentifier(topLevelDocumentOriginIdentifier)->securityOrigin());
+        bool authorizedForAudio = false;
+        bool authorizedForVideo = false;
+        auto userMediaOrigin = API::SecurityOrigin::create(SecurityOriginData::fromDatabaseIdentifier(userMediaDocumentOriginIdentifier).value_or(SecurityOriginData()).securityOrigin());
+        auto topLevelOrigin = API::SecurityOrigin::create(SecurityOriginData::fromDatabaseIdentifier(topLevelDocumentOriginIdentifier).value_or(SecurityOriginData()).securityOrigin());
         auto request = createRequest(userMediaID, frameID, userMediaDocumentOriginIdentifier, topLevelDocumentOriginIdentifier, audioDeviceUIDs, videoDeviceUIDs);
 
-        bool authorizedForAudio = false;
-        bool authorizedForVideo = false;
-        auto& fameState = stateForRequest(request);
+        auto& frameState = stateForRequest(request.get());
         for (auto deviceUID : audioDeviceUIDs) {
-            if (fameState.hasPermissionToUseCaptureDevice(deviceUID)) {
+            if (frameState.hasPermissionToUseCaptureDevice(deviceUID)) {
                 authorizedForAudio = true;
                 break;
             }
         }
         for (auto deviceUID : videoDeviceUIDs) {
-            if (fameState.hasPermissionToUseCaptureDevice(deviceUID)) {
+            if (frameState.hasPermissionToUseCaptureDevice(deviceUID)) {
                 authorizedForVideo = true;
                 break;
             }
@@ -302,11 +306,30 @@
         return;
     }
 
-    auto audioConstraints = MediaConstraintsImpl::create(MediaConstraintsData(audioConstraintsData));
-    auto videoConstraints = MediaConstraintsImpl::create(MediaConstraintsData(videoConstraintsData));
+    auto validateConstraintsHandler = [this, userMediaID, validHandler = WTFMove(validHandler), invalidHandler = WTFMove(invalidHandler), audioConstraintsData, videoConstraintsData](String&& deviceIdentifierHashSalt) mutable {
+        syncWithWebCorePrefs();
 
-    syncWithWebCorePrefs();
-    RealtimeMediaSourceCenter::singleton().validateRequestConstraints(validHandler, invalidHandler, audioConstraints, videoConstraints);
+        auto audioConstraints = MediaConstraintsImpl::create(MediaConstraintsData(audioConstraintsData, deviceIdentifierHashSalt));
+        auto videoConstraints = MediaConstraintsImpl::create(MediaConstraintsData(videoConstraintsData, deviceIdentifierHashSalt));
+
+        RealtimeMediaSourceCenter::singleton().validateRequestConstraints(WTFMove(validHandler), WTFMove(invalidHandler), audioConstraints, videoConstraints);
+    };
+
+    auto haveDeviceSaltHandler = [this, userMediaID, frameID, validateConstraintsHandler = WTFMove(validateConstraintsHandler)](uint64_t userMediaID, String&& deviceIdentifierHashSalt, bool originHasPersistentAccess) mutable {
+
+        auto request = m_pendingDeviceRequests.take(userMediaID);
+        if (!request)
+            return;
+
+        if (!m_page.isValid())
+            return;
+
+        auto& frameState = stateForRequest(*request);
+        frameState.setDeviceIdentifierHashSalt(String(deviceIdentifierHashSalt));
+        validateConstraintsHandler(WTFMove(deviceIdentifierHashSalt));
+    };
+
+    getUserMediaPermissionInfo(userMediaID, frameID, WTFMove(haveDeviceSaltHandler), WTFMove(userMediaDocumentOriginIdentifier), WTFMove(topLevelDocumentOriginIdentifier));
 #else
     UNUSED_PARAM(userMediaID);
     UNUSED_PARAM(frameID);
@@ -317,42 +340,47 @@
 #endif
 }
 
-void UserMediaPermissionRequestManagerProxy::enumerateMediaDevicesForFrame(uint64_t userMediaID, uint64_t frameID, String userMediaDocumentOriginIdentifier, String topLevelDocumentOriginIdentifier)
+#if ENABLE(MEDIA_STREAM)
+void UserMediaPermissionRequestManagerProxy::getUserMediaPermissionInfo(uint64_t userMediaID, uint64_t frameID, UserMediaPermissionCheckProxy::CompletionHandler&& handler, String&& userMediaDocumentOriginIdentifier, String&& topLevelDocumentOriginIdentifier)
 {
-#if ENABLE(MEDIA_STREAM)
-    auto request = UserMediaPermissionCheckProxy::create(*this, userMediaID);
+    UserMediaPermissionCheckProxy::CompletionHandler failureHandler = handler;
+
+    auto request = UserMediaPermissionCheckProxy::create(userMediaID, frameID, WTFMove(handler), String(userMediaDocumentOriginIdentifier), String(topLevelDocumentOriginIdentifier));
     m_pendingDeviceRequests.add(userMediaID, request.ptr());
 
     auto userMediaOrigin = API::SecurityOrigin::create(SecurityOriginData::fromDatabaseIdentifier(userMediaDocumentOriginIdentifier).value_or(SecurityOriginData()).securityOrigin());
     auto topLevelOrigin = API::SecurityOrigin::create(SecurityOriginData::fromDatabaseIdentifier(topLevelDocumentOriginIdentifier).value_or(SecurityOriginData()).securityOrigin());
 
-    if (!m_page.uiClient().checkUserMediaPermissionForOrigin(m_page, *m_page.process().webFrame(frameID), userMediaOrigin, topLevelOrigin, request.get())) {
-        m_pendingDeviceRequests.take(userMediaID);
-        m_page.process().send(Messages::WebPage::DidCompleteMediaDeviceEnumeration(userMediaID, Vector<WebCore::CaptureDevice>(), emptyString(), false), m_page.pageID());
-    }
-#else
-    UNUSED_PARAM(userMediaID);
-    UNUSED_PARAM(frameID);
-    UNUSED_PARAM(userMediaDocumentOriginIdentifier);
-    UNUSED_PARAM(topLevelDocumentOriginIdentifier);
+    if (!m_page.uiClient().checkUserMediaPermissionForOrigin(m_page, *m_page.process().webFrame(frameID), userMediaOrigin.get(), topLevelOrigin.get(), request.get()))
+        failureHandler(userMediaID, String(), false);
+}
 #endif
-}
 
-void UserMediaPermissionRequestManagerProxy::didCompleteUserMediaPermissionCheck(uint64_t userMediaID, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess)
+void UserMediaPermissionRequestManagerProxy::enumerateMediaDevicesForFrame(uint64_t userMediaID, uint64_t frameID, String&& userMediaDocumentOriginIdentifier, String&& topLevelDocumentOriginIdentifier)
 {
-    if (!m_page.isValid())
-        return;
+#if ENABLE(MEDIA_STREAM)
+    auto completionHandler = [this, userMediaID](uint64_t userMediaID, String&& deviceIdentifierHashSalt, bool originHasPersistentAccess) {
+        auto request = m_pendingDeviceRequests.take(userMediaID);
+        if (!request)
+            return;
 
-    if (!m_pendingDeviceRequests.take(userMediaID))
-        return;
+        if (!m_page.isValid())
+            return;
 
-#if ENABLE(MEDIA_STREAM)
-    syncWithWebCorePrefs();
-    auto deviceInfo = RealtimeMediaSourceCenter::singleton().getMediaStreamDevices();
-    m_page.process().send(Messages::WebPage::DidCompleteMediaDeviceEnumeration(userMediaID, deviceInfo, deviceIdentifierHashSalt, originHasPersistentAccess), m_page.pageID());
+        auto& frameState = stateForRequest(*request);
+        frameState.setDeviceIdentifierHashSalt(String(deviceIdentifierHashSalt));
+
+        syncWithWebCorePrefs();
+        auto deviceInfo = RealtimeMediaSourceCenter::singleton().getMediaStreamDevices();
+        m_page.process().send(Messages::WebPage::DidCompleteMediaDeviceEnumeration(userMediaID, deviceInfo, deviceIdentifierHashSalt, originHasPersistentAccess), m_page.pageID());
+    };
+
+    getUserMediaPermissionInfo(userMediaID, frameID, WTFMove(completionHandler), WTFMove(userMediaDocumentOriginIdentifier), WTFMove(topLevelDocumentOriginIdentifier));
 #else
-    UNUSED_PARAM(deviceIdentifierHashSalt);
-    UNUSED_PARAM(originHasPersistentAccess);
+    UNUSED_PARAM(userMediaID);
+    UNUSED_PARAM(frameID);
+    UNUSED_PARAM(userMediaDocumentOriginIdentifier);
+    UNUSED_PARAM(topLevelDocumentOriginIdentifier);
 #endif
 }
 

Modified: trunk/Source/WebKit2/UIProcess/UserMediaPermissionRequestManagerProxy.h (216835 => 216836)


--- trunk/Source/WebKit2/UIProcess/UserMediaPermissionRequestManagerProxy.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/UIProcess/UserMediaPermissionRequestManagerProxy.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -30,6 +30,7 @@
 namespace WebCore {
 class CaptureDevice;
 struct MediaConstraintsData;
+class SecurityOrigin;
 };
 
 namespace WebKit {
@@ -38,17 +39,21 @@
 
 class FrameAuthorizationState {
 public:
-    explicit FrameAuthorizationState(UserMediaPermissionRequestProxy&);
+    explicit FrameAuthorizationState(WebCore::SecurityOrigin* userMediaDocumentSecurityOrigin, WebCore::SecurityOrigin* topLevelDocumentSecurityOrigin);
 
     bool hasPermissionToUseCaptureDevice(const String& deviceUID);
     void setHasPermissionToUseCaptureDevice(const String&, bool);
 
-    void ensureSecurityOriginsAreEqual(UserMediaPermissionRequestProxy&);
+    void ensureSecurityOriginsAreEqual(WebCore::SecurityOrigin* userMediaDocumentSecurityOrigin, WebCore::SecurityOrigin* topLevelDocumentSecurityOrigin);
 
+    void setDeviceIdentifierHashSalt(String&& hashSalt) { m_deviceIdentifierHashSalt = hashSalt; }
+    const String& deviceIdentifierHashSalt() const { return m_deviceIdentifierHashSalt; }
+
 private:
     RefPtr<WebCore::SecurityOrigin> m_userMediaDocumentSecurityOrigin;
     RefPtr<WebCore::SecurityOrigin> m_topLevelDocumentSecurityOrigin;
     Vector<String> m_authorizedDeviceUIDs;
+    String m_deviceIdentifierHashSalt;
 };
 
 class UserMediaPermissionRequestManagerProxy {
@@ -64,11 +69,11 @@
 
     void userMediaAccessWasGranted(uint64_t, const String& audioDeviceUID, const String& videoDeviceUID);
     void userMediaAccessWasDenied(uint64_t, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason);
-    FrameAuthorizationState& stateForRequest(UserMediaPermissionRequestProxy&);
 
-    void enumerateMediaDevicesForFrame(uint64_t userMediaID, uint64_t frameID, String userMediaDocumentOriginIdentifier, String topLevelDocumentOriginIdentifier);
+    template <typename RequestType>
+    FrameAuthorizationState& stateForRequest(RequestType&);
 
-    void didCompleteUserMediaPermissionCheck(uint64_t, const String&, bool allow);
+    void enumerateMediaDevicesForFrame(uint64_t userMediaID, uint64_t frameID, String&& userMediaDocumentOriginIdentifier, String&& topLevelDocumentOriginIdentifier);
 
     void stopCapture();
     void scheduleNextRejection();
@@ -81,7 +86,9 @@
 private:
     Ref<UserMediaPermissionRequestProxy> createRequest(uint64_t userMediaID, uint64_t frameID, const String&userMediaDocumentOriginIdentifier, const String& topLevelDocumentOriginIdentifier, const Vector<String>& audioDeviceUIDs, const Vector<String>& videoDeviceUIDs);
     void denyRequest(uint64_t userMediaID, UserMediaPermissionRequestProxy::UserMediaAccessDenialReason, const String& invalidConstraint);
-    Ref<UserMediaPermissionCheckProxy> createUserMediaPermissionCheck(uint64_t userMediaID);
+
+    void getUserMediaPermissionInfo(uint64_t userMediaID, uint64_t frameID, UserMediaPermissionCheckProxy::CompletionHandler&&, String&& userMediaDocumentOriginIdentifier, String&& topLevelDocumentOriginIdentifier);
+
     void syncWithWebCorePrefs() const;
 
     HashMap<uint64_t, RefPtr<UserMediaPermissionRequestProxy>> m_pendingUserMediaRequests;

Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp (216835 => 216836)


--- trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -5766,7 +5766,7 @@
     WebFrameProxy* frame = m_process->webFrame(frameID);
     MESSAGE_CHECK(frame);
 
-    userMediaPermissionRequestManager().enumerateMediaDevicesForFrame(userMediaID, frameID, userMediaDocumentOriginIdentifier, topLevelDocumentOriginIdentifier);
+    userMediaPermissionRequestManager().enumerateMediaDevicesForFrame(userMediaID, frameID, WTFMove(userMediaDocumentOriginIdentifier), WTFMove(topLevelDocumentOriginIdentifier));
 #else
     UNUSED_PARAM(userMediaID);
     UNUSED_PARAM(frameID);

Modified: trunk/Source/WebKit2/WebProcess/MediaStream/UserMediaPermissionRequestManager.cpp (216835 => 216836)


--- trunk/Source/WebKit2/WebProcess/MediaStream/UserMediaPermissionRequestManager.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/WebProcess/MediaStream/UserMediaPermissionRequestManager.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -154,7 +154,7 @@
     m_userMediaRequestToIDMap.remove(&request);
 }
 
-void UserMediaPermissionRequestManager::userMediaAccessWasGranted(uint64_t requestID, const String& audioDeviceUID, const String& videoDeviceUID)
+void UserMediaPermissionRequestManager::userMediaAccessWasGranted(uint64_t requestID, String&& audioDeviceUID, String&& videoDeviceUID, String&& deviceIdentifierHashSalt)
 {
     auto request = m_idToUserMediaRequestMap.take(requestID);
     if (!request)
@@ -161,10 +161,10 @@
         return;
     removeMediaRequestFromMaps(*request);
 
-    request->allow(audioDeviceUID, videoDeviceUID);
+    request->allow(WTFMove(audioDeviceUID), WTFMove(videoDeviceUID), WTFMove(deviceIdentifierHashSalt));
 }
 
-void UserMediaPermissionRequestManager::userMediaAccessWasDenied(uint64_t requestID, WebCore::UserMediaRequest::MediaAccessDenialReason reason, const String& invalidConstraint)
+void UserMediaPermissionRequestManager::userMediaAccessWasDenied(uint64_t requestID, WebCore::UserMediaRequest::MediaAccessDenialReason reason, String&& invalidConstraint)
 {
     auto request = m_idToUserMediaRequestMap.take(requestID);
     if (!request)
@@ -171,7 +171,7 @@
         return;
     removeMediaRequestFromMaps(*request);
 
-    request->deny(reason, invalidConstraint);
+    request->deny(reason, WTFMove(invalidConstraint));
 }
 
 void UserMediaPermissionRequestManager::enumerateMediaDevices(MediaDevicesEnumerationRequest& request)
@@ -205,7 +205,7 @@
     m_idToMediaDevicesEnumerationRequestMap.remove(requestID);
 }
 
-void UserMediaPermissionRequestManager::didCompleteMediaDeviceEnumeration(uint64_t requestID, const Vector<CaptureDevice>& deviceList, const String& mediaDeviceIdentifierHashSalt, bool hasPersistentAccess)
+void UserMediaPermissionRequestManager::didCompleteMediaDeviceEnumeration(uint64_t requestID, const Vector<CaptureDevice>& deviceList, String&& mediaDeviceIdentifierHashSalt, bool hasPersistentAccess)
 {
     RefPtr<MediaDevicesEnumerationRequest> request = m_idToMediaDevicesEnumerationRequestMap.take(requestID);
     if (!request)
@@ -212,7 +212,7 @@
         return;
     m_mediaDevicesEnumerationRequestToIDMap.remove(request);
     
-    request->setDeviceInfo(deviceList, mediaDeviceIdentifierHashSalt, hasPersistentAccess);
+    request->setDeviceInfo(deviceList, WTFMove(mediaDeviceIdentifierHashSalt), hasPersistentAccess);
 }
 
 void UserMediaPermissionRequestManager::grantUserMediaDeviceSandboxExtensions(const MediaDeviceSandboxExtensions& extensions)

Modified: trunk/Source/WebKit2/WebProcess/MediaStream/UserMediaPermissionRequestManager.h (216835 => 216836)


--- trunk/Source/WebKit2/WebProcess/MediaStream/UserMediaPermissionRequestManager.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/WebProcess/MediaStream/UserMediaPermissionRequestManager.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -45,12 +45,12 @@
 
     void startUserMediaRequest(WebCore::UserMediaRequest&);
     void cancelUserMediaRequest(WebCore::UserMediaRequest&);
-    void userMediaAccessWasGranted(uint64_t, const String& audioDeviceUID, const String& videoDeviceUID);
-    void userMediaAccessWasDenied(uint64_t, WebCore::UserMediaRequest::MediaAccessDenialReason, const String&);
+    void userMediaAccessWasGranted(uint64_t, String&& audioDeviceUID, String&& videoDeviceUID, String&& deviceIdentifierHashSalt);
+    void userMediaAccessWasDenied(uint64_t, WebCore::UserMediaRequest::MediaAccessDenialReason, String&&);
 
     void enumerateMediaDevices(WebCore::MediaDevicesEnumerationRequest&);
     void cancelMediaDevicesEnumeration(WebCore::MediaDevicesEnumerationRequest&);
-    void didCompleteMediaDeviceEnumeration(uint64_t, const Vector<WebCore::CaptureDevice>& deviceList, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess);
+    void didCompleteMediaDeviceEnumeration(uint64_t, const Vector<WebCore::CaptureDevice>& deviceList, String&& deviceIdentifierHashSalt, bool originHasPersistentAccess);
 
     void grantUserMediaDeviceSandboxExtensions(const MediaDeviceSandboxExtensions&);
     void revokeUserMediaDeviceSandboxExtensions(const Vector<String>&);

Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp (216835 => 216836)


--- trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/WebProcess/WebPage/WebPage.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -3861,19 +3861,19 @@
 }
 
 #if ENABLE(MEDIA_STREAM)
-void WebPage::userMediaAccessWasGranted(uint64_t userMediaID, const String& audioDeviceUID, const String& videoDeviceUID)
+void WebPage::userMediaAccessWasGranted(uint64_t userMediaID, String&& audioDeviceUID, String&& videoDeviceUID, String&& mediaDeviceIdentifierHashSalt)
 {
-    m_userMediaPermissionRequestManager->userMediaAccessWasGranted(userMediaID, audioDeviceUID, videoDeviceUID);
+    m_userMediaPermissionRequestManager->userMediaAccessWasGranted(userMediaID, WTFMove(audioDeviceUID), WTFMove(videoDeviceUID), WTFMove(mediaDeviceIdentifierHashSalt));
 }
 
-void WebPage::userMediaAccessWasDenied(uint64_t userMediaID, uint64_t reason, String invalidConstraint)
+void WebPage::userMediaAccessWasDenied(uint64_t userMediaID, uint64_t reason, String&& invalidConstraint)
 {
-    m_userMediaPermissionRequestManager->userMediaAccessWasDenied(userMediaID, static_cast<UserMediaRequest::MediaAccessDenialReason>(reason), invalidConstraint);
+    m_userMediaPermissionRequestManager->userMediaAccessWasDenied(userMediaID, static_cast<UserMediaRequest::MediaAccessDenialReason>(reason), WTFMove(invalidConstraint));
 }
 
-void WebPage::didCompleteMediaDeviceEnumeration(uint64_t userMediaID, const Vector<CaptureDevice>& devices, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess)
+void WebPage::didCompleteMediaDeviceEnumeration(uint64_t userMediaID, const Vector<CaptureDevice>& devices, String&& deviceIdentifierHashSalt, bool originHasPersistentAccess)
 {
-    m_userMediaPermissionRequestManager->didCompleteMediaDeviceEnumeration(userMediaID, devices, deviceIdentifierHashSalt, originHasPersistentAccess);
+    m_userMediaPermissionRequestManager->didCompleteMediaDeviceEnumeration(userMediaID, devices, WTFMove(deviceIdentifierHashSalt), originHasPersistentAccess);
 }
 #if ENABLE(SANDBOX_EXTENSIONS)
 void WebPage::grantUserMediaDeviceSandboxExtensions(const MediaDeviceSandboxExtensions& extensions)

Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h (216835 => 216836)


--- trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/WebProcess/WebPage/WebPage.h	2017-05-14 05:27:18 UTC (rev 216836)
@@ -1174,10 +1174,10 @@
     void didReceiveNotificationPermissionDecision(uint64_t notificationID, bool allowed);
 
 #if ENABLE(MEDIA_STREAM)
-    void userMediaAccessWasGranted(uint64_t userMediaID, const String& audioDeviceUID, const String& videoDeviceUID);
-    void userMediaAccessWasDenied(uint64_t userMediaID, uint64_t reason, String invalidConstraint);
+    void userMediaAccessWasGranted(uint64_t userMediaID, String&& audioDeviceUID, String&& videoDeviceUID, String&& mediaDeviceIdentifierHashSalt);
+    void userMediaAccessWasDenied(uint64_t userMediaID, uint64_t reason, String&& invalidConstraint);
 
-    void didCompleteMediaDeviceEnumeration(uint64_t userMediaID, const Vector<WebCore::CaptureDevice>& devices, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess);
+    void didCompleteMediaDeviceEnumeration(uint64_t userMediaID, const Vector<WebCore::CaptureDevice>& devices, String&& deviceIdentifierHashSalt, bool originHasPersistentAccess);
 #if ENABLE(SANDBOX_EXTENSIONS)
     void grantUserMediaDeviceSandboxExtensions(const MediaDeviceSandboxExtensions&);
     void revokeUserMediaDeviceSandboxExtensions(const Vector<String>&);

Modified: trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in (216835 => 216836)


--- trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/WebProcess/WebPage/WebPage.messages.in	2017-05-14 05:27:18 UTC (rev 216836)
@@ -298,7 +298,7 @@
 
 #if ENABLE(MEDIA_STREAM)
     # MediaSteam
-    UserMediaAccessWasGranted(uint64_t userMediaID, String audioDeviceUID, String videoDeviceUID)
+    UserMediaAccessWasGranted(uint64_t userMediaID, String audioDeviceUID, String videoDeviceUID, String mediaDeviceIdentifierHashSalt)
     UserMediaAccessWasDenied(uint64_t userMediaID, uint64_t reason, String invalidConstraint)
     DidCompleteMediaDeviceEnumeration(uint64_t userMediaID, Vector<WebCore::CaptureDevice> devices, String mediaDeviceIdentifierHashSalt, bool hasPersistentAccess)
 #if ENABLE(SANDBOX_EXTENSIONS)

Modified: trunk/Source/WebKit2/WebProcess/cocoa/UserMediaCaptureManager.cpp (216835 => 216836)


--- trunk/Source/WebKit2/WebProcess/cocoa/UserMediaCaptureManager.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Source/WebKit2/WebProcess/cocoa/UserMediaCaptureManager.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -212,15 +212,11 @@
         return { };
 
     uint64_t id = nextSessionID();
-    MediaConstraintsData constraintsData;
-    constraintsData.mandatoryConstraints = constraints->mandatoryConstraints();
-    constraintsData.advancedConstraints = constraints->advancedConstraints();
-    constraintsData.isValid = constraints->isValid();
     bool succeeded;
 
     RealtimeMediaSourceSettings settings;
     String errorMessage;
-    if (!m_process.sendSync(Messages::UserMediaCaptureManagerProxy::CreateMediaSourceForCaptureDeviceWithConstraints(id, deviceID, sourceType, constraintsData), Messages::UserMediaCaptureManagerProxy::CreateMediaSourceForCaptureDeviceWithConstraints::Reply(succeeded, errorMessage, settings), 0))
+    if (!m_process.sendSync(Messages::UserMediaCaptureManagerProxy::CreateMediaSourceForCaptureDeviceWithConstraints(id, deviceID, sourceType, *constraints), Messages::UserMediaCaptureManagerProxy::CreateMediaSourceForCaptureDeviceWithConstraints::Reply(succeeded, errorMessage, settings), 0))
         return WTFMove(errorMessage);
 
     auto source = adoptRef(*new Source(String::number(id), sourceType, emptyString(), id, *this));

Modified: trunk/Tools/ChangeLog (216835 => 216836)


--- trunk/Tools/ChangeLog	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Tools/ChangeLog	2017-05-14 05:27:18 UTC (rev 216836)
@@ -1,3 +1,21 @@
+2017-05-13  Eric Carlson  <[email protected]>
+
+        [MediaStream] deviceId constraint doesn't work with getUserMedia
+        https://bugs.webkit.org/show_bug.cgi?id=171877
+        <rdar://problem/31899730>
+
+        Reviewed by Jer Noble.
+
+        The device ID hash salt is now required for getUserMedia to check deviceId constraint, so
+        implement the "checkUserMediaPermission" callback.
+        * TestWebKitAPI/Tests/WebKit2/UserMedia.cpp:
+        (TestWebKitAPI::decidePolicyForUserMediaPermissionRequestCallBack):
+        (TestWebKitAPI::checkUserMediaPermissionCallback):
+        (TestWebKitAPI::TEST):
+
+        * TestWebKitAPI/Tests/WebKit2Cocoa/UserMediaDisabled.mm:
+        (-[UserMediaUIDelegate _webView:checkUserMediaPermissionForURL:mainFrameURL:frameIdentifier:decisionHandler:]):
+
 2017-05-13  Chris Dumez  <[email protected]>
 
         Stop using RefPtr::release()

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2/UserMedia.cpp (216835 => 216836)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit2/UserMedia.cpp	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2/UserMedia.cpp	2017-05-14 05:27:18 UTC (rev 216836)
@@ -29,6 +29,7 @@
 #include <WebKit/WKPreferencesRef.h>
 #include <WebKit/WKPreferencesRefPrivate.h>
 #include <WebKit/WKRetainPtr.h>
+#include <WebKit/WKUserMediaPermissionCheck.h>
 #include <string.h>
 #include <vector>
 
@@ -36,7 +37,7 @@
 
 static bool wasPrompted;
 
-void decidePolicyForUserMediaPermissionRequestCallBack(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKSecurityOriginRef, WKUserMediaPermissionRequestRef permissionRequest, const void* /* clientInfo */)
+static void decidePolicyForUserMediaPermissionRequestCallBack(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKSecurityOriginRef, WKUserMediaPermissionRequestRef permissionRequest, const void* /* clientInfo */)
 {
     WKRetainPtr<WKArrayRef> audioDeviceUIDs = WKUserMediaPermissionRequestAudioDeviceUIDs(permissionRequest);
     WKRetainPtr<WKArrayRef> videoDeviceUIDs = WKUserMediaPermissionRequestVideoDeviceUIDs(permissionRequest);
@@ -60,6 +61,11 @@
     wasPrompted = true;
 }
 
+static void checkUserMediaPermissionCallback(WKPageRef, WKFrameRef, WKSecurityOriginRef, WKSecurityOriginRef, WKUserMediaPermissionCheckRef permissionRequest, const void*)
+{
+    WKUserMediaPermissionCheckSetUserMediaAccessInfo(permissionRequest, WKStringCreateWithUTF8CString("0x123456789"), false);
+}
+
 TEST(WebKit2, UserMediaBasic)
 {
     auto context = adoptWK(WKContextCreate());
@@ -75,7 +81,8 @@
     memset(&uiClient, 0, sizeof(uiClient));
     uiClient.base.version = 6;
     uiClient.decidePolicyForUserMediaPermissionRequest = decidePolicyForUserMediaPermissionRequestCallBack;
-
+    uiClient.checkUserMediaPermissionForOrigin = checkUserMediaPermissionCallback;
+    
     PlatformWebView webView(context.get(), pageGroup.get());
     WKPageSetPageUIClient(webView.page(), &uiClient.base);
 

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/UserMediaDisabled.mm (216835 => 216836)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/UserMediaDisabled.mm	2017-05-14 05:21:12 UTC (rev 216835)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/UserMediaDisabled.mm	2017-05-14 05:27:18 UTC (rev 216836)
@@ -87,6 +87,8 @@
         decisionHandler(nil, NO);
         return;
     }
+
+    decisionHandler(@"0x987654321", NO);
 }
 @end
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to