[MediaStream] 'devicechange' event when more capture device information are revealed.
authoreric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Dec 2018 16:28:31 +0000 (16:28 +0000)
committereric.carlson@apple.com <eric.carlson@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Dec 2018 16:28:31 +0000 (16:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=192268

Reviewed by Youenn Fablet.

Source/WebCore:

Test: fast/mediastream/enumerate-devices-change-event.html

* Modules/mediastream/MediaDevicesRequest.cpp:
(WebCore::MediaDevicesRequest::start): Remove code to modify device based on access, that is
now done in the UI process.
(WebCore::MediaDevicesRequest::filterDeviceList): Deleted.
* Modules/mediastream/MediaDevicesRequest.h:
* platform/mediastream/RealtimeMediaSourceCenter.h:

Source/WebKit:

* UIProcess/UserMediaPermissionRequestManagerProxy.cpp:
(WebKit::UserMediaPermissionRequestManagerProxy::userMediaAccessWasGranted): Call captureDevicesChanged
if a filtered device list was returned previously.
(WebKit::UserMediaPermissionRequestManagerProxy::resetAccess): Clear m_hasFilteredDeviceList.
(WebKit::UserMediaPermissionRequestManagerProxy::wasGrantedVideoOrAudioAccess): New.
(WebKit::UserMediaPermissionRequestManagerProxy::enumerateMediaDevicesForFrame): Filter device
list and strip device IDs if gUM permission hasn't been given.
(WebKit::UserMediaPermissionRequestManagerProxy::watchdogTimerFired): Clear m_hasFilteredDeviceList.
* UIProcess/UserMediaPermissionRequestManagerProxy.h:

LayoutTests:

* fast/mediastream/device-change-event-2.html: Fix bogus title.
* fast/mediastream/enumerate-devices-change-event-expected.txt: Added.
* fast/mediastream/enumerate-devices-change-event.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@238796 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/mediastream/device-change-event-2.html
LayoutTests/fast/mediastream/enumerate-devices-change-event-expected.txt [new file with mode: 0644]
LayoutTests/fast/mediastream/enumerate-devices-change-event.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediastream/MediaDevicesRequest.cpp
Source/WebCore/Modules/mediastream/MediaDevicesRequest.h
Source/WebCore/platform/mediastream/RealtimeMediaSourceCenter.h
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.cpp
Source/WebKit/UIProcess/UserMediaPermissionRequestManagerProxy.h

index 86c8bb1..36eb9af 100644 (file)
@@ -1,3 +1,14 @@
+2018-12-03  Eric Carlson  <eric.carlson@apple.com>
+
+        [MediaStream] 'devicechange' event when more capture device information are revealed.
+        https://bugs.webkit.org/show_bug.cgi?id=192268
+
+        Reviewed by Youenn Fablet.
+
+        * fast/mediastream/device-change-event-2.html: Fix bogus title.
+        * fast/mediastream/enumerate-devices-change-event-expected.txt: Added.
+        * fast/mediastream/enumerate-devices-change-event.html: Added.
+
 2018-12-03  Guillaume Emont  <guijemont@igalia.com>
 
         Gardening: unskip marsaglia.js on arm
index 614b8d1..e780f11 100644 (file)
@@ -2,7 +2,7 @@
 <html>
 <head>
     <meta charset="utf-8">
-    <title>Testing local audio capture playback causes "playing" event to fire</title>
+    <title>Testing 'devicechange' event is fired correctly.</title>
     <script src="../../resources/testharness.js"></script>
     <script src="../../resources/testharnessreport.js"></script>
     <script>
diff --git a/LayoutTests/fast/mediastream/enumerate-devices-change-event-expected.txt b/LayoutTests/fast/mediastream/enumerate-devices-change-event-expected.txt
new file mode 100644 (file)
index 0000000..7997bd8
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS 'devicechange' event fired after getUserMedia() if enumerateDevices() previously returned filtered list 
+
diff --git a/LayoutTests/fast/mediastream/enumerate-devices-change-event.html b/LayoutTests/fast/mediastream/enumerate-devices-change-event.html
new file mode 100644 (file)
index 0000000..ede0dfa
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <script src="../../resources/testharness.js"></script>
+    <script src="../../resources/testharnessreport.js"></script>
+    <script>
+    
+    promise_test(async (test) => {
+    
+        if (window.testRunner)
+            testRunner.setUserMediaPermission(true);
+
+        let devices1 = await navigator.mediaDevices.enumerateDevices();
+
+        let eventCount = 0;
+        await new Promise((resolve, reject) => {
+            navigator.mediaDevices.ondevicechange = (evt) => {
+                ++eventCount;
+                resolve();
+            }
+
+            setTimeout(() => {
+                reject("navigator.mediaDevices.ondevicechange took too long")
+            }, 4000);
+
+            navigator.mediaDevices.getUserMedia({ audio:true, video:true });
+        });
+
+        let devices2 = await navigator.mediaDevices.enumerateDevices();
+        assert_true(devices1.length < devices2.length, "more devices revealed after gUM");
+        assert_equals(eventCount, 1, "one event fired");
+
+    }, "'devicechange' event fired after getUserMedia() if enumerateDevices() previously returned filtered list");
+
+    </script>
+</head>
+<body>
+</body>
+</html>
index 6cbbea1..953a769 100644 (file)
@@ -1,3 +1,19 @@
+2018-12-03  Eric Carlson  <eric.carlson@apple.com>
+
+        [MediaStream] 'devicechange' event when more capture device information are revealed.
+        https://bugs.webkit.org/show_bug.cgi?id=192268
+
+        Reviewed by Youenn Fablet.
+
+        Test: fast/mediastream/enumerate-devices-change-event.html
+
+        * Modules/mediastream/MediaDevicesRequest.cpp:
+        (WebCore::MediaDevicesRequest::start): Remove code to modify device based on access, that is
+        now done in the UI process.
+        (WebCore::MediaDevicesRequest::filterDeviceList): Deleted.
+        * Modules/mediastream/MediaDevicesRequest.h:
+        * platform/mediastream/RealtimeMediaSourceCenter.h:
+
 2018-12-03  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         [iOSMac] Unable to upload non-image files using drag and drop in WKWebView
index dba7a61..10fdd02 100644 (file)
@@ -79,28 +79,6 @@ void MediaDevicesRequest::contextDestroyed()
     ContextDestructionObserver::contextDestroyed();
 }
 
-void MediaDevicesRequest::filterDeviceList(Vector<Ref<MediaDeviceInfo>>& devices)
-{
-#if PLATFORM(IOS_FAMILY)
-    static const int defaultCameraCount = 2;
-#else
-    static const int defaultCameraCount = 1;
-#endif
-
-    static const int defaultMicrophoneCount = 1;
-
-    int cameraCount = 0;
-    int microphoneCount = 0;
-    devices.removeAllMatching([&](const Ref<MediaDeviceInfo>& device) -> bool {
-        if (device->kind() == MediaDeviceInfo::Kind::Videoinput && ++cameraCount > defaultCameraCount)
-            return true;
-        if (device->kind() == MediaDeviceInfo::Kind::Audioinput && ++microphoneCount > defaultMicrophoneCount)
-            return true;
-
-        return false;
-    });
-}
-
 void MediaDevicesRequest::start()
 {
     auto& document = downcast<Document>(*scriptExecutionContext());
@@ -127,7 +105,7 @@ void MediaDevicesRequest::start()
     }
 
     // This lambda keeps |this| alive until the request completes or is canceled.
-    auto completion = [this, protectedThis = makeRef(*this), canAccessMicrophone, canAccessCamera] (const Vector<CaptureDevice>& captureDevices, const String& deviceIdentifierHashSalt, bool originHasPersistentAccess) mutable {
+    auto completion = [this, protectedThis = makeRef(*this), canAccessMicrophone, canAccessCamera] (const Vector<CaptureDevice>& captureDevices, const String& deviceIdentifierHashSalt, bool) mutable {
 
         m_enumerationRequest = nullptr;
 
@@ -138,31 +116,16 @@ void MediaDevicesRequest::start()
         document.setDeviceIDHashSalt(deviceIdentifierHashSalt);
 
         Vector<Ref<MediaDeviceInfo>> devices;
-        bool revealIdsAndLabels = originHasPersistentAccess || document.hasHadCaptureMediaStreamTrack();
         for (auto& deviceInfo : captureDevices) {
             if (!canAccessMicrophone && deviceInfo.type() == CaptureDevice::DeviceType::Microphone)
                 continue;
             if (!canAccessCamera && deviceInfo.type() == CaptureDevice::DeviceType::Camera)
                 continue;
 
-            auto label = emptyString();
-            auto id = emptyString();
-            auto groupId = emptyString();
-            if (revealIdsAndLabels) {
-                label = deviceInfo.label();
-                id = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(deviceInfo.persistentId(), deviceIdentifierHashSalt);
-                if (id.isEmpty())
-                    continue;
-                groupId = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(deviceInfo.groupId(), deviceIdentifierHashSalt);
-            }
-
             auto deviceType = deviceInfo.type() == CaptureDevice::DeviceType::Microphone ? MediaDeviceInfo::Kind::Audioinput : MediaDeviceInfo::Kind::Videoinput;
-            devices.append(MediaDeviceInfo::create(scriptExecutionContext(), label, id, groupId, deviceType));
+            devices.append(MediaDeviceInfo::create(scriptExecutionContext(), deviceInfo.label(), deviceInfo.persistentId(), deviceInfo.groupId(), deviceType));
         }
 
-        if (!revealIdsAndLabels)
-            filterDeviceList(devices);
-
         callOnMainThread([protectedThis = makeRef(*this), devices = WTFMove(devices)]() mutable {
             protectedThis->m_promise.resolve(devices);
         });
index 33abbd6..753e64c 100644 (file)
@@ -51,8 +51,6 @@ private:
 
     void contextDestroyed() final;
 
-    void filterDeviceList(Vector<Ref<MediaDeviceInfo>>&);
-
     MediaDevices::EnumerateDevicesPromise m_promise;
     RefPtr<MediaDevicesRequest> m_protector;
     RefPtr<MediaDevicesEnumerationRequest> m_enumerationRequest;
index 1efffdb..9348124 100644 (file)
@@ -88,7 +88,7 @@ public:
     virtual CaptureDeviceManager& videoCaptureDeviceManager() = 0;
     virtual CaptureDeviceManager& displayCaptureDeviceManager() = 0;
 
-    String hashStringWithSalt(const String& id, const String& hashSalt);
+    WEBCORE_EXPORT String hashStringWithSalt(const String& id, const String& hashSalt);
     WEBCORE_EXPORT CaptureDevice captureDeviceWithUniqueID(const String& id, const String& hashSalt);
     WEBCORE_EXPORT ExceptionOr<void> setDeviceEnabled(const String&, bool);
 
index 1bcddcb..cd5d405 100644 (file)
@@ -1,3 +1,20 @@
+2018-12-03  Eric Carlson  <eric.carlson@apple.com>
+
+        [MediaStream] 'devicechange' event when more capture device information are revealed.
+        https://bugs.webkit.org/show_bug.cgi?id=192268
+
+        Reviewed by Youenn Fablet.
+
+        * UIProcess/UserMediaPermissionRequestManagerProxy.cpp:
+        (WebKit::UserMediaPermissionRequestManagerProxy::userMediaAccessWasGranted): Call captureDevicesChanged
+        if a filtered device list was returned previously.
+        (WebKit::UserMediaPermissionRequestManagerProxy::resetAccess): Clear m_hasFilteredDeviceList.
+        (WebKit::UserMediaPermissionRequestManagerProxy::wasGrantedVideoOrAudioAccess): New.
+        (WebKit::UserMediaPermissionRequestManagerProxy::enumerateMediaDevicesForFrame): Filter device
+        list and strip device IDs if gUM permission hasn't been given.
+        (WebKit::UserMediaPermissionRequestManagerProxy::watchdogTimerFired): Clear m_hasFilteredDeviceList.
+        * UIProcess/UserMediaPermissionRequestManagerProxy.h:
+
 2018-12-03  Keith Rollin  <krollin@apple.com>
 
         Remove Network Capture
index 56d3588..1a06620 100644 (file)
@@ -164,8 +164,13 @@ void UserMediaPermissionRequestManagerProxy::userMediaAccessWasGranted(uint64_t
         return;
 
     auto deviceIDHashSalt = m_page.websiteDataStore().deviceIdHashSaltStorage()->deviceIdHashSaltForOrigin(request->userMediaDocumentSecurityOrigin(), request->topLevelDocumentSecurityOrigin());
-    if (grantAccess(userMediaID, WTFMove(audioDevice), WTFMove(videoDevice), WTFMove(deviceIDHashSalt)))
+
+    if (grantAccess(userMediaID, WTFMove(audioDevice), WTFMove(videoDevice), WTFMove(deviceIDHashSalt))) {
         m_grantedRequests.append(request.releaseNonNull());
+        if (m_hasFilteredDeviceList)
+            captureDevicesChanged();
+        m_hasFilteredDeviceList = false;
+    }
 #else
     UNUSED_PARAM(userMediaID);
     UNUSED_PARAM(audioDevice);
@@ -181,6 +186,7 @@ void UserMediaPermissionRequestManagerProxy::resetAccess(uint64_t frameID)
     });
     m_pregrantedRequests.clear();
     m_deniedRequests.clear();
+    m_hasFilteredDeviceList = false;
 }
 
 const UserMediaPermissionRequestProxy* UserMediaPermissionRequestManagerProxy::searchForGrantedRequest(uint64_t frameID, const SecurityOrigin& userMediaDocumentOrigin, const SecurityOrigin& topLevelDocumentOrigin, bool needsAudio, bool needsVideo) const
@@ -393,12 +399,39 @@ void UserMediaPermissionRequestManagerProxy::getUserMediaPermissionInfo(uint64_t
         m_pendingDeviceRequests.take(userMediaID);
         request->completionHandler()(userMediaID, false);
     }
-} 
+}
+
+bool UserMediaPermissionRequestManagerProxy::wasGrantedVideoOrAudioAccess(uint64_t frameID, const SecurityOrigin& userMediaDocumentOrigin, const SecurityOrigin& topLevelDocumentOrigin)
+{
+    for (const auto& grantedRequest : m_grantedRequests) {
+        if (grantedRequest->requiresDisplayCapture())
+            continue;
+        if (!grantedRequest->userMediaDocumentSecurityOrigin().isSameSchemeHostPort(userMediaDocumentOrigin))
+            continue;
+        if (!grantedRequest->topLevelDocumentSecurityOrigin().isSameSchemeHostPort(topLevelDocumentOrigin))
+            continue;
+        if (grantedRequest->frameID() != frameID)
+            continue;
+
+        if (grantedRequest->requiresVideoCapture() || grantedRequest->requiresAudioCapture())
+            return true;
+    }
+
+    return false;
+}
 #endif
 
 void UserMediaPermissionRequestManagerProxy::enumerateMediaDevicesForFrame(uint64_t userMediaID, uint64_t frameID, Ref<SecurityOrigin>&& userMediaDocumentOrigin, Ref<SecurityOrigin>&& topLevelDocumentOrigin)
 {
 #if ENABLE(MEDIA_STREAM)
+
+#if PLATFORM(IOS_FAMILY)
+    static const int defaultMaximumCameraCount = 2;
+#else
+    static const int defaultMaximumCameraCount = 1;
+#endif
+    static const int defaultMaximumMicrophoneCount = 1;
+
     auto completionHandler = [this, requestOrigin = userMediaDocumentOrigin.copyRef(), topOrigin = topLevelDocumentOrigin.copyRef()](uint64_t userMediaID, bool originHasPersistentAccess) {
         auto pendingRequest = m_pendingDeviceRequests.take(userMediaID);
         if (!pendingRequest)
@@ -410,12 +443,39 @@ void UserMediaPermissionRequestManagerProxy::enumerateMediaDevicesForFrame(uint6
         syncWithWebCorePrefs();
 
         auto devices = RealtimeMediaSourceCenter::singleton().getMediaStreamDevices();
-        devices.removeAllMatching([&](auto& device) -> bool {
-            return !device.enabled() || (device.type() != WebCore::CaptureDevice::DeviceType::Camera && device.type() != WebCore::CaptureDevice::DeviceType::Microphone);
-        });
-
+        auto& request = *pendingRequest;
+        bool revealIdsAndLabels = originHasPersistentAccess || wasGrantedVideoOrAudioAccess(request->frameID(), request->userMediaDocumentSecurityOrigin(), request->topLevelDocumentSecurityOrigin());
+        int cameraCount = 0;
+        int microphoneCount = 0;
         auto deviceIDHashSalt = m_page.websiteDataStore().deviceIdHashSaltStorage()->deviceIdHashSaltForOrigin(requestOrigin.get(), topOrigin.get());
-        m_page.process().send(Messages::WebPage::DidCompleteMediaDeviceEnumeration(userMediaID, WTFMove(devices), WTFMove(deviceIDHashSalt), WTFMove(originHasPersistentAccess)), m_page.pageID());
+
+        Vector<CaptureDevice> filteredDevices;
+        for (const auto& device : devices) {
+            if (!device.enabled() || (device.type() != WebCore::CaptureDevice::DeviceType::Camera && device.type() != WebCore::CaptureDevice::DeviceType::Microphone))
+                continue;
+
+            if (!revealIdsAndLabels) {
+                if (device.type() == WebCore::CaptureDevice::DeviceType::Camera && ++cameraCount > defaultMaximumCameraCount)
+                    continue;
+                if (device.type() == WebCore::CaptureDevice::DeviceType::Microphone && ++microphoneCount > defaultMaximumMicrophoneCount)
+                    continue;
+            }
+
+            auto label = emptyString();
+            auto id = emptyString();
+            auto groupId = emptyString();
+            if (revealIdsAndLabels) {
+                label = device.label();
+                id = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(device.persistentId(), deviceIDHashSalt);
+                groupId = RealtimeMediaSourceCenter::singleton().hashStringWithSalt(device.groupId(), deviceIDHashSalt);
+            }
+
+            filteredDevices.append(CaptureDevice(id, device.type(), label, groupId));
+        }
+
+        m_hasFilteredDeviceList = !revealIdsAndLabels;
+
+        m_page.process().send(Messages::WebPage::DidCompleteMediaDeviceEnumeration(userMediaID, WTFMove(filteredDevices), WTFMove(deviceIDHashSalt), originHasPersistentAccess), m_page.pageID());
     };
 
     getUserMediaPermissionInfo(userMediaID, frameID, WTFMove(completionHandler), WTFMove(userMediaDocumentOrigin), WTFMove(topLevelDocumentOrigin));
@@ -484,6 +544,7 @@ void UserMediaPermissionRequestManagerProxy::watchdogTimerFired()
     m_grantedRequests.clear();
     m_pregrantedRequests.clear();
     m_currentWatchdogInterval = 0_s;
+    m_hasFilteredDeviceList = false;
 }
 
 } // namespace WebKit
index 847f324..24443db 100644 (file)
@@ -83,6 +83,8 @@ private:
         Prompt
     };
     RequestAction getRequestAction(uint64_t frameID, WebCore::SecurityOrigin& userMediaDocumentOrigin, WebCore::SecurityOrigin& topLevelDocumentOrigin, const WebCore::MediaStreamRequest&, Vector<WebCore::CaptureDevice>& audioDevices, Vector<WebCore::CaptureDevice>& videoDevices);
+
+    bool wasGrantedVideoOrAudioAccess(uint64_t, const WebCore::SecurityOrigin& userMediaDocumentOrigin, const WebCore::SecurityOrigin& topLevelDocumentOrigin);
 #endif
 
     void watchdogTimerFired();
@@ -111,6 +113,7 @@ private:
     WebCore::MediaProducer::MediaStateFlags m_captureState { WebCore::MediaProducer::IsNotPlaying };
     RunLoop::Timer<UserMediaPermissionRequestManagerProxy> m_watchdogTimer;
     Seconds m_currentWatchdogInterval;
+    bool m_hasFilteredDeviceList { false };
 };
 
 } // namespace WebKit